Print this page
4818 printf(1) should support n$ width and precision specifiers
        
*** 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,95 ****
--- 79,94 ----
  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;
*** 128,138 ****
          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;
--- 127,137 ----
          chopped = 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;
*** 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,298 ----
          /* NOTREACHED */
  }
  
  
  static char *
! doformat(char *fmt, int *rval)
  {
          static const char skip1[] = "#'-+ 0";
!         char **save;
          int fieldwidth, haveprec, havewidth, mod_ldbl, precision;
          char convch, nextch;
+         char *start;
+         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 = gargv;
+ 
          /* skip to field width */
!         while (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 (idx <= myargc) {
+                                 gargv = &myargv[idx - 1];
+                         } else {
+                                 gargv = &myargv[myargc];
+                         }
+                         fmt += l + 1;
+                 }
+ 
                  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 (idx <= myargc) {
+                                         gargv = &myargv[idx - 1];
+                                 } else {
+                                         gargv = &myargv[myargc];
+                                 }
+                                 fmt += l + 1;
+                         }
+ 
                          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 ****
--- 311,324 ----
                  }
          } else {
                  mod_ldbl = 0;
          }
  
+         gargv = save;
          convch = *fmt;
          nextch = *++fmt;
+ 
          *fmt = '\0';
          switch (convch) {
          case 'b': {
                  size_t len;
                  char *p;