feat(xce-test-e2e-primitives): !cmd shell-escape via system(), feeds $? (task 2.1)

This commit is contained in:
Rongsong Shen 2026-06-19 15:45:09 +00:00
parent 7ad63e7403
commit 0584a6229a
2 changed files with 36 additions and 1 deletions

View file

@ -17,7 +17,7 @@
- _Depends: 1.1_
- [ ] 2. Shell-escape
- [ ] 2.1 `!<cmd>` runs a shell command and feeds `$?`
- [x] 2.1 `!<cmd>` runs a shell command and feeds `$?`
- In `preprocess_and_run`, when the first non-whitespace char is `!`, run the remainder via `system()`; set `g_last_status = WEXITSTATUS(rc)` (non-zero on `system` failure); a non-zero status is a command failure (subject to 1.1's abort/continue + exit code).
- Observable: `!true` succeeds and sets `$?`=0; `!false` fails the session and sets `$?`≠0; a script can `!kill ...` / `!bash start.sh ...` to drive node lifecycle.
- _Requirements: 2.1, 2.2, 2.3, 2.4_

View file

@ -433,6 +433,41 @@ int preprocess_and_run(char* line)
return 0;
}
/* Shell-escape: a line whose first non-whitespace char is `!` runs the
* remainder via system(). The shell command's exit status becomes
* g_last_status (the `$?` auto-variable); a non-zero status is a command
* failure subject to the same abort-vs-continue rules as execute_line. The
* remainder is passed raw to the shell (no parse_argv / auto-var
* substitution — that is task 3.1). (xce-test-e2e-primitives task 2.1) */
if (*p == '!') {
const char* rest = p + 1;
int sysrc = system(rest);
int status;
if (sysrc == -1) {
/* system() itself failed to run the shell — treat as failure. */
status = 127;
} else if (WIFEXITED(sysrc)) {
status = WEXITSTATUS(sysrc);
} else {
/* Signaled / abnormal termination — non-zero failure status. */
status = 1;
}
g_last_status = status;
if (status != 0) {
g_session_failed = 1;
if (!g_continue_on_error) {
return 1; /* abort: stop the read loop */
}
} else {
add_history(line);
}
return 0;
}
rc = execute_line(line);
g_last_status = (rc < 0) ? (rc) : 0;