diff options
-rw-r--r-- | src/til_settings.c | 94 |
1 files changed, 54 insertions, 40 deletions
diff --git a/src/til_settings.c b/src/til_settings.c index 4631849..239e359 100644 --- a/src/til_settings.c +++ b/src/til_settings.c @@ -464,62 +464,76 @@ int til_setting_spec_check(const til_setting_spec_t *spec, const char *value) } -/* wrapper around sprintf for convenient buffer size computation */ -/* supply NULL buf when computing size, size and offset are ignored. - * supply non-NULL for actual writing into buf of size bytes @ offset. - * return value is number of bytes (potentially if !buf) written - */ -static int snpf(char *buf, size_t size, off_t offset, const char *format, ...) +static inline void fputc_escaped(FILE *out, int c, unsigned depth) { - size_t avail = 0; - va_list ap; - int r; - - if (buf) { - assert(size > offset); + unsigned escapes = 0; - avail = size - offset; - buf += offset; + for (unsigned i = 0; i < depth; i++) { + escapes <<= 1; + escapes += 1; } - va_start(ap, format); - r = vsnprintf(buf, avail, format, ap); - va_end(ap); + for (unsigned i = 0; i < escapes; i++) + fputc('\\', out); - return r; + fputc(c, out); } -char * til_settings_as_arg(const til_settings_t *settings) +static inline void fputs_escaped(FILE *out, const char *value, unsigned depth) { - char *buf = NULL; - size_t off, size; - - /* intentionally avoided open_memstream for portability reasons */ - for (;;) { - unsigned i; + char c; + + while ((c = *value++)) { + switch (c) { + case '\'': /* this isn't strictly necessary, but let's just make settings-as-arg easily quotable for shell purposes, excessive escaping is otherwise benign */ + case '=': + case ',': + case '\\': + fputc_escaped(out, c, depth); + break; + default: + fputc(c, out); + break; + } + } +} - for (i = off = 0; i < settings->num; i++) { - if (i > 0) - off += snpf(buf, size, off, ","); - off += snpf(buf, size, off, "%s", settings->settings[i]->key); +static void settings_as_arg(const til_settings_t *settings, unsigned depth, FILE *out) +{ + for (size_t i = 0; i < settings->num; i++) { + if (i > 0) + fputc_escaped(out, ',', depth); + if (settings->settings[i]->key) { + fputs_escaped(out, settings->settings[i]->key, depth); if (settings->settings[i]->value) - off += snpf(buf, size, off, "=%s", settings->settings[i]->value); + fputc_escaped(out, '=', depth); } - if (!buf) { - size = off + 1; - buf = calloc(size, sizeof(char)); - if (!buf) - return NULL; - - continue; + if (settings->settings[i]->value_as_nested_settings) { + settings_as_arg(settings->settings[i]->value_as_nested_settings, depth + 1, out); + } else if (settings->settings[i]->value) { + fputs_escaped(out, settings->settings[i]->value, depth); } - - break; } +} - return buf; + +char * til_settings_as_arg(const til_settings_t *settings) +{ + FILE *out; + char *outbuf; + size_t outsize; + + out = open_memstream(&outbuf, &outsize); /* TODO FIXME: open_memstream() isn't portable */ + if (!out) + return NULL; + + settings_as_arg(settings, 0, out); + + fclose(out); + + return outbuf; } |