Print this page
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   95          int chopped, end, rval;
  97   96          char *format, *fmt, *start;
  98   97  
  99   98          (void) setlocale(LC_ALL, "");
 100   99  
↓ open down ↓ 22 lines elided ↑ open up ↑
 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  127          chopped = 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 ↓ 23 lines elided ↑ open up ↑
 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;
      179 +        char **save;
 182  180          int fieldwidth, haveprec, havewidth, mod_ldbl, precision;
 183  181          char convch, nextch;
      182 +        char *start;
      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++;
 198      -        } else {
 199      -                fmt = start + 1;
      203 +                if (gargv > maxargv) {
      204 +                        maxargv = gargv;
      205 +                }
      206 +                fmt += l + 1;
 200  207          }
 201  208  
      209 +        save = gargv;
      210 +
 202  211          /* skip to field width */
 203      -        fmt += strspn(fmt, skip1);
      212 +        while (strchr(skip1, *fmt) != NULL) {
      213 +                *dptr++ = *fmt++;
      214 +                *dptr = 0;
      215 +        }
      216 +
 204  217          if (*fmt == '*') {
      218 +
      219 +                fmt++;
      220 +                l = strspn(fmt, digits);
      221 +                if ((l > 0) && (fmt[l] == '$')) {
      222 +                        int idx = atoi(fmt);
      223 +                        if (idx <= myargc) {
      224 +                                gargv = &myargv[idx - 1];
      225 +                        } else {
      226 +                                gargv = &myargv[myargc];
      227 +                        }
      228 +                        fmt += l + 1;
      229 +                }
      230 +
 205  231                  if (getint(&fieldwidth))
 206  232                          return (NULL);
      233 +                if (gargv > maxargv) {
      234 +                        maxargv = gargv;
      235 +                }
 207  236                  havewidth = 1;
 208      -                ++fmt;
      237 +
      238 +                *dptr++ = '*';
      239 +                *dptr = 0;
 209  240          } else {
 210  241                  havewidth = 0;
 211  242  
 212  243                  /* skip to possible '.', get following precision */
 213      -                fmt += strspn(fmt, skip2);
      244 +                while (isdigit(*fmt)) {
      245 +                        *dptr++ = *fmt++;
      246 +                        *dptr = 0;
      247 +                }
 214  248          }
      249 +
 215  250          if (*fmt == '.') {
 216  251                  /* precision present? */
 217      -                ++fmt;
      252 +                fmt++;
      253 +                *dptr++ = '.';
      254 +
 218  255                  if (*fmt == '*') {
      256 +
      257 +                        fmt++;
      258 +                        l = strspn(fmt, digits);
      259 +                        if ((l > 0) && (fmt[l] == '$')) {
      260 +                                int idx = atoi(fmt);
      261 +                                if (idx <= myargc) {
      262 +                                        gargv = &myargv[idx - 1];
      263 +                                } else {
      264 +                                        gargv = &myargv[myargc];
      265 +                                }
      266 +                                fmt += l + 1;
      267 +                        }
      268 +
 219  269                          if (getint(&precision))
 220  270                                  return (NULL);
      271 +                        if (gargv > maxargv) {
      272 +                                maxargv = gargv;
      273 +                        }
 221  274                          haveprec = 1;
 222      -                        ++fmt;
      275 +                        *dptr++ = '*';
      276 +                        *dptr = 0;
 223  277                  } else {
 224  278                          haveprec = 0;
 225  279  
 226  280                          /* skip to conversion char */
 227      -                        fmt += strspn(fmt, skip2);
      281 +                        while (isdigit(*fmt)) {
      282 +                                *dptr++ = *fmt++;
      283 +                                *dptr = 0;
      284 +                        }
 228  285                  }
 229  286          } else
 230  287                  haveprec = 0;
 231  288          if (!*fmt) {
 232  289                  warnx1(_("missing format character"), NULL, NULL);
 233  290                  return (NULL);
 234  291          }
      292 +        *dptr++ = *fmt;
      293 +        *dptr = 0;
 235  294  
 236  295          /*
 237  296           * Look for a length modifier.  POSIX doesn't have these, so
 238  297           * we only support them for floating-point conversions, which
 239  298           * are extensions.  This is useful because the L modifier can
 240  299           * be used to gain extra range and precision, while omitting
 241  300           * it is more likely to produce consistent results on different
 242  301           * architectures.  This is not so important for integers
 243  302           * because overflow is the only bad thing that can happen to
 244  303           * them, but consider the command  printf %a 1.1
↓ open down ↓ 2 lines elided ↑ open up ↑
 247  306                  mod_ldbl = 1;
 248  307                  fmt++;
 249  308                  if (!strchr("aAeEfFgG", *fmt)) {
 250  309                          warnx2(_("bad modifier L for %%%c"), *fmt, NULL);
 251  310                          return (NULL);
 252  311                  }
 253  312          } else {
 254  313                  mod_ldbl = 0;
 255  314          }
 256  315  
      316 +        gargv = save;
 257  317          convch = *fmt;
 258  318          nextch = *++fmt;
      319 +
 259  320          *fmt = '\0';
 260  321          switch (convch) {
 261  322          case 'b': {
 262  323                  size_t len;
 263  324                  char *p;
 264  325                  int getout;
 265  326  
 266  327                  p = strdup(getstr());
 267  328                  if (p == NULL) {
 268  329                          warnx2("%s", strerror(ENOMEM), NULL);
↓ open down ↓ 287 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX