/* * autovars.c -- xce_test auto-variable substitution (see include/autovars.h). */ #include #include #include #include "autovars.h" static void set_err(char* errbuf, size_t errlen, const char* msg) { if ((errbuf != NULL) && (errlen > 0)) { snprintf(errbuf, errlen, "%s", msg); } } int autovar_substitute(const char* line, const autovar_ctx_t* ctx, char* out, size_t outlen, char* errbuf, size_t errlen) { const char* p = line; const char* first; size_t o = 0; if ((errbuf != NULL) && (errlen > 0)) { errbuf[0] = '\0'; } if ((line == NULL) || (ctx == NULL) || (out == NULL) || (outlen == 0)) { return -EINVAL; } /* `!` shell-escape and `#` comment lines are left untouched. */ first = line; while ((*first == ' ') || (*first == '\t')) { first++; } if ((*first == '!') || (*first == '#')) { size_t len = strlen(line); if (len >= outlen) { return -ENOSPC; } memcpy(out, line, len + 1); return 0; } 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; const char* stop; /* where the replaced text ends */ 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); /* `$jobid[...]` (Req 4.10): only `$jobid` is replaced; the rest * of the token, from the `[`, is copied unchanged. */ int jobid_index = (namelen > 5) && (strncmp(name, "jobid[", 6) == 0); stop = end; if ((namelen == 1) && (name[0] == '?')) { snprintf(numbuf, sizeof(numbuf), "%d", ctx->last_status); repl = numbuf; } else if (jobid_index || ((namelen == 5) && (strncmp(name, "jobid", 5) == 0))) { /* Req 4.11: an unset $jobid fails `$jobid[...]` the same * way as `$jobid`. */ if ((ctx->jobid == NULL) || (ctx->jobid[0] == '\0')) { set_err(errbuf, errlen, "autovar: $jobid referenced before set"); return -EINVAL; } repl = ctx->jobid; stop = name + 5; } else if ((namelen == 8) && (strncmp(name, "jobcount", 8) == 0)) { if (ctx->jobcount < 0) { set_err(errbuf, errlen, "autovar: $jobcount referenced before set"); return -EINVAL; } snprintf(numbuf, sizeof(numbuf), "%d", ctx->jobcount); repl = numbuf; } if (repl != NULL) { size_t rlen = strlen(repl); if ((o + rlen) >= outlen) { return -ENOSPC; } memcpy(out + o, repl, rlen); o += rlen; p = stop; /* consume `$name`; a `[...]` rest is copied */ continue; } /* Not a recognized auto-var: copy the whole token verbatim. */ while (p < end) { if ((o + 1) >= outlen) { return -ENOSPC; } out[o++] = *p++; } continue; } if ((o + 1) >= outlen) { return -ENOSPC; } out[o++] = *p++; } out[o] = '\0'; return 0; }