diff options
-rw-r--r-- | ChangeLog | 1 | ||||
-rw-r--r-- | src/bltin/printf.c | 80 | ||||
-rw-r--r-- | src/output.c | 82 | ||||
-rw-r--r-- | src/output.h | 3 |
4 files changed, 119 insertions, 47 deletions
diff --git a/ChangeLog b/ChangeLog index 46ef4c2..379672f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ 2014-10-27 Herbert Xu <herbert@gondor.apana.org.au> * Add printf support for format string a, A, and F. + * Handle embedded NULs correctly in printf. 2014-10-13 Eric Blake <eblake@redhat.com> diff --git a/src/bltin/printf.c b/src/bltin/printf.c index e0ef912..213025f 100644 --- a/src/bltin/printf.c +++ b/src/bltin/printf.c @@ -40,7 +40,7 @@ #include <string.h> #include <unistd.h> -static int conv_escape_str(char *); +static int conv_escape_str(char *, char **); static char *conv_escape(char *, int *); static int getchr(void); static double getdouble(void); @@ -73,6 +73,53 @@ static char **gargv; } \ } +#define ASPF(sp, f, func) ({ \ + int ret; \ + switch ((char *)param - (char *)array) { \ + default: \ + ret = xasprintf(sp, f, array[0], array[1], func); \ + break; \ + case sizeof(*param): \ + ret = xasprintf(sp, f, array[0], func); \ + break; \ + case 0: \ + ret = xasprintf(sp, f, func); \ + break; \ + } \ + ret; \ +}) + + +static int print_escape_str(const char *f, int *param, int *array, char *s) +{ + struct stackmark smark; + char *p, *q; + int done; + int len; + int total; + + setstackmark(&smark); + done = conv_escape_str(s, &p); + q = stackblock(); + len = p - q; + + p = makestrspace(len, p); + memset(p, 'X', len - 1); + p[len - 1] = 0; + + q = stackblock(); + total = ASPF(&p, f, p); + + len = strchrnul(p, 'X') - p; + memcpy(p + len, q, strchrnul(p + len, ' ') - (p + len)); + + out1mem(p, total); + + popstackmark(&smark); + return done; +} + + int printfcmd(int argc, char *argv[]) { char *fmt; @@ -154,17 +201,14 @@ pc: fmt[1] = 0; switch (ch) { - case 'b': { - int done = conv_escape_str(getstr()); - char *p = stackblock(); + case 'b': *fmt = 's'; - PF(start, p); /* escape if a \c was encountered */ - if (done) + if (print_escape_str(start, param, array, + getstr())) goto out; *fmt = 'b'; break; - } case 'c': { int p = getchr(); PF(start, p); @@ -223,8 +267,9 @@ err: * Halts processing string if a \c escape is encountered. */ static int -conv_escape_str(char *str) +conv_escape_str(char *str, char **sp) { + int c; int ch; char *cp; @@ -232,16 +277,14 @@ conv_escape_str(char *str) STARTSTACKSTR(cp); do { - int c; - - ch = *str++; + c = ch = *str++; if (ch != '\\') continue; ch = *str++; if (ch == 'c') { /* \c as in SYSV echo - abort all processing.... */ - ch = 0x100; + c = ch = 0x100; continue; } @@ -253,14 +296,14 @@ conv_escape_str(char *str) if (ch == '0') { unsigned char i; i = 3; - ch = 0; + c = 0; do { unsigned k = octtobin(*str); if (k > 7) break; str++; - ch <<= 3; - ch += k; + c <<= 3; + c += k; } while (--i); continue; } @@ -268,7 +311,9 @@ conv_escape_str(char *str) /* Finally test for sequences valid in the format string */ str = conv_escape(str - 1, &c); ch = c; - } while (STPUTC(ch, cp), (char)ch); + } while (STPUTC(c, cp), (char)ch); + + *sp = cp; return ch; } @@ -456,8 +501,7 @@ echocmd(int argc, char **argv) do { int c; - nonl += conv_escape_str(*argv); - outstr(stackblock(), outs); + nonl += print_escape_str("%s", NULL, NULL, *argv); if (nonl > 0) break; diff --git a/src/output.c b/src/output.c index f62e7ea..1b20850 100644 --- a/src/output.c +++ b/src/output.c @@ -99,9 +99,6 @@ struct output *out1 = &output; struct output *out2 = &errout; -#ifndef USE_GLIBC_STDIO -static void __outstr(const char *, size_t, struct output *); -#endif static int xvsnprintf(char *, size_t, const char *, va_list); @@ -134,10 +131,14 @@ RESET { #endif -#ifndef USE_GLIBC_STDIO -static void -__outstr(const char *p, size_t len, struct output *dest) +void +outmem(const char *p, size_t len, struct output *dest) { +#ifdef USE_GLIBC_STDIO + INTOFF; + fwrite(p, 1, len, dest->stream); + INTON; +#else size_t bufsize; size_t offset; size_t nleft; @@ -186,8 +187,8 @@ alloc: err: dest->flags |= OUTPUT_ERR; } -} #endif +} void @@ -201,7 +202,7 @@ outstr(const char *p, struct output *file) size_t len; len = strlen(p); - __outstr(p, len, file); + outmem(p, len, file); #endif } @@ -213,7 +214,7 @@ void outcslow(int c, struct output *dest) { char buf = c; - __outstr(&buf, 1, dest); + outmem(&buf, 1, dest); } #endif @@ -283,35 +284,58 @@ fmtstr(char *outbuf, size_t length, const char *fmt, ...) } +static int xvasprintf(char **sp, size_t size, const char *f, va_list ap) +{ + char *s; + int len; + va_list ap2; + + va_copy(ap2, ap); + len = xvsnprintf(*sp, size, f, ap2); + va_end(ap2); + if (len < 0) + sh_error("xvsnprintf failed"); + if (len < size) + return len; + + s = stalloc((len >= stackblocksize() ? len : stackblocksize()) + 1); + *sp = s; + len = xvsnprintf(s, len + 1, f, ap); + return len; +} + + +int xasprintf(char **sp, const char *f, ...) +{ + va_list ap; + int ret; + + va_start(ap, f); + ret = xvasprintf(sp, 0, f, ap); + va_end(ap); + return ret; +} + + #ifndef USE_GLIBC_STDIO void doformat(struct output *dest, const char *f, va_list ap) { struct stackmark smark; char *s; - int len, ret; - size_t size; - va_list ap2; + int len; + int olen; - va_copy(ap2, ap); - size = dest->end - dest->nextc; - len = xvsnprintf(dest->nextc, size, f, ap2); - va_end(ap2); - if (len < 0) { - dest->flags |= OUTPUT_ERR; - return; - } - if (len < size) { + setstackmark(&smark); + s = dest->nextc; + olen = dest->end - dest->nextc; + len = xvasprintf(&s, olen, f, ap); + if (likely(olen > len)) { dest->nextc += len; - return; + goto out; } - setstackmark(&smark); - s = stalloc((len >= stackblocksize() ? len : stackblocksize()) + 1); - ret = xvsnprintf(s, len + 1, f, ap); - if (ret == len) - __outstr(s, len, dest); - else - dest->flags |= OUTPUT_ERR; + outmem(s, len, dest); +out: popstackmark(&smark); } #endif diff --git a/src/output.h b/src/output.h index f853e9d..c43d493 100644 --- a/src/output.h +++ b/src/output.h @@ -63,6 +63,7 @@ extern struct output memout; extern struct output *out1; extern struct output *out2; +void outmem(const char *, size_t, struct output *); void outstr(const char *, struct output *); #ifndef USE_GLIBC_STDIO void outcslow(int, struct output *); @@ -75,6 +76,7 @@ void out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2))); int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4))); +int xasprintf(char **, const char *, ...); #ifndef USE_GLIBC_STDIO void doformat(struct output *, const char *, va_list); #endif @@ -115,6 +117,7 @@ static inline void outc(int ch, struct output *file) #endif #define out1c(c) outc((c), out1) #define out2c(c) outcslow((c), out2) +#define out1mem(s, l) outmem((s), (l), out1) #define out1str(s) outstr((s), out1) #define out2str(s) outstr((s), out2) #define outerr(f) (f)->flags |