Template
feat(gen): add POSIX-sh emitter with quoting
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
#include "sh_emit.h"
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
Reference in New Issue
Block a user