From 00c7638bc6791b810069381b8aaebfeac67cdfee Mon Sep 17 00:00:00 2001 Message-Id: <00c7638bc6791b810069381b8aaebfeac67cdfee.1714954671.git.mdw@distorted.org.uk> From: Mark Wooding Date: Tue, 15 Dec 1998 23:53:23 +0000 Subject: [PATCH] New functions `dstr_putf' and `dstr_vputf' which do `printf'-style formatting in a safe way. Organization: Straylight/Edgeware From: mdw --- dstr.c | 258 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- dstr.h | 41 ++++++++- 2 files changed, 293 insertions(+), 6 deletions(-) diff --git a/dstr.c b/dstr.c index 7299bf6..8a80920 100644 --- a/dstr.c +++ b/dstr.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: dstr.c,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * $Id: dstr.c,v 1.2 1998/12/15 23:53:22 mdw Exp $ * * Handle dynamically growing strings * @@ -29,13 +29,21 @@ /*----- Revision history --------------------------------------------------* * * $Log: dstr.c,v $ - * Revision 1.1 1998/06/17 23:44:42 mdw - * Initial revision + * Revision 1.2 1998/12/15 23:53:22 mdw + * New functions `dstr_putf' and `dstr_vputf' which do `printf'-style + * formatting in a safe way. + * + * Revision 1.1.1.1 1998/06/17 23:44:42 mdw + * Initial version of mLib * */ /*----- Header files ------------------------------------------------------*/ +#include +#include +#include +#include #include #include #include @@ -46,6 +54,8 @@ /*----- Tunable constants -------------------------------------------------*/ #define DSTR_INITSZ 256 /* Initial buffer size */ +#define DSTR_INCSZ 4096 /* Threshhold for doubling */ +#define DSTR_PUTFSTEP 64 /* Buffer size for @putf@ */ /*----- Main code ---------------------------------------------------------*/ @@ -189,6 +199,248 @@ void dstr_puts(dstr *d, const char *s) DPUTS(d, s); } +/* --- @dstr_vputf@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * @const char *p@ = pointer to @printf@-style format string + * @va_list ap@ = argument handle + * + * Returns: --- + * + * Use: As for @dstr_putf@, but may be used as a back-end to user- + * supplied functions with @printf@-style interfaces. + */ + +int dstr_vputf(dstr *d, const char *p, va_list ap) +{ + const char *q = p; + size_t n = d->len; + size_t sz; + + while (*p) { + unsigned f; + int wd, prec; + dstr dd; + + enum { + f_short = 1, + f_long = 2, + f_Long = 4, + f_wd = 8, + f_prec = 16 + }; + + /* --- Most stuff gets passed on through --- */ + + if (*p != '%') { + p++; + continue; + } + + /* --- Dump out what's between @q@ and @p@ --- */ + + DPUTM(d, q, p - q); + p++; + + /* --- Sort out the various silly flags and things --- */ + + dstr_create(&dd); + DPUTC(&dd, '%'); + f = 0; + sz = DSTR_PUTFSTEP; + + for (;;) { + switch (*p) { + + /* --- Various simple flags --- */ + + case '+': + case '-': + case '#': + case '0': + goto putch; + case 'h': + f |= f_short; + goto putch; + case 'l': + f |= f_long; + goto putch; + case 'L': + f |= f_Long; + goto putch; + case 0: + goto finished; + + /* --- Field widths and precision specifiers --- */ + + { + int *ip; + + case '.': + DPUTC(&dd, '.'); + ip = ≺ + f |= f_prec; + goto getnum; + case '*': + ip = &wd; + f |= f_wd; + goto getnum; + default: + if (isdigit((unsigned char)*p)) { + f |= f_prec; + ip = &wd; + goto getnum; + } + DPUTC(d, *p); + goto formatted; + getnum: + *ip = 0; + if (*p == '*') { + *ip = va_arg(ap, int); + DENSURE(&dd, DSTR_PUTFSTEP); + dd.len += sprintf(dd.buf + dd.len, "%i", *ip); + } else { + *ip = *p + '0'; + DPUTC(&dd, *p); + p++; + while (isdigit((unsigned char)*p)) { + DPUTC(&dd, *p); + *ip = 10 * *ip + *p++ + '0'; + } + } + break; + } + + /* --- Output formatting --- */ + + case 'd': case 'i': case 'x': case 'X': case 'o': case 'u': + DPUTC(&dd, *p); + DPUTZ(&dd); + if ((f & f_prec) && prec + 16 > sz) + sz = prec + 16; + if ((f & f_wd) && wd + 1> sz) + sz = wd + 1; + DENSURE(d, sz); + if (f & f_long) + d->len += sprintf(d->buf + d->len, dd.buf, + va_arg(ap, unsigned long)); + else + d->len += sprintf(d->buf + d->len, dd.buf, + va_arg(ap, unsigned int)); + goto formatted; + + case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': + DPUTC(&dd, *p); + DPUTZ(&dd); + if (*p == 'f') { + size_t mx = (f & f_Long ? LDBL_MAX_10_EXP : DBL_MAX_10_EXP) + 16; + if (mx > sz) + sz = mx; + } + if ((f & f_prec) == 0) + prec = 6; + if ((f & f_prec)) + sz += prec + 16; + if ((f & f_wd) && wd + 1 > sz) + sz = wd + 1; + DENSURE(d, sz); + if (f & f_Long) + d->len += sprintf(d->buf + d->len, dd.buf, + va_arg(ap, long double)); + else + d->len += sprintf(d->buf + d->len, dd.buf, + va_arg(ap, double)); + goto formatted; + + case 'c': + DPUTC(&dd, *p); + DPUTZ(&dd); + if ((f & f_wd) && wd + 1> sz) + sz = wd + 1; + DENSURE(d, sz); + d->len += sprintf(d->buf + d->len, dd.buf, + va_arg(ap, unsigned char)); + goto formatted; + + case 's': { + const char *s = va_arg(ap, const char *); + sz = strlen(s); + DPUTC(&dd, *p); + DPUTZ(&dd); + if (f & f_prec) + sz = prec; + if ((f & f_wd) && wd > sz) + sz = wd; + DENSURE(d, sz + 1); + d->len += sprintf(d->buf + d->len, dd.buf, s); + goto formatted; + } + + case 'p': + DPUTC(&dd, *p); + DPUTZ(&dd); + if ((f & f_prec) && prec + 16 > sz) + sz = prec + 16; + if ((f & f_wd) && wd + 1> sz) + sz = wd + 1; + DENSURE(d, sz); + d->len += sprintf(d->buf + d->len, dd.buf, + va_arg(ap, const void *)); + goto formatted; + + case 'n': + if (f & f_long) + *va_arg(ap, long *) = (long)(d->len - n); + else if (f & f_short) + *va_arg(ap, short *) = (short)(d->len - n); + else + *va_arg(ap, int *) = (int)(d->len - n); + goto formatted; + + /* --- Other random stuff --- */ + + putch: + DPUTC(&dd, *p); + p++; + break; + } + } + + formatted: + dstr_destroy(&dd); + q = ++p; + } + + DPUTM(d, q, p - q); +finished: + DPUTZ(d); + return (d->len - n); +} + +/* --- @dstr_putf@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * @const char *p@ = pointer to @printf@-style format string + * @...@ = argument handle + * + * Returns: --- + * + * Use: Writes a piece of text to a dynamic string, doing @printf@- + * style substitutions as it goes. Intended to be robust if + * faced with malicious arguments, but not if the format string + * itself is malicious. + */ + +int dstr_putf(dstr *d, const char *p, ...) +{ + int n; + va_list ap; + va_start(ap, p); + n = dstr_vputf(d, p, ap); + va_end(ap); + return (n); +} + /* --- @dstr_putd@ --- * * * Arguments: @dstr *d@ = pointer to a dynamic string block diff --git a/dstr.h b/dstr.h index b7240fe..73b1ceb 100644 --- a/dstr.h +++ b/dstr.h @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: dstr.h,v 1.1 1998/06/17 23:44:42 mdw Exp $ + * $Id: dstr.h,v 1.2 1998/12/15 23:53:23 mdw Exp $ * * Handle dynamically growing strings * @@ -29,8 +29,12 @@ /*----- Revision history --------------------------------------------------* * * $Log: dstr.h,v $ - * Revision 1.1 1998/06/17 23:44:42 mdw - * Initial revision + * Revision 1.2 1998/12/15 23:53:23 mdw + * New functions `dstr_putf' and `dstr_vputf' which do `printf'-style + * formatting in a safe way. + * + * Revision 1.1.1.1 1998/06/17 23:44:42 mdw + * Initial version of mLib * */ @@ -53,6 +57,7 @@ /*----- Header files ------------------------------------------------------*/ +#include #include /*----- Data structures ---------------------------------------------------*/ @@ -170,6 +175,36 @@ extern void dstr_puts(dstr */*d*/, const char */*s*/); (d)->len += sz; \ } while (0) +/* --- @dstr_vputf@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * @const char *p@ = pointer to @printf@-style format string + * @va_list ap@ = argument handle + * + * Returns: --- + * + * Use: As for @dstr_putf@, but may be used as a back-end to user- + * supplied functions with @printf@-style interfaces. + */ + +extern int dstr_vputf(dstr */*d*/, const char */*p*/, va_list /*ap*/); + +/* --- @dstr_putf@ --- * + * + * Arguments: @dstr *d@ = pointer to a dynamic string block + * @const char *p@ = pointer to @printf@-style format string + * @...@ = argument handle + * + * Returns: --- + * + * Use: Writes a piece of text to a dynamic string, doing @printf@- + * style substitutions as it goes. Intended to be robust if + * faced with malicious arguments, but not if the format string + * itself is malicious. + */ + +extern int dstr_putf(dstr */*d*/, const char */*p*/, ...); + /* --- @dstr_putd@ --- * * * Arguments: @dstr *d@ = pointer to a dynamic string block -- [mdw]