Template
163 lines
3.7 KiB
C
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;
|
|
}
|