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

Split Close
Expand all
Collapse all
          --- old/usr/src/cmd/printf/printf.c
          +++ new/usr/src/cmd/printf/printf.c
   1    1  /*
        2 + * Copyright 2014 Garrett D'Amore <garrett@damore.org>
   2    3   * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
   3    4   * Copyright (c) 1989, 1993
   4    5   *      The Regents of the University of California.  All rights reserved.
   5    6   *
   6    7   * Redistribution and use in source and binary forms, with or without
   7    8   * modification, are permitted provided that the following conditions
   8    9   * are met:
   9   10   * 1. Redistributions of source code must retain the above copyright
  10   11   *    notice, this list of conditions and the following disclaimer.
  11   12   * 2. Redistributions in binary form must reproduce the above copyright
↓ open down ↓ 19 lines elided ↑ open up ↑
  31   32  #include <sys/types.h>
  32   33  
  33   34  #include <err.h>
  34   35  #include <errno.h>
  35   36  #include <inttypes.h>
  36   37  #include <limits.h>
  37   38  #include <stdio.h>
  38   39  #include <stdlib.h>
  39   40  #include <string.h>
  40   41  #include <unistd.h>
       42 +#include <alloca.h>
       43 +#include <ctype.h>
  41   44  #include <locale.h>
  42   45  #include <note.h>
  43   46  
  44   47  #define warnx1(a, b, c)         warnx(a)
  45   48  #define warnx2(a, b, c)         warnx(a, b)
  46   49  #define warnx3(a, b, c)         warnx(a, b, c)
  47   50  
  48   51  #define PTRDIFF(x, y)   ((uintptr_t)(x) - (uintptr_t)(y))
  49   52  
  50   53  #define _(x)    gettext(x)
  51   54  
  52   55  #define PF(f, func) do {                                                \
  53   56          char *b = NULL;                                                 \
  54      -        int dollar = 0;                                                 \
  55      -        if (*f == '$')  {                                               \
  56      -                dollar++;                                               \
  57      -                *f = '%';                                               \
  58      -        }                                                               \
  59   57          if (havewidth)                                                  \
  60   58                  if (haveprec)                                           \
  61   59                          (void) asprintf(&b, f, fieldwidth, precision, func); \
  62   60                  else                                                    \
  63   61                          (void) asprintf(&b, f, fieldwidth, func);       \
  64   62          else if (haveprec)                                              \
  65   63                  (void) asprintf(&b, f, precision, func);                \
  66   64          else                                                            \
  67   65                  (void) asprintf(&b, f, func);                           \
  68   66          if (b) {                                                        \
  69   67                  (void) fputs(b, stdout);                                \
  70   68                  free(b);                                                \
  71   69          }                                                               \
  72      -        if (dollar)                                                     \
  73      -                *f = '$';                                               \
  74   70  _NOTE(CONSTCOND) } while (0)
  75   71  
  76   72  static int       asciicode(void);
  77   73  static char     *doformat(char *, int *);
  78   74  static int       escape(char *, int, size_t *);
  79   75  static int       getchr(void);
  80   76  static int       getfloating(long double *, int);
  81   77  static int       getint(int *);
  82   78  static int       getnum(intmax_t *, uintmax_t *, int);
  83   79  static const char
  84   80                  *getstr(void);
  85   81  static char     *mknum(char *, char);
  86   82  static void      usage(void);
  87   83  
       84 +static const char digits[] = "0123456789";
       85 +
  88   86  static int  myargc;
  89   87  static char **myargv;
  90   88  static char **gargv;
       89 +static char **maxargv;
  91   90  
  92   91  int
  93   92  main(int argc, char *argv[])
  94   93  {
  95   94          size_t len;
  96      -        int chopped, end, rval;
       95 +        int end, rval;
  97   96          char *format, *fmt, *start;
  98   97  
  99   98          (void) setlocale(LC_ALL, "");
 100   99  
 101  100          argv++;
 102  101          argc--;
 103  102  
 104  103          /*
 105  104           * POSIX says: Standard utilities that do not accept options,
 106  105           * but that do accept operands, shall recognize "--" as a
↓ open down ↓ 11 lines elided ↑ open up ↑
 118  117  
 119  118          /*
 120  119           * Basic algorithm is to scan the format string for conversion
 121  120           * specifications -- once one is found, find out if the field
 122  121           * width or precision is a '*'; if it is, gather up value.  Note,
 123  122           * format strings are reused as necessary to use up the provided
 124  123           * arguments, arguments of zero/null string are provided to use
 125  124           * up the format string.
 126  125           */
 127  126          fmt = format = *argv;
 128      -        chopped = escape(fmt, 1, &len);         /* backslash interpretation */
      127 +        (void) escape(fmt, 1, &len);    /* backslash interpretation */
 129  128          rval = end = 0;
 130  129          gargv = ++argv;
 131  130  
 132  131          for (;;) {
 133      -                char **maxargv = gargv;
      132 +                maxargv = gargv;
 134  133  
 135  134                  myargv = gargv;
 136  135                  for (myargc = 0; gargv[myargc]; myargc++)
 137  136                          /* nop */;
 138  137                  start = fmt;
 139  138                  while (fmt < format + len) {
 140  139                          if (fmt[0] == '%') {
 141  140                                  (void) fwrite(start, 1, PTRDIFF(fmt, start),
 142  141                                      stdout);
 143  142                                  if (fmt[1] == '%') {
↓ open down ↓ 12 lines elided ↑ open up ↑
 156  155                          if (gargv > maxargv)
 157  156                                  maxargv = gargv;
 158  157                  }
 159  158                  gargv = maxargv;
 160  159  
 161  160                  if (end == 1) {
 162  161                          warnx1(_("missing format character"), NULL, NULL);
 163  162                          return (1);
 164  163                  }
 165  164                  (void) fwrite(start, 1, PTRDIFF(fmt, start), stdout);
 166      -                if (chopped || !*gargv)
      165 +                if (!*gargv)
 167  166                          return (rval);
 168  167                  /* Restart at the beginning of the format string. */
 169  168                  fmt = format;
 170  169                  end = 1;
 171  170          }
 172  171          /* NOTREACHED */
 173  172  }
 174  173  
 175  174  
 176  175  static char *
 177      -doformat(char *start, int *rval)
      176 +doformat(char *fmt, int *rval)
 178  177  {
 179  178          static const char skip1[] = "#'-+ 0";
 180      -        static const char skip2[] = "0123456789";
 181      -        char *fmt;
 182  179          int fieldwidth, haveprec, havewidth, mod_ldbl, precision;
 183  180          char convch, nextch;
      181 +        char *start;
      182 +        char **fargv;
      183 +        char *dptr;
      184 +        int l;
 184  185  
 185      -        fmt = start + 1;
      186 +        start = alloca(strlen(fmt) + 1);
 186  187  
      188 +        dptr = start;
      189 +        *dptr++ = '%';
      190 +        *dptr = 0;
      191 +
      192 +        fmt++;
      193 +
 187  194          /* look for "n$" field index specifier */
 188      -        fmt += strspn(fmt, skip2);
 189      -        if ((*fmt == '$') && (fmt != (start + 1))) {
 190      -                int idx = atoi(start + 1);
      195 +        l = strspn(fmt, digits);
      196 +        if ((l > 0) && (fmt[l] == '$')) {
      197 +                int idx = atoi(fmt);
 191  198                  if (idx <= myargc) {
 192  199                          gargv = &myargv[idx - 1];
 193  200                  } else {
 194  201                          gargv = &myargv[myargc];
 195  202                  }
 196      -                start = fmt;
 197      -                fmt++;
      203 +                if (gargv > maxargv) {
      204 +                        maxargv = gargv;
      205 +                }
      206 +                fmt += l + 1;
      207 +
      208 +                /* save format argument */
      209 +                fargv = gargv;
 198  210          } else {
 199      -                fmt = start + 1;
      211 +                fargv = NULL;
 200  212          }
 201  213  
 202  214          /* skip to field width */
 203      -        fmt += strspn(fmt, skip1);
      215 +        while (*fmt && strchr(skip1, *fmt) != NULL) {
      216 +                *dptr++ = *fmt++;
      217 +                *dptr = 0;
      218 +        }
      219 +
      220 +
 204  221          if (*fmt == '*') {
      222 +
      223 +                fmt++;
      224 +                l = strspn(fmt, digits);
      225 +                if ((l > 0) && (fmt[l] == '$')) {
      226 +                        int idx = atoi(fmt);
      227 +                        if (fargv == NULL) {
      228 +                                warnx1(_("incomplete use of n$"), NULL, NULL);
      229 +                                return (NULL);
      230 +                        }
      231 +                        if (idx <= myargc) {
      232 +                                gargv = &myargv[idx - 1];
      233 +                        } else {
      234 +                                gargv = &myargv[myargc];
      235 +                        }
      236 +                        fmt += l + 1;
      237 +                } else if (fargv != NULL) {
      238 +                        warnx1(_("incomplete use of n$"), NULL, NULL);
      239 +                        return (NULL);
      240 +                }
      241 +
 205  242                  if (getint(&fieldwidth))
 206  243                          return (NULL);
      244 +                if (gargv > maxargv) {
      245 +                        maxargv = gargv;
      246 +                }
 207  247                  havewidth = 1;
 208      -                ++fmt;
      248 +
      249 +                *dptr++ = '*';
      250 +                *dptr = 0;
 209  251          } else {
 210  252                  havewidth = 0;
 211  253  
 212  254                  /* skip to possible '.', get following precision */
 213      -                fmt += strspn(fmt, skip2);
      255 +                while (isdigit(*fmt)) {
      256 +                        *dptr++ = *fmt++;
      257 +                        *dptr = 0;
      258 +                }
 214  259          }
      260 +
 215  261          if (*fmt == '.') {
 216  262                  /* precision present? */
 217      -                ++fmt;
      263 +                fmt++;
      264 +                *dptr++ = '.';
      265 +
 218  266                  if (*fmt == '*') {
      267 +
      268 +                        fmt++;
      269 +                        l = strspn(fmt, digits);
      270 +                        if ((l > 0) && (fmt[l] == '$')) {
      271 +                                int idx = atoi(fmt);
      272 +                                if (fargv == NULL) {
      273 +                                        warnx1(_("incomplete use of n$"),
      274 +                                            NULL, NULL);
      275 +                                        return (NULL);
      276 +                                }
      277 +                                if (idx <= myargc) {
      278 +                                        gargv = &myargv[idx - 1];
      279 +                                } else {
      280 +                                        gargv = &myargv[myargc];
      281 +                                }
      282 +                                fmt += l + 1;
      283 +                        } else if (fargv != NULL) {
      284 +                                warnx1(_("incomplete use of n$"), NULL, NULL);
      285 +                                return (NULL);
      286 +                        }
      287 +
 219  288                          if (getint(&precision))
 220  289                                  return (NULL);
      290 +                        if (gargv > maxargv) {
      291 +                                maxargv = gargv;
      292 +                        }
 221  293                          haveprec = 1;
 222      -                        ++fmt;
      294 +                        *dptr++ = '*';
      295 +                        *dptr = 0;
 223  296                  } else {
 224  297                          haveprec = 0;
 225  298  
 226  299                          /* skip to conversion char */
 227      -                        fmt += strspn(fmt, skip2);
      300 +                        while (isdigit(*fmt)) {
      301 +                                *dptr++ = *fmt++;
      302 +                                *dptr = 0;
      303 +                        }
 228  304                  }
 229  305          } else
 230  306                  haveprec = 0;
 231  307          if (!*fmt) {
 232  308                  warnx1(_("missing format character"), NULL, NULL);
 233  309                  return (NULL);
 234  310          }
      311 +        *dptr++ = *fmt;
      312 +        *dptr = 0;
 235  313  
 236  314          /*
 237  315           * Look for a length modifier.  POSIX doesn't have these, so
 238  316           * we only support them for floating-point conversions, which
 239  317           * are extensions.  This is useful because the L modifier can
 240  318           * be used to gain extra range and precision, while omitting
 241  319           * it is more likely to produce consistent results on different
 242  320           * architectures.  This is not so important for integers
 243  321           * because overflow is the only bad thing that can happen to
 244  322           * them, but consider the command  printf %a 1.1
↓ open down ↓ 2 lines elided ↑ open up ↑
 247  325                  mod_ldbl = 1;
 248  326                  fmt++;
 249  327                  if (!strchr("aAeEfFgG", *fmt)) {
 250  328                          warnx2(_("bad modifier L for %%%c"), *fmt, NULL);
 251  329                          return (NULL);
 252  330                  }
 253  331          } else {
 254  332                  mod_ldbl = 0;
 255  333          }
 256  334  
      335 +        /* save the current arg offset, and set to the format arg */
      336 +        if (fargv != NULL) {
      337 +                gargv = fargv;
      338 +        }
      339 +
 257  340          convch = *fmt;
 258  341          nextch = *++fmt;
      342 +
 259  343          *fmt = '\0';
 260  344          switch (convch) {
 261  345          case 'b': {
 262  346                  size_t len;
 263  347                  char *p;
 264  348                  int getout;
 265  349  
 266  350                  p = strdup(getstr());
 267  351                  if (p == NULL) {
 268  352                          warnx2("%s", strerror(ENOMEM), NULL);
 269  353                          return (NULL);
 270  354                  }
 271  355                  getout = escape(p, 0, &len);
 272      -                *(fmt - 1) = 's';
 273      -                PF(start, p);
 274      -                *(fmt - 1) = 'b';
      356 +                (void) fputs(p, stdout);
 275  357                  free(p);
 276  358  
 277  359                  if (getout)
 278      -                        return (fmt);
      360 +                        exit(*rval);
 279  361                  break;
 280  362          }
 281  363          case 'c': {
 282  364                  char p;
 283  365  
 284  366                  p = getchr();
 285  367                  PF(start, p);
 286  368                  break;
 287  369          }
 288  370          case 's': {
↓ open down ↓ 32 lines elided ↑ open up ↑
 321  403                          PF(start, p);
 322  404                  else
 323  405                          PF(start, (double)p);
 324  406                  break;
 325  407          }
 326  408          default:
 327  409                  warnx2(_("illegal format character %c"), convch, NULL);
 328  410                  return (NULL);
 329  411          }
 330  412          *fmt = nextch;
      413 +
      414 +        /* return the gargv to the next element */
 331  415          return (fmt);
 332  416  }
 333  417  
 334  418  static char *
 335  419  mknum(char *str, char ch)
 336  420  {
 337  421          static char *copy;
 338  422          static size_t copy_size;
 339  423          char *newcopy;
 340  424          size_t len, newlen;
↓ open down ↓ 37 lines elided ↑ open up ↑
 378  462                  case '\'':              /* single quote */
 379  463                          *store = *fmt;
 380  464                          break;
 381  465                  case 'a':               /* bell/alert */
 382  466                          *store = '\a';
 383  467                          break;
 384  468                  case 'b':               /* backspace */
 385  469                          *store = '\b';
 386  470                          break;
 387  471                  case 'c':
 388      -                        *store = '\0';
 389      -                        *len = PTRDIFF(store, save);
 390      -                        return (1);
      472 +                        if (!percent) {
      473 +                                *store = '\0';
      474 +                                *len = PTRDIFF(store, save);
      475 +                                return (1);
      476 +                        }
      477 +                        *store = 'c';
      478 +                        break;
 391  479                  case 'f':               /* form-feed */
 392  480                          *store = '\f';
 393  481                          break;
 394  482                  case 'n':               /* newline */
 395  483                          *store = '\n';
 396  484                          break;
 397  485                  case 'r':               /* carriage-return */
 398  486                          *store = '\r';
 399  487                          break;
 400  488                  case 't':               /* horizontal tab */
↓ open down ↓ 155 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX