Author: pfg
Date: Wed May  7 19:30:28 2014
New Revision: 265592
URL: http://svnweb.freebsd.org/changeset/base/265592

Log:
  Add width and precision specifiers to printf(1) %n$.
  
  This actually completes r264743 so that width and precision
  specifiers work properly with %n$. These keeps consistency
  with  ksh93 and zsh.
  
  Requested by: jilles
  Obtained from:        Garrett D'Amore (Illumos)
  MFC after:    4 days

Modified:
  head/usr.bin/printf/printf.c

Modified: head/usr.bin/printf/printf.c
==============================================================================
--- head/usr.bin/printf/printf.c        Wed May  7 19:22:54 2014        
(r265591)
+++ head/usr.bin/printf/printf.c        Wed May  7 19:30:28 2014        
(r265592)
@@ -1,4 +1,5 @@
 /*-
+ * Copyright 2014 Garrett D'Amore <garr...@damore.org>
  * Copyright 2010 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 1989, 1993
  *     The Regents of the University of California.  All rights reserved.
@@ -50,6 +51,7 @@ static const char rcsid[] =
 
 #include <sys/types.h>
 
+#include <ctype.h>
 #include <err.h>
 #include <errno.h>
 #include <inttypes.h>
@@ -70,11 +72,6 @@ static const char rcsid[] =
 
 #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); \
@@ -88,8 +85,6 @@ static const char rcsid[] =
                (void)fputs(b, stdout);                                 \
                free(b);                                                \
        }                                                               \
-       if (dollar)                                                     \
-               *f = '$';                                               \
 } while (0)
 
 static int      asciicode(void);
@@ -104,9 +99,12 @@ static const char
 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[])
@@ -158,7 +156,7 @@ main(int argc, char *argv[])
        gargv = ++argv;
 
        for (;;) {
-               char **maxargv = gargv;
+               maxargv = gargv;
 
                myargv = gargv;
                for (myargc = 0; gargv[myargc]; myargc++)
@@ -212,57 +210,115 @@ main(int argc, char *argv[])
 
 
 static char *
-printf_doformat(char *start, int *rval)
+printf_doformat(char *fmt, int *rval)
 {
        static const char skip1[] = "#'-+ 0";
-       static const char skip2[] = "0123456789";
-       char *fmt;
        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 = start + 1;
+       fmt++;
 
        /* look for "n$" field index specifier */
-       fmt += strspn(fmt, skip2);
-       if ((*fmt == '$') && (fmt != (start + 1))) {
-               int idx = atoi(start + 1);
+       l = strspn(fmt, digits);
+       if ((l > 0) && (fmt[l] == '$')) {
+               int idx = atoi(fmt);
                if (idx <= myargc) {
                        gargv = &myargv[idx - 1];
                } else {
                        gargv = &myargv[myargc];
                }
-               start = fmt;
-               fmt++;
+               if (gargv > maxargv)
+                       maxargv = gargv;
+               fmt += l + 1;
+
+       /* save format argument */
+       fargv = gargv;
        } else {
-               fmt = start + 1;
+       fargv = NULL;
        }
 
        /* skip to field width */
-       fmt += strspn(fmt, skip1);
+       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;
-               ++fmt;
+
+               *dptr++ = '*';
+               *dptr = 0;
        } else {
                havewidth = 0;
 
                /* skip to possible '.', get following precision */
-               fmt += strspn(fmt, skip2);
+               while (isdigit(*fmt)) {
+                       *dptr++ = *fmt++;
+                       *dptr = 0;
+               }
        }
+
        if (*fmt == '.') {
                /* precision present? */
-               ++fmt;
+               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;
-                       ++fmt;
+                       *dptr++ = '*';
+                       *dptr = 0;
                } else {
                        haveprec = 0;
 
                        /* skip to conversion char */
-                       fmt += strspn(fmt, skip2);
+                       while (isdigit(*fmt)) {
+                               *dptr++ = *fmt++;
+                               *dptr = 0;
+                       }
                }
        } else
                haveprec = 0;
@@ -270,6 +326,8 @@ printf_doformat(char *start, int *rval)
                warnx("missing format character");
                return (NULL);
        }
+       *dptr++ = *fmt;
+       *dptr = 0;
 
        /*
         * Look for a length modifier.  POSIX doesn't have these, so
@@ -292,8 +350,14 @@ printf_doformat(char *start, int *rval)
                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': {
@@ -365,6 +429,7 @@ printf_doformat(char *start, int *rval)
                return (NULL);
        }
        *fmt = nextch;
+       /* return the gargv to the next element */
        return (fmt);
 }
 
@@ -508,7 +573,7 @@ getnum(intmax_t *ip, uintmax_t *uip, int
        int rval;
 
        if (!*gargv) {
-               *ip = *uip = 0;
+               *ip = 0;
                return (0);
        }
        if (**gargv == '"' || **gargv == '\'') {
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to