X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/mLib/blobdiff_plain/e63124bc579bfd97cfe2f620ddd84df9f20477d8..d04c0e00da3a27693bbf9cc4f2d5c88e56d80f20:/utils/gprintf.c diff --git a/utils/gprintf.c b/utils/gprintf.c index f527186..45cd35a 100644 --- a/utils/gprintf.c +++ b/utils/gprintf.c @@ -48,6 +48,7 @@ #include "darray.h" #include "dstr.h" #include "gprintf.h" +#include "growbuf.h" #include "macros.h" /*----- Tunable constants -------------------------------------------------*/ @@ -373,7 +374,7 @@ int vgprintf(const struct gprintf_ops *ops, void *out, break; default: fprintf(stderr, - "FATAL dstr_vputf: unknown format specifier `%c'\n", p[-1]); + "FATAL vgprintf: unknown format specifier `%c'\n", p[-1]); abort(); } @@ -408,17 +409,21 @@ int vgprintf(const struct gprintf_ops *ops, void *out, /* --- Output the literal portion --- */ if (fs->n) { - if (ops->putm(out, fs->p, fs->n)) return (-1); - tot += fs->n; + n = ops->putm(out, fs->p, fs->n); if (n < 0) return (-1); + tot += n; } /* --- And now the variable portion --- */ if (fs->fmt == fmt_unset) { switch (fs->ch) { - case 0: break; - case '%': ops->putch(out, '%'); break; - default: abort(); + case 0: + break; + case '%': + n = ops->putch(out, '%'); if (n < 0) return (-1); + tot += n; break; + default: + abort(); } continue; } @@ -496,8 +501,8 @@ int vgprintf(const struct gprintf_ops *ops, void *out, break; #else # define MSG "" - if (ops->putm(out, MSG, sizeof(MSG) - 1)) return (-1); - continue; + n = ops->putm(out, MSG, sizeof(MSG) - 1); if (n < 0) return (-1); + tot += n; continue; # undef MSG #endif case 's': @@ -522,14 +527,14 @@ int vgprintf(const struct gprintf_ops *ops, void *out, switch (fs->fmt) { #define CASE(code, ty) \ case fmt_##code: \ - i = ops->nputf(out, sz, dd.buf, fa[fs->arg].u.code); \ + n = ops->nputf(out, sz, dd.buf, fa[fs->arg].u.code); \ break; OUTPUT_FMTTYPES(CASE) #undef CASE default: abort(); } - if (i < 0) return (-1); - tot += i; + if (n < 0) return (-1); + tot += n; } /* --- We're done --- */ @@ -563,6 +568,48 @@ int gprintf(const struct gprintf_ops *ops, void *out, const char *p, ...) return (n); } +/*----- Utilities ---------------------------------------------------------*/ + +/* --- @gprintf_memputf@ --- * + * + * Arguments: @arena *a@ = memory allocation arena + * @char **buf_inout@ = address of output buffer pointer + * @size_t *sz_inout@ = address of buffer size + * @size_t maxsz@ = buffer size needed for this operation + * @const char *p@ = pointer to format string + * @va_list *ap@ = captured format-arguments tail + * + * Returns: The formatted length. + * + * Use: Generic utility for mostly implementing the @nputf@ output + * function, if you don't have a better option. + * + * On entry, @*buf_inout@ should be null or a buffer pointer, + * with @*sz_inout@ either zero or the buffer's size, + * respectively. On exit, @*buf_input@ and @*sz_inout@ will be + * updated, if necessary, to describe a sufficiently large + * buffer, and the formatted string will have been written to + * the buffer. + * + * When the buffer is no longer required, free it using + * @x_free@. + */ + +size_t gprintf_memputf(arena *a, char **buf_inout, size_t *sz_inout, + size_t maxsz, const char *p, va_list ap) +{ + int n; + + GROWBUF_REPLACE(size_t, a, *buf_inout, *sz_inout, maxsz, 64, 1); +#ifdef HAVE_SNPRINTF + n = vsnprintf(*buf_inout, maxsz + 1, p, ap); +#else + n = vsprintf(*buf_inout, p, ap); +#endif + assert(0 <= n && n <= maxsz); + return (n); +} + /*----- Standard printers -------------------------------------------------*/ static int file_putch(void *out, int ch)