feat(xce-test-e2e-primitives): auto-var substitution $?/$jobid/$jobcount (task 3.1)
This commit is contained in:
parent
0584a6229a
commit
5331832565
2 changed files with 138 additions and 2 deletions
|
|
@ -16,6 +16,7 @@
|
|||
#include <cjson/s2j.h>
|
||||
|
||||
#include "client.h"
|
||||
#include "config.h" /* XCE_MAXNAME_LEN */
|
||||
#include "controller.capnp.h"
|
||||
#include "delay.h"
|
||||
#include "idgen.h"
|
||||
|
|
@ -133,6 +134,19 @@ static int g_session_failed = 0;
|
|||
static int g_continue_on_error = 0;
|
||||
static int g_last_status = 0;
|
||||
|
||||
/* Auto-variable state for the fixed `$jobid` / `$jobcount` substitution
|
||||
* (xce-test-e2e-primitives task 3.1, Req 3.1/3.3/3.5/3.6).
|
||||
*
|
||||
* g_last_jobid most recently acknowledged JobId as a string; empty ("")
|
||||
* means unset. Populated later by cmd_jobsub (task 4.1).
|
||||
* g_last_jobcount most recent job count; -1 means unset. Populated later by
|
||||
* cmd_joblist (task 5.1).
|
||||
*
|
||||
* Until those tasks land, both are always unset — which is exactly what makes
|
||||
* an unset `$jobid`/`$jobcount` reference fail the command (Req 3.6). */
|
||||
static char g_last_jobid[XCE_MAXNAME_LEN] = { 0 };
|
||||
static int g_last_jobcount = -1;
|
||||
|
||||
static idgen_ctx_t idctx;
|
||||
delaymgr_t delaymgr;
|
||||
|
||||
|
|
@ -405,6 +419,105 @@ int execute_line(char* line)
|
|||
return rc;
|
||||
}
|
||||
|
||||
/* Substitute the fixed auto-variables `$?`, `$jobid`, `$jobcount` on a raw line
|
||||
* before tokenizing (xce-test-e2e-primitives task 3.1, Req 3.1/3.3/3.5/3.6).
|
||||
*
|
||||
* Only WHOLE-TOKEN occurrences are recognized: a `$name` must be bounded by the
|
||||
* start of the line / whitespace on its left and whitespace / end-of-line on its
|
||||
* right. A `$name` glued to other non-whitespace (e.g. `x$jobid`, `$jobidx`) is
|
||||
* NOT a recognized auto-variable and is copied through verbatim. No other `$...`
|
||||
* syntax is recognized: an unknown whole token such as `$other` is also copied
|
||||
* verbatim (it is simply not one of the three names).
|
||||
*
|
||||
* Replacements:
|
||||
* $? -> decimal of g_last_status (always available).
|
||||
* $jobid -> g_last_jobid if set (non-empty); unset => command failure.
|
||||
* $jobcount -> decimal of g_last_jobcount if set (>=0); unset => failure.
|
||||
*
|
||||
* Writes the expanded line into `out` (capacity `outsz`, always NUL-terminated
|
||||
* on success). Returns 0 on success, -EINVAL on an unset auto-var reference,
|
||||
* -ENOSPC if the expansion would overflow `out`. On any error `out` content is
|
||||
* unspecified and the caller must fail the command rather than run it. */
|
||||
static int substitute_autovars(const char* line, char* out, size_t outsz)
|
||||
{
|
||||
const char* p = line;
|
||||
size_t o = 0;
|
||||
|
||||
if ((line == NULL) || (out == NULL) || (outsz == 0)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
while (*p != '\0') {
|
||||
/* A `$` starts a candidate auto-var only when it begins a token, i.e.
|
||||
* the previous char is the line start or whitespace. */
|
||||
int at_token_start
|
||||
= (p == line) || (*(p - 1) == ' ') || (*(p - 1) == '\t');
|
||||
|
||||
if ((*p == '$') && at_token_start) {
|
||||
const char* name = p + 1;
|
||||
const char* end = name;
|
||||
const char* repl = NULL;
|
||||
char numbuf[32];
|
||||
|
||||
/* Scan to the token end (whitespace or NUL). */
|
||||
while ((*end != '\0') && (*end != ' ') && (*end != '\t')) {
|
||||
end++;
|
||||
}
|
||||
|
||||
size_t namelen = (size_t)(end - name);
|
||||
|
||||
if ((namelen == 1) && (name[0] == '?')) {
|
||||
snprintf(numbuf, sizeof(numbuf), "%d", g_last_status);
|
||||
repl = numbuf;
|
||||
} else if ((namelen == 5) && (strncmp(name, "jobid", 5) == 0)) {
|
||||
if (g_last_jobid[0] == '\0') {
|
||||
fprintf(stderr,
|
||||
"autovar: $jobid referenced before set\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
repl = g_last_jobid;
|
||||
} else if ((namelen == 8) && (strncmp(name, "jobcount", 8) == 0)) {
|
||||
if (g_last_jobcount < 0) {
|
||||
fprintf(stderr,
|
||||
"autovar: $jobcount referenced before set\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
snprintf(numbuf, sizeof(numbuf), "%d", g_last_jobcount);
|
||||
repl = numbuf;
|
||||
}
|
||||
|
||||
if (repl != NULL) {
|
||||
size_t rlen = strlen(repl);
|
||||
|
||||
if ((o + rlen) >= outsz) {
|
||||
return -ENOSPC;
|
||||
}
|
||||
memcpy(out + o, repl, rlen);
|
||||
o += rlen;
|
||||
p = end; /* consume the whole `$name` token */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Not a recognized auto-var: copy the whole token verbatim. */
|
||||
while (p < end) {
|
||||
if ((o + 1) >= outsz) {
|
||||
return -ENOSPC;
|
||||
}
|
||||
out[o++] = *p++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((o + 1) >= outsz) {
|
||||
return -ENOSPC;
|
||||
}
|
||||
out[o++] = *p++;
|
||||
}
|
||||
|
||||
out[o] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Main-loop wrapper around execute_line that implements the session verdict
|
||||
* (xce-test-e2e-primitives task 1.1):
|
||||
* - blank / whitespace-only lines and `#`-comment lines are ignored;
|
||||
|
|
@ -468,7 +581,28 @@ int preprocess_and_run(char* line)
|
|||
return 0;
|
||||
}
|
||||
|
||||
rc = execute_line(line);
|
||||
/* Substitute the fixed auto-variables (`$?`, `$jobid`, `$jobcount`) before
|
||||
* tokenizing (xce-test-e2e-primitives task 3.1). An unset `$jobid` /
|
||||
* `$jobcount` reference (Req 3.6) — or an overflow — fails the command:
|
||||
* we do NOT run execute_line, and the result is treated exactly like a
|
||||
* failing command (g_last_status non-zero, g_session_failed, abort vs
|
||||
* continue). The leading `!` shell-escape above is intentionally NOT
|
||||
* substituted: that line goes raw to system() and the shell owns `$?`. */
|
||||
{
|
||||
char expanded[MAX_LINE_LEN];
|
||||
int subrc = substitute_autovars(p, expanded, sizeof(expanded));
|
||||
|
||||
if (subrc < 0) {
|
||||
g_last_status = subrc;
|
||||
g_session_failed = 1;
|
||||
if (!g_continue_on_error) {
|
||||
return 1; /* abort: stop the read loop */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = execute_line(expanded);
|
||||
}
|
||||
|
||||
g_last_status = (rc < 0) ? (rc) : 0;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue