Print this page
4818 printf(1) should support n$ width and precision specifiers
4854 printf(1) doesn't support %b and \c properly
Reviewed by: Keith Wesolowski <keith.wesolowski@joyent.com>
Approved by: TBD

*** 1,6 **** --- 1,7 ---- /* + * Copyright 2014 Garrett D'Amore <garrett@damore.org> * Copyright 2010 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without
*** 36,45 **** --- 37,48 ---- #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> + #include <alloca.h> + #include <ctype.h> #include <locale.h> #include <note.h> #define warnx1(a, b, c) warnx(a) #define warnx2(a, b, c) warnx(a, b)
*** 49,63 **** #define _(x) gettext(x) #define PF(f, func) do { \ char *b = NULL; \ - int dollar = 0; \ - if (*f == '$') { \ - dollar++; \ - *f = '%'; \ - } \ if (havewidth) \ if (haveprec) \ (void) asprintf(&b, f, fieldwidth, precision, func); \ else \ (void) asprintf(&b, f, fieldwidth, func); \ --- 52,61 ----
*** 67,78 **** (void) asprintf(&b, f, func); \ if (b) { \ (void) fputs(b, stdout); \ free(b); \ } \ - if (dollar) \ - *f = '$'; \ _NOTE(CONSTCOND) } while (0) static int asciicode(void); static char *doformat(char *, int *); static int escape(char *, int, size_t *); --- 65,74 ----
*** 83,101 **** static const char *getstr(void); static char *mknum(char *, char); static void usage(void); static int myargc; static char **myargv; static char **gargv; int main(int argc, char *argv[]) { size_t len; ! int chopped, end, rval; char *format, *fmt, *start; (void) setlocale(LC_ALL, ""); argv++; --- 79,100 ---- static const char *getstr(void); static char *mknum(char *, char); static void usage(void); + static const char digits[] = "0123456789"; + static int myargc; static char **myargv; static char **gargv; + static char **maxargv; int main(int argc, char *argv[]) { size_t len; ! int end, rval; char *format, *fmt, *start; (void) setlocale(LC_ALL, ""); argv++;
*** 123,138 **** * format strings are reused as necessary to use up the provided * arguments, arguments of zero/null string are provided to use * up the format string. */ fmt = format = *argv; ! chopped = escape(fmt, 1, &len); /* backslash interpretation */ rval = end = 0; gargv = ++argv; for (;;) { ! char **maxargv = gargv; myargv = gargv; for (myargc = 0; gargv[myargc]; myargc++) /* nop */; start = fmt; --- 122,137 ---- * format strings are reused as necessary to use up the provided * arguments, arguments of zero/null string are provided to use * up the format string. */ fmt = format = *argv; ! (void) escape(fmt, 1, &len); /* backslash interpretation */ rval = end = 0; gargv = ++argv; for (;;) { ! maxargv = gargv; myargv = gargv; for (myargc = 0; gargv[myargc]; myargc++) /* nop */; start = fmt;
*** 161,171 **** if (end == 1) { warnx1(_("missing format character"), NULL, NULL); return (1); } (void) fwrite(start, 1, PTRDIFF(fmt, start), stdout); ! if (chopped || !*gargv) return (rval); /* Restart at the beginning of the format string. */ fmt = format; end = 1; } --- 160,170 ---- if (end == 1) { warnx1(_("missing format character"), NULL, NULL); return (1); } (void) fwrite(start, 1, PTRDIFF(fmt, start), stdout); ! if (!*gargv) return (rval); /* Restart at the beginning of the format string. */ fmt = format; end = 1; }
*** 172,239 **** /* NOTREACHED */ } static char * ! doformat(char *start, int *rval) { static const char skip1[] = "#'-+ 0"; - static const char skip2[] = "0123456789"; - char *fmt; int fieldwidth, haveprec, havewidth, mod_ldbl, precision; char convch, nextch; ! fmt = start + 1; /* look for "n$" field index specifier */ ! fmt += strspn(fmt, skip2); ! if ((*fmt == '$') && (fmt != (start + 1))) { ! int idx = atoi(start + 1); if (idx <= myargc) { gargv = &myargv[idx - 1]; } else { gargv = &myargv[myargc]; } ! start = fmt; ! fmt++; } else { ! fmt = start + 1; } /* skip to field width */ ! fmt += strspn(fmt, skip1); if (*fmt == '*') { if (getint(&fieldwidth)) return (NULL); havewidth = 1; ! ++fmt; } else { havewidth = 0; /* skip to possible '.', get following precision */ ! fmt += strspn(fmt, skip2); } if (*fmt == '.') { /* precision present? */ ! ++fmt; if (*fmt == '*') { if (getint(&precision)) return (NULL); haveprec = 1; ! ++fmt; } else { haveprec = 0; /* skip to conversion char */ ! fmt += strspn(fmt, skip2); } } else haveprec = 0; if (!*fmt) { warnx1(_("missing format character"), NULL, NULL); return (NULL); } /* * Look for a length modifier. POSIX doesn't have these, so * we only support them for floating-point conversions, which * are extensions. This is useful because the L modifier can --- 171,317 ---- /* NOTREACHED */ } static char * ! doformat(char *fmt, int *rval) { static const char skip1[] = "#'-+ 0"; int fieldwidth, haveprec, havewidth, mod_ldbl, precision; char convch, nextch; + char *start; + char **fargv; + char *dptr; + int l; ! start = alloca(strlen(fmt) + 1); + dptr = start; + *dptr++ = '%'; + *dptr = 0; + + fmt++; + /* look for "n$" field index specifier */ ! l = strspn(fmt, digits); ! if ((l > 0) && (fmt[l] == '$')) { ! int idx = atoi(fmt); if (idx <= myargc) { gargv = &myargv[idx - 1]; } else { gargv = &myargv[myargc]; } ! if (gargv > maxargv) { ! maxargv = gargv; ! } ! fmt += l + 1; ! ! /* save format argument */ ! fargv = gargv; } else { ! fargv = NULL; } /* skip to field width */ ! while (*fmt && strchr(skip1, *fmt) != NULL) { ! *dptr++ = *fmt++; ! *dptr = 0; ! } ! ! if (*fmt == '*') { + + fmt++; + l = strspn(fmt, digits); + if ((l > 0) && (fmt[l] == '$')) { + int idx = atoi(fmt); + if (fargv == NULL) { + warnx1(_("incomplete use of n$"), NULL, NULL); + return (NULL); + } + if (idx <= myargc) { + gargv = &myargv[idx - 1]; + } else { + gargv = &myargv[myargc]; + } + fmt += l + 1; + } else if (fargv != NULL) { + warnx1(_("incomplete use of n$"), NULL, NULL); + return (NULL); + } + if (getint(&fieldwidth)) return (NULL); + if (gargv > maxargv) { + maxargv = gargv; + } havewidth = 1; ! ! *dptr++ = '*'; ! *dptr = 0; } else { havewidth = 0; /* skip to possible '.', get following precision */ ! while (isdigit(*fmt)) { ! *dptr++ = *fmt++; ! *dptr = 0; } + } + if (*fmt == '.') { /* precision present? */ ! fmt++; ! *dptr++ = '.'; ! if (*fmt == '*') { + + fmt++; + l = strspn(fmt, digits); + if ((l > 0) && (fmt[l] == '$')) { + int idx = atoi(fmt); + if (fargv == NULL) { + warnx1(_("incomplete use of n$"), + NULL, NULL); + return (NULL); + } + if (idx <= myargc) { + gargv = &myargv[idx - 1]; + } else { + gargv = &myargv[myargc]; + } + fmt += l + 1; + } else if (fargv != NULL) { + warnx1(_("incomplete use of n$"), NULL, NULL); + return (NULL); + } + if (getint(&precision)) return (NULL); + if (gargv > maxargv) { + maxargv = gargv; + } haveprec = 1; ! *dptr++ = '*'; ! *dptr = 0; } else { haveprec = 0; /* skip to conversion char */ ! while (isdigit(*fmt)) { ! *dptr++ = *fmt++; ! *dptr = 0; } + } } else haveprec = 0; if (!*fmt) { warnx1(_("missing format character"), NULL, NULL); return (NULL); } + *dptr++ = *fmt; + *dptr = 0; /* * Look for a length modifier. POSIX doesn't have these, so * we only support them for floating-point conversions, which * are extensions. This is useful because the L modifier can
*** 252,263 **** --- 330,347 ---- } } else { mod_ldbl = 0; } + /* save the current arg offset, and set to the format arg */ + if (fargv != NULL) { + gargv = fargv; + } + convch = *fmt; nextch = *++fmt; + *fmt = '\0'; switch (convch) { case 'b': { size_t len; char *p;
*** 267,283 **** if (p == NULL) { warnx2("%s", strerror(ENOMEM), NULL); return (NULL); } getout = escape(p, 0, &len); ! *(fmt - 1) = 's'; ! PF(start, p); ! *(fmt - 1) = 'b'; free(p); if (getout) ! return (fmt); break; } case 'c': { char p; --- 351,365 ---- if (p == NULL) { warnx2("%s", strerror(ENOMEM), NULL); return (NULL); } getout = escape(p, 0, &len); ! (void) fputs(p, stdout); free(p); if (getout) ! exit(*rval); break; } case 'c': { char p;
*** 326,335 **** --- 408,419 ---- default: warnx2(_("illegal format character %c"), convch, NULL); return (NULL); } *fmt = nextch; + + /* return the gargv to the next element */ return (fmt); } static char * mknum(char *str, char ch)
*** 383,395 **** --- 467,483 ---- break; case 'b': /* backspace */ *store = '\b'; break; case 'c': + if (!percent) { *store = '\0'; *len = PTRDIFF(store, save); return (1); + } + *store = 'c'; + break; case 'f': /* form-feed */ *store = '\f'; break; case 'n': /* newline */ *store = '\n';