Print this page
4854 printf(1) doesn't support %b and \c properly
4818 printf(1) should support n$ width and precision specifiers

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 (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 (idx <= myargc) {
      228 +                                gargv = &myargv[idx - 1];
      229 +                        } else {
      230 +                                gargv = &myargv[myargc];
      231 +                        }
      232 +                        fmt += l + 1;
      233 +                }
      234 +
 205  235                  if (getint(&fieldwidth))
 206  236                          return (NULL);
      237 +                if (gargv > maxargv) {
      238 +                        maxargv = gargv;
      239 +                }
 207  240                  havewidth = 1;
 208      -                ++fmt;
      241 +
      242 +                *dptr++ = '*';
      243 +                *dptr = 0;
 209  244          } else {
 210  245                  havewidth = 0;
 211  246  
 212  247                  /* skip to possible '.', get following precision */
 213      -                fmt += strspn(fmt, skip2);
      248 +                while (isdigit(*fmt)) {
      249 +                        *dptr++ = *fmt++;
      250 +                        *dptr = 0;
      251 +                }
 214  252          }
      253 +
 215  254          if (*fmt == '.') {
 216  255                  /* precision present? */
 217      -                ++fmt;
      256 +                fmt++;
      257 +                *dptr++ = '.';
      258 +
 218  259                  if (*fmt == '*') {
      260 +
      261 +                        fmt++;
      262 +                        l = strspn(fmt, digits);
      263 +                        if ((l > 0) && (fmt[l] == '$')) {
      264 +                                int idx = atoi(fmt);
      265 +                                if (idx <= myargc) {
      266 +                                        gargv = &myargv[idx - 1];
      267 +                                } else {
      268 +                                        gargv = &myargv[myargc];
      269 +                                }
      270 +                                fmt += l + 1;
      271 +                        }
      272 +
 219  273                          if (getint(&precision))
 220  274                                  return (NULL);
      275 +                        if (gargv > maxargv) {
      276 +                                maxargv = gargv;
      277 +                        }
 221  278                          haveprec = 1;
 222      -                        ++fmt;
      279 +                        *dptr++ = '*';
      280 +                        *dptr = 0;
 223  281                  } else {
 224  282                          haveprec = 0;
 225  283  
 226  284                          /* skip to conversion char */
 227      -                        fmt += strspn(fmt, skip2);
      285 +                        while (isdigit(*fmt)) {
      286 +                                *dptr++ = *fmt++;
      287 +                                *dptr = 0;
      288 +                        }
 228  289                  }
 229  290          } else
 230  291                  haveprec = 0;
 231  292          if (!*fmt) {
 232  293                  warnx1(_("missing format character"), NULL, NULL);
 233  294                  return (NULL);
 234  295          }
      296 +        *dptr++ = *fmt;
      297 +        *dptr = 0;
 235  298  
 236  299          /*
 237  300           * Look for a length modifier.  POSIX doesn't have these, so
 238  301           * we only support them for floating-point conversions, which
 239  302           * are extensions.  This is useful because the L modifier can
 240  303           * be used to gain extra range and precision, while omitting
 241  304           * it is more likely to produce consistent results on different
 242  305           * architectures.  This is not so important for integers
 243  306           * because overflow is the only bad thing that can happen to
 244  307           * them, but consider the command  printf %a 1.1
↓ open down ↓ 2 lines elided ↑ open up ↑
 247  310                  mod_ldbl = 1;
 248  311                  fmt++;
 249  312                  if (!strchr("aAeEfFgG", *fmt)) {
 250  313                          warnx2(_("bad modifier L for %%%c"), *fmt, NULL);
 251  314                          return (NULL);
 252  315                  }
 253  316          } else {
 254  317                  mod_ldbl = 0;
 255  318          }
 256  319  
      320 +        /* save the current arg offset, and set to the format arg */
      321 +        if (fargv != NULL) {
      322 +                gargv = fargv;
      323 +        }
      324 +
 257  325          convch = *fmt;
 258  326          nextch = *++fmt;
      327 +
 259  328          *fmt = '\0';
 260  329          switch (convch) {
 261  330          case 'b': {
 262  331                  size_t len;
 263  332                  char *p;
 264  333                  int getout;
 265  334  
 266  335                  p = strdup(getstr());
 267  336                  if (p == NULL) {
 268  337                          warnx2("%s", strerror(ENOMEM), NULL);
 269  338                          return (NULL);
 270  339                  }
 271  340                  getout = escape(p, 0, &len);
 272      -                *(fmt - 1) = 's';
 273      -                PF(start, p);
 274      -                *(fmt - 1) = 'b';
      341 +                (void) fputs(p, stdout);
 275  342                  free(p);
 276  343  
 277  344                  if (getout)
 278      -                        return (fmt);
      345 +                        exit(*rval);
 279  346                  break;
 280  347          }
 281  348          case 'c': {
 282  349                  char p;
 283  350  
 284  351                  p = getchr();
 285  352                  PF(start, p);
 286  353                  break;
 287  354          }
 288  355          case 's': {
↓ open down ↓ 32 lines elided ↑ open up ↑
 321  388                          PF(start, p);
 322  389                  else
 323  390                          PF(start, (double)p);
 324  391                  break;
 325  392          }
 326  393          default:
 327  394                  warnx2(_("illegal format character %c"), convch, NULL);
 328  395                  return (NULL);
 329  396          }
 330  397          *fmt = nextch;
      398 +
      399 +        /* return the gargv to the next element */
 331  400          return (fmt);
 332  401  }
 333  402  
 334  403  static char *
 335  404  mknum(char *str, char ch)
 336  405  {
 337  406          static char *copy;
 338  407          static size_t copy_size;
 339  408          char *newcopy;
 340  409          size_t len, newlen;
↓ open down ↓ 37 lines elided ↑ open up ↑
 378  447                  case '\'':              /* single quote */
 379  448                          *store = *fmt;
 380  449                          break;
 381  450                  case 'a':               /* bell/alert */
 382  451                          *store = '\a';
 383  452                          break;
 384  453                  case 'b':               /* backspace */
 385  454                          *store = '\b';
 386  455                          break;
 387  456                  case 'c':
 388      -                        *store = '\0';
 389      -                        *len = PTRDIFF(store, save);
 390      -                        return (1);
      457 +                        if (!percent) {
      458 +                                *store = '\0';
      459 +                                *len = PTRDIFF(store, save);
      460 +                                return (1);
      461 +                        }
      462 +                        *store = 'c';
      463 +                        break;
 391  464                  case 'f':               /* form-feed */
 392  465                          *store = '\f';
 393  466                          break;
 394  467                  case 'n':               /* newline */
 395  468                          *store = '\n';
 396  469                          break;
 397  470                  case 'r':               /* carriage-return */
 398  471                          *store = '\r';
 399  472                          break;
 400  473                  case 't':               /* horizontal tab */
↓ open down ↓ 155 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX