Files
stupidtools/src/gen/sh_emit.c
T

163 lines
3.7 KiB
C

#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;
}