aboutsummaryrefslogtreecommitdiff
path: root/modules/ext/shell.js
diff options
context:
space:
mode:
Diffstat (limited to 'modules/ext/shell.js')
-rw-r--r--modules/ext/shell.js44
1 files changed, 42 insertions, 2 deletions
diff --git a/modules/ext/shell.js b/modules/ext/shell.js
index cb26484..ff07d92 100644
--- a/modules/ext/shell.js
+++ b/modules/ext/shell.js
@@ -34,6 +34,46 @@ class ShellModule {
this.commands['script'] = (args) => this._cmdScript(args);
this.commands['sh'] = (args) => this._cmdScript(args);
this.commands['set'] = () => this._cmdSet();
+ // v4.6.133: `test` / `[` were not registered, so `[ -f /x ] && echo yes` fell through to the
+ // AI fallback and asked a model whether a file exists. _evalTest() already implemented the
+ // operators (-f -d -e -z -n ! = !=) for the if/while parser; the command was simply missing.
+ // `test` already means "run the regression suite" in the on-demand test-suite module, so a
+ // non-expression argument list is delegated to whatever handler was registered before us.
+ const prevTest = this.commands['test'];
+ this.commands['test'] = (args) => this._cmdTest(args, 'test', prevTest);
+ this.commands['['] = (args) => this._cmdTest(args, '[');
+ }
+
+ /** True when the argv looks like a POSIX test expression rather than a suite-runner subcommand. */
+ _looksLikeTest(expr) {
+ if (!expr) return false;
+ if (/^(?:!(?:\s|$)|-[fderwxszn]\b)/.test(expr)) return true; // `-f x`, `! -d y`
+ if (/\s(?:=|==|!=)\s/.test(expr)) return true; // `a = b`
+ return false;
+ }
+
+ /** POSIX-ish `test`/`[`: silent on success, throws a silent error on false so `&&`/`||` skip. */
+ _cmdTest(args, name, fallback) {
+ let expr = (args || []).join(' ').trim();
+ if (name === 'test' && !this._looksLikeTest(expr) && typeof fallback === 'function') {
+ return fallback(args); // `test`, `test auto`, `test list` -> the suite runner
+ }
+ if (name === '[') {
+ if (!expr.endsWith(']')) throw new Error("[ : missing ']'");
+ expr = expr.slice(0, -1).trim();
+ }
+ if (!expr) { // `test` with no expression: false, and no output
+ const e = new Error(''); e.silent = true; throw e;
+ }
+ if (!this._evalTest(expr)) {
+ const e = new Error(''); e.silent = true; throw e; // POSIX: exit 1 prints nothing
+ }
+ }
+
+ /** Error printer shared by the shell paths: `silent` errors are control flow (a false test), not failures. */
+ _printErr(term, e) {
+ if (!e || e.silent) return;
+ (term || this.terminal).print('Error: ' + (e.message || e), 'error');
}
// ── Hook into terminal.executeCommand ──
@@ -123,7 +163,7 @@ class ShellModule {
await self.commands[cmd](args);
lastOk = true;
} catch(e) {
- this.print('Error: ' + e.message, 'error');
+ self._printErr(this, e);
lastOk = false;
}
} else {
@@ -131,7 +171,7 @@ class ShellModule {
await this.security.checkAndRun(expandedCmd);
lastOk = true;
} catch(e) {
- this.print('Error: ' + e.message, 'error');
+ self._printErr(this, e);
lastOk = false;
}
}