diff options
Diffstat (limited to 'src/twopane.bash')
-rwxr-xr-x | src/twopane.bash | 220 |
1 files changed, 140 insertions, 80 deletions
diff --git a/src/twopane.bash b/src/twopane.bash index 327d246..c266828 100755 --- a/src/twopane.bash +++ b/src/twopane.bash | |||
@@ -6,7 +6,10 @@ set -f | |||
6 | set -o pipefail | 6 | set -o pipefail |
7 | shopt -s lastpipe | 7 | shopt -s lastpipe |
8 | 8 | ||
9 | PS4='+ \t$LINENO\t ' | ||
10 | |||
9 | . read_chars.bash | 11 | . read_chars.bash |
12 | . finally.bash | ||
10 | 13 | ||
11 | kill_tty_forward() | 14 | kill_tty_forward() |
12 | { | 15 | { |
@@ -17,17 +20,39 @@ kill_tty_forward() | |||
17 | BOT_SIZE=8 | 20 | BOT_SIZE=8 |
18 | BOT_TITLE=input | 21 | BOT_TITLE=input |
19 | 22 | ||
23 | DO_RESTART=y | ||
24 | DO_RESTART= | ||
25 | DO_START= | ||
26 | DO_START=y | ||
27 | |||
20 | if [ $# = 0 ] | 28 | if [ $# = 0 ] |
21 | then | 29 | then |
22 | TOP_CMD="$SHELL -i" | 30 | TOP_CMD="$SHELL -i" |
23 | BOT_CMD=start | 31 | if [ "$DO_RESTART" ] |
24 | #TOP_EXIT=quit | 32 | then |
25 | #TOP_EXIT=prompt | 33 | SIG=INT |
26 | TOP_EXIT=restart | 34 | BOT_CMD=PROMPT_COMMAND=prompt_command |
35 | TOP_EXIT=restart | ||
36 | elif [ "$DO_START" ] | ||
37 | then | ||
38 | BOT_CMD=start | ||
39 | TOP_EXIT=quit | ||
40 | else | ||
41 | BOT_CMD=PROMPT_COMMAND=prompt_command | ||
42 | TOP_EXIT=prompt | ||
43 | fi | ||
27 | elif [ "$TWOPANE" -a "$*" = detach ] | 44 | elif [ "$TWOPANE" -a "$*" = detach ] |
28 | then | 45 | then |
29 | kill_tty_forward -TSTP | 46 | kill_tty_forward -TSTP |
30 | exit | 47 | exit |
48 | elif [ "$TWOPANE" -a "$*" = focus ] | ||
49 | then | ||
50 | screen -X focus | ||
51 | exit | ||
52 | elif [ "$TWOPANE" -a "$*" = quit ] | ||
53 | then | ||
54 | screen -X quit | ||
55 | exit | ||
31 | else | 56 | else |
32 | TOP_CMD=$* | 57 | TOP_CMD=$* |
33 | BOT_CMD=start | 58 | BOT_CMD=start |
@@ -156,33 +181,42 @@ restart_top_pane() | |||
156 | with_screen_pane 1 restart_pane "$@" | 181 | with_screen_pane 1 restart_pane "$@" |
157 | } | 182 | } |
158 | 183 | ||
184 | # connect_coproc() -- start a coprocess and connect it to copied file | ||
185 | # descriptors named {NAME}_IN and {NAME}_OUT where {NAME} is the | ||
186 | # supplied first argument or default "COPROC". {NAME} is the name of | ||
187 | # the created bash coprocess. | ||
188 | |||
189 | # The copied file descriptors (unlike the original coprocess | ||
190 | # file descriptors, in ${NAME[0]} and ${NAME[1]}) can be | ||
191 | # passed to external processes (e.g.: socat(1) instances) and | ||
192 | # used in subshells. | ||
193 | |||
194 | # The created coprocess is silently disowned from job control before | ||
195 | # bash can make noise about it. | ||
159 | connect_coproc() | 196 | connect_coproc() |
160 | { | 197 | { |
161 | [ $# -gt 0 ] || set -- COPROC | 198 | [ $# -gt 0 ] || set -- COPROC |
162 | declare -n coproc="$1" | 199 | declare -n coproc="$1" |
163 | shift | 200 | shift |
164 | [ $# -gt 0 ] || set -- "${!coproc}" | 201 | [ $# -gt 0 ] || set -- "${!coproc}" |
165 | declare -n std1="${!coproc}_STDOUT" | 202 | declare -n std0="${!coproc}_IN" |
166 | declare -n std0="${!coproc}_STDIN" | 203 | declare -n std1="${!coproc}_OUT" |
167 | declare -n opid="${!coproc}_PID" | 204 | declare -n std2="${!coproc}_ERR" |
168 | if ! [ "$opid" ] | 205 | declare -n pid="${!coproc}_PID" |
206 | if ! [ "$pid" ] | ||
169 | then | 207 | then |
170 | i "coproc starting: ${!coproc}" | 208 | i "coproc starting: ${!coproc}" |
171 | local STDERR | ||
172 | { | 209 | { |
173 | coproc "${!coproc}" \ | 210 | coproc "${!coproc}" \ |
174 | { | 211 | { |
175 | "$@" | 212 | "$@" |
176 | } 2>&$STDERR {STDERR}>&- | 213 | } 2>&$std2 {std2}>&- |
214 | unset std2 | ||
177 | disown "%coproc ${!coproc} " | 215 | disown "%coproc ${!coproc} " |
178 | } {STDERR}>&2 2>/dev/null | 216 | } {std2}>&2 2>/dev/null |
179 | else | 217 | else |
180 | i "coproc already running: ${!coproc}" | 218 | i "coproc already running: ${!coproc}" |
181 | fi | 219 | fi |
182 | # The copied file descriptors (unlike the original coprocess | ||
183 | # file descriptors, in ${coproc[0]} and ${coproc[1]}) can be | ||
184 | # passed to external processes (e.g.: socat(1) instances) and | ||
185 | # used in subshells. | ||
186 | { | 220 | { |
187 | exec {std0}<&0 {std1}>&1 | 221 | exec {std0}<&0 {std1}>&1 |
188 | } <&${coproc[0]} >&${coproc[1]} | 222 | } <&${coproc[0]} >&${coproc[1]} |
@@ -192,8 +226,8 @@ disconnect_coproc() | |||
192 | { | 226 | { |
193 | [ $# -gt 0 ] || set -- COPROC | 227 | [ $# -gt 0 ] || set -- COPROC |
194 | declare -n coproc="$1" | 228 | declare -n coproc="$1" |
195 | declare -n std0="${!coproc}_STDIN" | 229 | declare -n std0="${!coproc}_IN" |
196 | declare -n std1="${!coproc}_STDOUT" | 230 | declare -n std1="${!coproc}_OUT" |
197 | declare -n pid="${!coproc}_PID" | 231 | declare -n pid="${!coproc}_PID" |
198 | if [ "$std0" -o "$std1" ] | 232 | if [ "$std0" -o "$std1" ] |
199 | then | 233 | then |
@@ -209,12 +243,17 @@ disconnect_coproc() | |||
209 | 243 | ||
210 | connect() | 244 | connect() |
211 | { | 245 | { |
212 | connect_coproc TOP_PANE | 246 | if ! [ "$TOP_PANE_PID" ] |
247 | then | ||
248 | disconnect_sink ECHOSEND | ||
249 | connect_coproc TOP_PANE | ||
250 | fi | ||
251 | connect_sink ECHOSEND echo_sender | ||
213 | } | 252 | } |
214 | 253 | ||
215 | disconnect() | 254 | disconnect() |
216 | { | 255 | { |
217 | : "${TOP_PANE_STDIN@A} ${TOP_PANE_STDOUT@A} ${TOP_PANE_PID@A} ${TOP_PANE[@]@A}" | 256 | : "${TOP_PANE_IN@A} ${TOP_PANE_OUT@A} ${TOP_PANE_PID@A} ${TOP_PANE[@]@A}" |
218 | disconnect_coproc TOP_PANE | 257 | disconnect_coproc TOP_PANE |
219 | : "${ECHOSEND@A} ${ECHOSEND_PID@A}" | 258 | : "${ECHOSEND@A} ${ECHOSEND_PID@A}" |
220 | disconnect_sink ECHOSEND | 259 | disconnect_sink ECHOSEND |
@@ -223,23 +262,13 @@ disconnect() | |||
223 | sendc() | 262 | sendc() |
224 | { | 263 | { |
225 | [ "${TOP_PANE[0]}" ] || connect | 264 | [ "${TOP_PANE[0]}" ] || connect |
226 | printf '%s' "$*" >&${TOP_PANE[1]} | 265 | printf '%s' "$*" >&${TOP_PANE_OUT?Internal error} |
227 | } | 266 | } |
228 | 267 | ||
229 | send() | 268 | send() |
230 | { | 269 | { |
231 | [ "${TOP_PANE[0]}" ] || connect | 270 | [ "${TOP_PANE[0]}" ] || connect |
232 | printf '%s\n' "$*" >&${TOP_PANE[1]} | 271 | printf '%s\n' "$*" >&${TOP_PANE_OUT?Internal error} |
233 | } | ||
234 | |||
235 | restart() | ||
236 | { | ||
237 | start "$@" | ||
238 | } | ||
239 | |||
240 | start() | ||
241 | { | ||
242 | foreground "$@" | ||
243 | } | 272 | } |
244 | 273 | ||
245 | tty_forward() | 274 | tty_forward() |
@@ -252,18 +281,16 @@ tty_forward() | |||
252 | TOP_PANE() | 281 | TOP_PANE() |
253 | { | 282 | { |
254 | (exec -a bottom-pane-tty-forward socat - UNIX-CONNECT:"$TWOPANE"/socket,forever) | 283 | (exec -a bottom-pane-tty-forward socat - UNIX-CONNECT:"$TWOPANE"/socket,forever) |
255 | x kill_tty_forward -STOP | ||
256 | case "$TOP_EXIT" in | 284 | case "$TOP_EXIT" in |
257 | restart ) | 285 | restart ) |
258 | # x kill -USR1 $$ | 286 | x kill_tty_forward -STOP |
259 | x kill -INT $$ | 287 | x kill -INT $$ |
260 | # with_screen_pane 1 start_screen_pane "$@" | ||
261 | ;; | 288 | ;; |
262 | quit ) | 289 | quit ) |
263 | kill -INT $$ | ||
264 | quit | 290 | quit |
265 | ;; | 291 | ;; |
266 | prompt | * ) | 292 | prompt | * ) |
293 | x kill_tty_forward -STOP | ||
267 | focus bottom | 294 | focus bottom |
268 | ;; | 295 | ;; |
269 | esac | 296 | esac |
@@ -271,10 +298,14 @@ TOP_PANE() | |||
271 | 298 | ||
272 | connect_sink() | 299 | connect_sink() |
273 | { | 300 | { |
274 | [ "$1" ] || return | 301 | (( $# > 0 )) || set -- SINK |
275 | declare -n fd="$1" | 302 | declare -n fd="$1" |
276 | declare -n pid="${!fd}_PID" | 303 | declare -n pid="${!fd}_PID" |
277 | disconnect_sink "$fd" | 304 | if [ "$pid" ] |
305 | then | ||
306 | i "sink already running: ${!fd}" | ||
307 | return | ||
308 | fi | ||
278 | i "sink starting: ${!fd}" | 309 | i "sink starting: ${!fd}" |
279 | exec {fd}> >("${@:2}") | 310 | exec {fd}> >("${@:2}") |
280 | pid=$! | 311 | pid=$! |
@@ -282,7 +313,7 @@ connect_sink() | |||
282 | 313 | ||
283 | disconnect_sink() | 314 | disconnect_sink() |
284 | { | 315 | { |
285 | [ "$1" ] || return | 316 | (( $# > 0 )) || set -- SINK |
286 | declare -n fd="$1" | 317 | declare -n fd="$1" |
287 | [ "$fd" ] || return | 318 | [ "$fd" ] || return |
288 | declare -n pid="${!fd}_PID" | 319 | declare -n pid="${!fd}_PID" |
@@ -300,51 +331,66 @@ quiet_bg() | |||
300 | } {STDERR}>&2 2>/dev/null | 331 | } {STDERR}>&2 2>/dev/null |
301 | } | 332 | } |
302 | 333 | ||
334 | # echo_sender() reads from two inputs and writes to two outputs. | ||
335 | # | ||
336 | # Read from both: | ||
337 | # read from stdin | ||
338 | # read from $TOP_PANE_IN | ||
339 | # Write to both: | ||
340 | # write to stdout | ||
341 | # write to $TOP_PANE_OUT | ||
342 | # I.e.: | ||
343 | # read from /dev/tty | ||
344 | # read from the socat-coproc-forwarded /dev/tty in the top pane. | ||
345 | # write to /dev/tty | ||
346 | # write to the socat-coproc-forwarded /dev/tty in the top pane. | ||
303 | echo_sender() | 347 | echo_sender() |
304 | { | 348 | { |
305 | (exec -a echo_sender socat - fd:${TOP_PANE_STDIN?$0: Internal error}!!-) | | 349 | (exec -a echo_sender socat - fd:${TOP_PANE_IN?$0: Internal error}!!-) | |
306 | tee >(write-tty) >&${TOP_PANE_STDOUT?$0: Internal error} | 350 | tee >(write-tty) >&${TOP_PANE_OUT?$0: Internal error} |
307 | } | 351 | } |
308 | 352 | ||
309 | background() | 353 | restart_tty_forward() |
310 | { | 354 | { |
355 | { | ||
356 | kill -INT %tty_forward | ||
357 | disown %tty_forward | ||
358 | } &>/dev/null | ||
359 | |||
311 | with_screen_pane 1 start_screen_pane "$@" | 360 | with_screen_pane 1 start_screen_pane "$@" |
312 | focus bottom | 361 | focus bottom |
313 | |||
314 | disconnect_sink ECHOSEND | ||
315 | connect | 362 | connect |
316 | connect_sink ECHOSEND echo_sender | ||
317 | kill %tty_forward 2>/dev/null | ||
318 | %tty_forward & | ||
319 | disown %tty_forward | ||
320 | i 'starting tty_forward' | 363 | i 'starting tty_forward' |
321 | quiet_bg tty_forward >&$ECHOSEND | 364 | quiet_bg tty_forward >&$ECHOSEND |
322 | echo $! > "$TWOPANE"/tty_forward.pid | 365 | echo $! > "$TWOPANE"/tty_forward.pid |
323 | } | 366 | } |
324 | 367 | ||
325 | foreground() | 368 | attach() |
326 | { | 369 | { |
327 | while true | 370 | if ! jobs %tty_forward &>/dev/null |
328 | do | 371 | then |
329 | background "$@" | 372 | restart_tty_forward "$@" |
330 | focus top | 373 | fi |
331 | set -x | 374 | focus top |
332 | %tty_forward >/dev/null 2>&1 | 375 | { %tty_forward; } &>/dev/null |
333 | if [ "$TOP_EXIT" != restart ] || jobs %tty_forward >/dev/null 2>&1 | 376 | focus bottom |
334 | then | ||
335 | focus bottom | ||
336 | set +x | ||
337 | break | ||
338 | fi | ||
339 | set +x | ||
340 | done | ||
341 | } | 377 | } |
342 | 378 | ||
343 | twopane() | 379 | detach() |
344 | { | 380 | { |
345 | start "$@" | 381 | { |
382 | kill -INT %tty_forward | ||
383 | disown %tty_forward | ||
384 | } &>/dev/null | ||
385 | focus bottom | ||
346 | } | 386 | } |
347 | 387 | ||
388 | restart() { attach "$@"; } | ||
389 | start() { attach "$@"; } | ||
390 | foreground() { attach "$@"; } | ||
391 | |||
392 | twopane() { attach "$@"; } | ||
393 | |||
348 | quit() | 394 | quit() |
349 | { | 395 | { |
350 | screen -X quit | 396 | screen -X quit |
@@ -360,16 +406,6 @@ resize() | |||
360 | screen -X resize "$@" | 406 | screen -X resize "$@" |
361 | } | 407 | } |
362 | 408 | ||
363 | attach() | ||
364 | { | ||
365 | if jobs %tty_forward >/dev/null 2>&1 | ||
366 | then | ||
367 | %tty_forward | ||
368 | else | ||
369 | foreground | ||
370 | fi | ||
371 | } | ||
372 | |||
373 | SIGUSR1() | 409 | SIGUSR1() |
374 | { | 410 | { |
375 | i "SIGUSR1: ${BASH_COMMAND@A}" | 411 | i "SIGUSR1: ${BASH_COMMAND@A}" |
@@ -382,21 +418,45 @@ SIGCHLD() | |||
382 | 418 | ||
383 | prompt_command() | 419 | prompt_command() |
384 | { | 420 | { |
385 | set -- 'unset PROMPT_COMMAND; restart' | 421 | [ "$TOP_EXIT" = 'restart' ] || return 0 |
422 | |||
423 | if [ "$SIG" = INT ] | ||
424 | then | ||
425 | finally 'unset SIG; start' detach | ||
426 | return | ||
427 | fi | ||
428 | |||
429 | local job jobnew | ||
430 | if job=$(jobs -n %tty_forward 2>/dev/null) | ||
431 | then | ||
432 | jobnew=y | ||
433 | else | ||
434 | job=$(jobs %tty_forward 2>/dev/null) | ||
435 | jobnew= | ||
436 | fi | ||
437 | |||
438 | if [ ! "$job" ] | ||
439 | then | ||
440 | finally 'start' : | ||
441 | return | ||
442 | fi | ||
386 | 443 | ||
387 | exec {FINALLY_0}<&0 {FINALLY_1}>&1 {FINALLY_2}>&2 | 444 | if [ "$jobnew" ] |
388 | if ! [ "$DEBUG" ] | ||
389 | then | 445 | then |
390 | exec &>/dev/null | 446 | read _ jobstatus _ <<< "$job" |
447 | if [ "$jobstatus" = 'Running' ] | ||
448 | then | ||
449 | jobs -x finally 'attach' kill -CONT %tty_forward | ||
450 | fi | ||
451 | return | ||
391 | fi | 452 | fi |
392 | cmd="history -d -1; exec <&$FINALLY_0 >&$FINALLY_1 >&$FINALLY_2; $*" | ||
393 | exec <<< "$cmd" | ||
394 | } | 453 | } |
395 | 454 | ||
396 | SIGINT() | 455 | SIGINT() |
397 | { | 456 | { |
398 | i INT "${BASH_COMMAND@A}" | 457 | i INT "${BASH_COMMAND@A}" |
399 | kill $TOP_PANE_PID $ECHOSEND_PID 2>/dev/null | 458 | kill $TOP_PANE_PID $ECHOSEND_PID 2>/dev/null |
459 | SIG=INT | ||
400 | PROMPT_COMMAND=prompt_command | 460 | PROMPT_COMMAND=prompt_command |
401 | } | 461 | } |
402 | 462 | ||