#include "sh_emit.h" #include "error.h" #include #include #include /* * src/gen/sh_emit.c — POSIX-sh text emitter (todo 15). * * The quoting rules implemented here are the single-quote rules of the * POSIX Shell Command Language (XBD 2.2.2 "Single-Quotes"): everything * between two unescaped `'` characters is literal, and the ONLY way to * embed a `'` is to close the quoted region, escape the quote with a * backslash, and reopen it — the four-character sequence `'\''`. */ static struct st_error * quote_error(const char *message) { return st_error_internal(message); } char * st_sh_quote_n(const char *value, size_t len, struct st_error **err) { size_t i; size_t out_len; char *out; if (err != NULL) { *err = NULL; } if (value == NULL) { if (err != NULL) { *err = quote_error("st_sh_quote: value is NULL"); } return NULL; } for (i = 0; i < len; i++) { if (value[i] == '\0') { if (err != NULL) { *err = quote_error("st_sh_quote: value contains a NUL byte"); } return NULL; } } /* Worst case: every byte is a `'` and expands to 4. Guard the * size computation against hostile (huge) lengths. */ if (len > (SIZE_MAX - 3) / 4) { if (err != NULL) { *err = quote_error("st_sh_quote: value too large to quote"); } return NULL; } out_len = 1; /* opening quote */ for (i = 0; i < len; i++) { out_len += (value[i] == '\'') ? 4 : 1; } out_len += 1; /* closing quote */ out = malloc(out_len + 1); if (out == NULL) { if (err != NULL) { *err = quote_error("st_sh_quote: out of memory"); } return NULL; } out_len = 0; out[out_len++] = '\''; for (i = 0; i < len; i++) { if (value[i] == '\'') { memcpy(out + out_len, "'\\''", 4); out_len += 4; } else { out[out_len++] = value[i]; } } out[out_len++] = '\''; out[out_len] = '\0'; return out; } char * st_sh_quote_ex(const char *value, struct st_error **err) { if (value == NULL) { if (err != NULL) { *err = quote_error("st_sh_quote: value is NULL"); } return NULL; } return st_sh_quote_n(value, strlen(value), err); } char * st_sh_quote(const char *value) { return st_sh_quote_ex(value, NULL); } int st_sh_emit_str(FILE *out, const char *s) { if (out == NULL || s == NULL) { return -1; } return fputs(s, out) == EOF ? -1 : 0; } int st_sh_emit_comment(FILE *out, const char *text) { const unsigned char *p; if (out == NULL || text == NULL) { return -1; } if (fputs("# ", out) == EOF) { return -1; } for (p = (const unsigned char *)text; *p != '\0'; p++) { if (putc(*p, out) == EOF) { return -1; } if (*p == '\n' && fputs("# ", out) == EOF) { return -1; } } return putc('\n', out) == EOF ? -1 : 0; } int st_sh_emit_assign(FILE *out, const char *var, const char *quoted_value) { if (out == NULL || var == NULL || quoted_value == NULL) { return -1; } if (fputs(var, out) == EOF || putc('=', out) == EOF || fputs(quoted_value, out) == EOF || putc('\n', out) == EOF) { return -1; } return 0; } int st_sh_emit_assign_q(FILE *out, const char *var, const char *value) { char *quoted; int rc; if (out == NULL || var == NULL) { return -1; } quoted = st_sh_quote(value); if (quoted == NULL) { return -1; } rc = st_sh_emit_assign(out, var, quoted); free(quoted); return rc; }