diff options
Diffstat (limited to 'src/twopane.bash')
-rwxr-xr-x | src/twopane.bash | 175 |
1 files changed, 101 insertions, 74 deletions
diff --git a/src/twopane.bash b/src/twopane.bash index 1ed840b..59077fe 100755 --- a/src/twopane.bash +++ b/src/twopane.bash | |||
@@ -1,6 +1,6 @@ | |||
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | BASH_ARGV0=twopane.bash | 2 | BASH_ARGV0=twopane.bash |
3 | echo -n "$0" >/proc/$$/comm | 3 | echo -n "$0" >/proc/$BASHPID/comm |
4 | set -e | 4 | set -e |
5 | set -f | 5 | set -f |
6 | set -o pipefail | 6 | set -o pipefail |
@@ -8,17 +8,26 @@ shopt -s lastpipe | |||
8 | 8 | ||
9 | . read_chars.bash | 9 | . read_chars.bash |
10 | 10 | ||
11 | kill_tty_forward() | ||
12 | { | ||
13 | read pid < "$TWOPANE"/tty_forward.pid | ||
14 | /bin/kill "${1:-'-INT'}" -- -$pid 2>/dev/null | ||
15 | } | ||
16 | |||
11 | BOT_SIZE=8 | 17 | BOT_SIZE=8 |
12 | BOT_TITLE=input | 18 | BOT_TITLE=input |
13 | 19 | ||
14 | if [ $# = 0 ] | 20 | if [ $# = 0 ] |
15 | then | 21 | then |
16 | TOP_CMD="$SHELL -i" | 22 | TOP_CMD="$SHELL -i" |
17 | BOT_CMD=foreground | 23 | BOT_CMD=start |
18 | #BOT_CMD=start | ||
19 | #TOP_EXIT=quit | 24 | #TOP_EXIT=quit |
20 | #TOP_EXIT=prompt | 25 | TOP_EXIT=prompt |
21 | TOP_EXIT=restart | 26 | #TOP_EXIT=restart |
27 | elif [ "$TWOPANE" -a "$*" = detach ] | ||
28 | then | ||
29 | kill_tty_forward -TSTP | ||
30 | exit | ||
22 | else | 31 | else |
23 | TOP_CMD=$* | 32 | TOP_CMD=$* |
24 | BOT_CMD=start | 33 | BOT_CMD=start |
@@ -75,26 +84,41 @@ screen -ln -t "$BOT_TITLE" 0 bash --noprofile --rcfile "$TWOPANE"/bashrc -i | |||
75 | layout save 0 | 84 | layout save 0 |
76 | . | 85 | . |
77 | 86 | ||
87 | with_screen_pane() | ||
88 | { | ||
89 | (( $# > 1 )) || return | ||
90 | declare -i PANE="$1" LOCK | ||
91 | (( PANE > 0 )) || return | ||
92 | ( | ||
93 | flock $LOCK | ||
94 | "$2" "$1" "${@:3}" | ||
95 | ) {LOCK}>"$TWOPANE"/pane$PANE | ||
96 | } | ||
97 | |||
78 | check_screen_pane() | 98 | check_screen_pane() |
79 | { | 99 | { |
80 | screen -p "$pane" -Q info >/dev/null | 100 | (( PANE > 0 )) || return |
101 | screen -p "$PANE" -Q info >/dev/null | ||
81 | } | 102 | } |
82 | 103 | ||
83 | kill_screen_pane() | 104 | kill_screen_pane() |
84 | { | 105 | { |
85 | while check_screen_pane | 106 | (( PANE > 0 )) || return |
107 | while check_screen_pane "$PANE" | ||
86 | do | 108 | do |
87 | screen -p "$pane" -X kill | 109 | screen -p "$PANE" -X kill |
88 | done | 110 | done |
89 | } | 111 | } |
90 | 112 | ||
91 | start_screen_pane() | 113 | start_screen_pane() |
92 | { | 114 | { |
93 | declare -i pane="${1:-1}" | 115 | (( PANE > 0 )) || return |
94 | (( pane > 0 )) || return | 116 | if check_screen_pane "$PANE" |
95 | if check_screen_pane "$pane" | ||
96 | then | 117 | then |
118 | i "pane already running: $PANE" | ||
97 | return | 119 | return |
120 | else | ||
121 | i "starting pane: $PANE" | ||
98 | fi | 122 | fi |
99 | shift | 123 | shift |
100 | if [ $# = 0 ] | 124 | if [ $# = 0 ] |
@@ -105,49 +129,55 @@ start_screen_pane() | |||
105 | TOP_TITLE="Command: ${*@Q}" | 129 | TOP_TITLE="Command: ${*@Q}" |
106 | screen -X focus top | 130 | screen -X focus top |
107 | screen -X screen -ln -t "$TOP_TITLE" 1 "$@" | 131 | screen -X screen -ln -t "$TOP_TITLE" 1 "$@" |
108 | start_screen_pane_subprocess "$pane" | 132 | start_screen_pane_subprocess |
109 | } | 133 | } |
110 | 134 | ||
111 | start_screen_pane_subprocess() | 135 | start_screen_pane_subprocess() |
112 | { | 136 | { |
113 | declare -i pane="${1:-1}" | 137 | (( PANE > 0 )) || return |
114 | subproc_command=( | 138 | subproc_command=( |
115 | exec -a top-pane-tty-forward | 139 | exec -a top-pane-tty-forward |
116 | socat | 140 | socat |
117 | UNIX-LISTEN:"$TWOPANE"/socket | 141 | UNIX-LISTEN:"$TWOPANE"/socket |
118 | STDIN,cfmakeraw,opost=1,onlcr=1!!STDOUT | 142 | STDIN,cfmakeraw,opost=1,onlcr=1!!STDOUT |
119 | ) | 143 | ) |
120 | screen -p "$pane" -X exec .!. bash -c "${subproc_command[*]}" | 144 | screen -p "$PANE" -X exec .!. bash -c "${subproc_command[*]}" |
121 | } | 145 | } |
122 | 146 | ||
123 | restart_screen_pane() | 147 | restart_pane() |
124 | { | 148 | { |
149 | (( PANE > 0 )) || return | ||
125 | kill_screen_pane | 150 | kill_screen_pane |
126 | start_screen_pane "$@" | 151 | start_screen_pane "$@" |
127 | } | 152 | } |
128 | 153 | ||
154 | restart_top_pane() | ||
155 | { | ||
156 | with_screen_pane 1 restart_pane "$@" | ||
157 | } | ||
158 | |||
129 | connect_coproc() | 159 | connect_coproc() |
130 | { | 160 | { |
161 | [ $# -gt 0 ] || set -- COPROC | ||
131 | declare -n coproc="$1" | 162 | declare -n coproc="$1" |
132 | shift | 163 | shift |
164 | [ $# -gt 0 ] || set -- "${!coproc}" | ||
133 | declare -n std1="${!coproc}_STDOUT" | 165 | declare -n std1="${!coproc}_STDOUT" |
134 | declare -n std0="${!coproc}_STDIN" | 166 | declare -n std0="${!coproc}_STDIN" |
135 | declare -n opid="${!coproc}_PID" | 167 | declare -n opid="${!coproc}_PID" |
136 | |||
137 | if [ $# = 0 ] | ||
138 | then | ||
139 | set -- "${!coproc}" | ||
140 | fi | ||
141 | if ! [ "$opid" ] | 168 | if ! [ "$opid" ] |
142 | then | 169 | then |
170 | i "coproc starting: ${!coproc}" | ||
143 | local STDERR | 171 | local STDERR |
144 | { | 172 | { |
145 | coproc "${!coproc}" \ | 173 | coproc "${!coproc}" \ |
146 | { | 174 | { |
147 | "$@" | 175 | "$@" |
148 | } 2>&$STDERR {STDERR}>&- | 176 | } 2>&$STDERR {STDERR}>&- |
177 | disown "%coproc ${!coproc} " | ||
149 | } {STDERR}>&2 2>/dev/null | 178 | } {STDERR}>&2 2>/dev/null |
150 | disown | 179 | else |
180 | i "coproc already running: ${!coproc}" | ||
151 | fi | 181 | fi |
152 | # The copied file descriptors (unlike the original coprocess | 182 | # The copied file descriptors (unlike the original coprocess |
153 | # file descriptors, in ${coproc[0]} and ${coproc[1]}) can be | 183 | # file descriptors, in ${coproc[0]} and ${coproc[1]}) can be |
@@ -160,7 +190,7 @@ connect_coproc() | |||
160 | 190 | ||
161 | disconnect_coproc() | 191 | disconnect_coproc() |
162 | { | 192 | { |
163 | [ $# = 1 ] || return | 193 | [ $# -gt 0 ] || set -- COPROC |
164 | declare -n coproc="$1" | 194 | declare -n coproc="$1" |
165 | declare -n std0="${!coproc}_STDIN" | 195 | declare -n std0="${!coproc}_STDIN" |
166 | declare -n std1="${!coproc}_STDOUT" | 196 | declare -n std1="${!coproc}_STDOUT" |
@@ -212,46 +242,21 @@ start() | |||
212 | foreground "$@" | 242 | foreground "$@" |
213 | } | 243 | } |
214 | 244 | ||
215 | check_tty_reader() | ||
216 | { | ||
217 | [ "$TTY_FORWARD_PID" ] || return | ||
218 | TTY_FORWARD_JOBSPEC=$(jobs -sl | pid_to_jobspec "$TTY_FORWARD_PID") | ||
219 | [ "$TTY_FORWARD_JOBSPEC" ] | ||
220 | } | ||
221 | |||
222 | pid_to_jobspec() | ||
223 | { | ||
224 | while read jspec pid status cmd | ||
225 | do | ||
226 | [ "$pid" = "$1" ] || continue | ||
227 | jspec=${jspec%]*} | ||
228 | jspec=%${jspec#[} | ||
229 | echo $jspec | ||
230 | return | ||
231 | done | ||
232 | false | ||
233 | } | ||
234 | |||
235 | tty_forward() | 245 | tty_forward() |
236 | { | 246 | { |
237 | printf '%d\n' "$BASHPID" > "$TWOPANE"/tty_forward.pid | 247 | printf '%d\n' "$BASHPID" > "$TWOPANE"/tty_forward.pid |
238 | read-tty | 248 | read-tty |
239 | } | 249 | } |
240 | 250 | ||
241 | kill_tty_forward() | ||
242 | { | ||
243 | read pid < "$TWOPANE"/tty_forward.pid | ||
244 | /bin/kill -INT -- -$pid 2>/dev/null | ||
245 | } | ||
246 | |||
247 | TOP_PANE() | 251 | TOP_PANE() |
248 | { | 252 | { |
249 | (exec -a bottom-pane-tty-forward socat - UNIX-CONNECT:"$TWOPANE"/socket,forever) | 253 | (exec -a bottom-pane-tty-forward socat - UNIX-CONNECT:"$TWOPANE"/socket,forever) |
250 | kill_tty_forward | 254 | x kill_tty_forward |
251 | case "$TOP_EXIT" in | 255 | case "$TOP_EXIT" in |
252 | restart ) | 256 | restart ) |
253 | start_screen_pane 1 | 257 | # x kill -USR1 $$ |
254 | kill -USR1 $$ | 258 | x kill -INT $$ |
259 | with_screen_pane 1 start_screen_pane "$@" | ||
255 | ;; | 260 | ;; |
256 | quit ) | 261 | quit ) |
257 | kill -INT $$ | 262 | kill -INT $$ |
@@ -263,11 +268,13 @@ TOP_PANE() | |||
263 | esac | 268 | esac |
264 | } | 269 | } |
265 | 270 | ||
266 | sink() | 271 | connect_sink() |
267 | { | 272 | { |
273 | [ "$1" ] || return | ||
268 | declare -n fd="$1" | 274 | declare -n fd="$1" |
269 | declare -n pid="${!fd}_PID" | 275 | declare -n pid="${!fd}_PID" |
270 | disconnect_sink "$fd" | 276 | disconnect_sink "$fd" |
277 | i "sink starting: ${!fd}" | ||
271 | exec {fd}> >("${@:2}") | 278 | exec {fd}> >("${@:2}") |
272 | pid=$! | 279 | pid=$! |
273 | } | 280 | } |
@@ -278,6 +285,7 @@ disconnect_sink() | |||
278 | declare -n fd="$1" | 285 | declare -n fd="$1" |
279 | [ "$fd" ] || return | 286 | [ "$fd" ] || return |
280 | declare -n pid="${!fd}_PID" | 287 | declare -n pid="${!fd}_PID" |
288 | i "sink stopping: ${!fd}" | ||
281 | exec {fd}>&- | 289 | exec {fd}>&- |
282 | kill $pid 2>/dev/null | 290 | kill $pid 2>/dev/null |
283 | unset fd pid | 291 | unset fd pid |
@@ -299,30 +307,36 @@ echo_sender() | |||
299 | 307 | ||
300 | background() | 308 | background() |
301 | { | 309 | { |
302 | start_screen_pane 1 "$@" | 310 | with_screen_pane 1 start_screen_pane "$@" |
303 | focus bottom | 311 | focus bottom |
304 | 312 | ||
305 | disconnect_sink ECHOSEND | 313 | disconnect_sink ECHOSEND |
306 | connect | 314 | connect |
307 | sink ECHOSEND echo_sender | 315 | connect_sink ECHOSEND echo_sender |
316 | kill %tty_forward | ||
317 | %tty_forward & | ||
318 | disown %tty_forward | ||
319 | i 'starting tty_forward' | ||
308 | quiet_bg tty_forward >&$ECHOSEND | 320 | quiet_bg tty_forward >&$ECHOSEND |
309 | 321 | echo $! > "$TWOPANE"/tty_forward.pid | |
310 | TTY_FORWARD_PID=$! | ||
311 | printf '%d\n' "$TTY_FORWARD_PID" > "$TWOPANE"/tty_forward.pid | ||
312 | printf '%s\n' \ | ||
313 | "#!/bin/bash" \ | ||
314 | "kill -TSTP $TTY_FORWARD_PID" \ | ||
315 | "screen -X focus bottom" \ | ||
316 | > "$TWOPANE"/unforward | ||
317 | chmod +x "$TWOPANE"/unforward | ||
318 | } | 322 | } |
319 | 323 | ||
320 | foreground() | 324 | foreground() |
321 | { | 325 | { |
322 | check_tty_reader || background "$@" | 326 | while true |
323 | focus top | 327 | do |
324 | fg %tty_forward >/dev/null 2>&1 | 328 | background "$@" |
325 | focus bottom | 329 | focus top |
330 | set -x | ||
331 | %tty_forward >/dev/null 2>&1 | ||
332 | if [ "$TOP_EXIT" != restart ] || jobs %tty_forward >/dev/null 2>&1 | ||
333 | then | ||
334 | focus bottom | ||
335 | set +x | ||
336 | break | ||
337 | fi | ||
338 | set +x | ||
339 | done | ||
326 | } | 340 | } |
327 | 341 | ||
328 | twopane() | 342 | twopane() |
@@ -345,34 +359,47 @@ resize() | |||
345 | screen -X resize "$@" | 359 | screen -X resize "$@" |
346 | } | 360 | } |
347 | 361 | ||
362 | attach() | ||
363 | { | ||
364 | if jobs %tty_forward >/dev/null 2>&1 | ||
365 | then | ||
366 | %tty_forward | ||
367 | else | ||
368 | foreground | ||
369 | fi | ||
370 | } | ||
371 | |||
348 | SIGUSR1() | 372 | SIGUSR1() |
349 | { | 373 | { |
350 | i SIGUSR1 "${BASH_COMMAND@A}" | 374 | i "SIGUSR1: ${BASH_COMMAND@A}" |
375 | } | ||
376 | |||
377 | SIGCHLD() | ||
378 | { | ||
379 | i "SIGCHILD: ${BASH_COMMAND@A}" | ||
351 | } | 380 | } |
352 | 381 | ||
353 | our_bashrc_main() | 382 | our_bashrc_main() |
354 | { | 383 | { |
384 | BASH_ARGV0=twopane | ||
385 | echo -n "$0" >/proc/$BASHPID/comm | ||
355 | set -m | 386 | set -m |
356 | set -f | 387 | set -f |
357 | set -o pipefail | 388 | set -o pipefail |
358 | trap 'quit' EXIT | ||
359 | trap 'SIGUSR1' USR1 | 389 | trap 'SIGUSR1' USR1 |
390 | # trap 'SIGCHLD' CHLD | ||
391 | trap '! [ "$DEBUG" ] || read -p "Exit> "; quit' EXIT | ||
360 | export PS1="$BOT_TITLE\\\$ " | 392 | export PS1="$BOT_TITLE\\\$ " |
361 | } | 393 | } |
362 | 394 | ||
363 | save_file bashrc <<. | 395 | save_file bashrc <<. |
364 | BASH_ARGV0=twopane | ||
365 | echo -n "\$0" >/proc/\$\$/comm | ||
366 | ${TOP_CMD@A} | 396 | ${TOP_CMD@A} |
367 | ${BOT_CMD@A} | 397 | ${BOT_CMD@A} |
368 | ${TOP_TITLE@A} | 398 | ${TOP_TITLE@A} |
369 | ${BOT_TITLE@A} | 399 | ${BOT_TITLE@A} |
370 | |||
371 | ${TOP_EXIT@A} | 400 | ${TOP_EXIT@A} |
372 | |||
373 | $(declare -f) | 401 | $(declare -f) |
374 | our_bashrc_main | 402 | our_bashrc_main |
375 | |||
376 | $BOT_CMD | 403 | $BOT_CMD |
377 | . | 404 | . |
378 | 405 | ||