fix Ctrl-C swallowing the next line
readline only redraws the prompt on Ctrl-C: the half-typed text stays in its buffer and the interrupt flag outlives the prompt, so the line typed next was silently discarded. Register a signal event hook that empties the line, the way bash does, and clear the flag around readline instead of eating the line: a Ctrl-C during a command still aborts the turn, and one during a multi-line task still abandons it. Two pty-based regression tests cover the idle-prompt and mid-line cases.
This commit is contained in:
@@ -119,6 +119,71 @@ LEFT=$(ps -o args= 2>/dev/null | grep -c '^sleep 41$')
|
||||
[ "$LEFT" -eq 0 ]
|
||||
ok $? 'a timed out command takes its process group with it' "$LEFT process(es) left"
|
||||
|
||||
# --- Ctrl-C must not eat the next line --------------------------------
|
||||
# readline only redraws the prompt on Ctrl-C: the half-typed text
|
||||
# stays in its buffer and the interrupt flag outlives the prompt.
|
||||
# Both used to swallow the next command silently. This needs a real
|
||||
# tty, so the agent runs on a pty driven by a small helper.
|
||||
D=$WORK/ctrlc
|
||||
cat >"$WORK/ptyrun.c" <<'PTY_EOF'
|
||||
/* Drive PROG on a pty from a script on stdin. A line "INT" sends
|
||||
SIGINT, a line "RAW text" writes text without a newline, and any
|
||||
other line is written with a newline appended. A short pause
|
||||
follows every step. Everything PROG prints is relayed to stdout. */
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/wait.h>
|
||||
#include <pty.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int m, st; pid_t k; char buf[4096], line[4096]; struct timeval tv; fd_set fs;
|
||||
if(argc < 2) { fprintf(stderr, "usage: ptyrun PROG [ARG...] < script\n"); return 2; }
|
||||
k = forkpty(&m, 0, 0, 0);
|
||||
if(k < 0) { perror("forkpty"); return 2; }
|
||||
if(!k) { execvp(argv[1], argv + 1); _exit(127); }
|
||||
fcntl(m, F_SETFL, O_NONBLOCK);
|
||||
while(fgets(line, sizeof line, stdin)) {
|
||||
size_t l = strlen(line);
|
||||
while(l && (line[l - 1] == '\n' || line[l - 1] == '\r')) line[--l] = 0;
|
||||
usleep(400000);
|
||||
if(!strcmp(line, "INT")) { kill(k, SIGINT); continue; }
|
||||
if(!strncmp(line, "RAW ", 4)) { if(write(m, line + 4, strlen(line + 4)) < 0) break; continue; }
|
||||
if(write(m, line, strlen(line)) < 0 || write(m, "\n", 1) < 0) break;
|
||||
}
|
||||
for(;;) {
|
||||
FD_ZERO(&fs); FD_SET(m, &fs); tv.tv_sec = 10; tv.tv_usec = 0;
|
||||
if(select(m + 1, &fs, 0, 0, &tv) <= 0) break;
|
||||
st = read(m, buf, sizeof buf);
|
||||
if(st <= 0) break;
|
||||
if(write(1, buf, st) < 0) break;
|
||||
}
|
||||
kill(k, SIGKILL);
|
||||
waitpid(k, &st, 0);
|
||||
return 0;
|
||||
}
|
||||
PTY_EOF
|
||||
# forkpty lives in libc since glibc 2.34; older systems need -lutil.
|
||||
cc -Wall -Wextra -O2 -std=c99 -o "$WORK/ptyrun" "$WORK/ptyrun.c" 2>/dev/null \
|
||||
|| cc -Wall -Wextra -O2 -std=c99 -o "$WORK/ptyrun" "$WORK/ptyrun.c" -lutil 2>/dev/null
|
||||
[ -x "$WORK/ptyrun" ] || { echo "test: the pty helper did not build" >&2; exit 1; }
|
||||
mkdir -p "$D"
|
||||
( cd "$D" && printf 'INT\n!echo PTY_CI\n/quit\n' | "$WORK/ptyrun" "$SOLO" ) \
|
||||
>"$WORK/ctrlc.out" 2>&1
|
||||
grep -q -- '--- \$ echo PTY_CI' "$WORK/ctrlc.out"
|
||||
ok $? 'a command typed after Ctrl-C at the prompt still runs'
|
||||
( cd "$D" && printf 'RAW echo partial\nINT\n!echo PTY_FRESH\n/quit\n' | "$WORK/ptyrun" "$SOLO" ) \
|
||||
>"$WORK/ctrlc2.out" 2>&1
|
||||
grep -q -- '--- \$ echo PTY_FRESH' "$WORK/ctrlc2.out"
|
||||
ok $? 'a command typed after Ctrl-C mid-line still runs'
|
||||
if grep -q 'partialPTY_FRESH\|partial!echo' "$WORK/ctrlc2.out"; then rc=1; else rc=0; fi
|
||||
ok "$rc" 'Ctrl-C mid-line clears the half-typed text'
|
||||
|
||||
# --- the tool description must quote the real timeout -------------------------
|
||||
D=$WORK/desc
|
||||
printf '/quit\n' | solo_in "$D" SOLO_TIMEOUT=42 >/dev/null 2>&1
|
||||
|
||||
Reference in New Issue
Block a user