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
This commit is contained in:
Pedro F. Giffuni 2014-05-07 19:30:28 +00:00
parent 4d6bab2606
commit f8a6c905bf
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=265592

View File

@ -1,4 +1,5 @@
/*- /*-
* Copyright 2014 Garrett D'Amore <garrett@damore.org>
* Copyright 2010 Nexenta Systems, Inc. All rights reserved. * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 1989, 1993 * Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved. * The Regents of the University of California. All rights reserved.
@ -50,6 +51,7 @@ static const char rcsid[] =
#include <sys/types.h> #include <sys/types.h>
#include <ctype.h>
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <inttypes.h> #include <inttypes.h>
@ -70,11 +72,6 @@ static const char rcsid[] =
#define PF(f, func) do { \ #define PF(f, func) do { \
char *b = NULL; \ char *b = NULL; \
int dollar = 0; \
if (*f == '$') { \
dollar++; \
*f = '%'; \
} \
if (havewidth) \ if (havewidth) \
if (haveprec) \ if (haveprec) \
(void)asprintf(&b, f, fieldwidth, precision, func); \ (void)asprintf(&b, f, fieldwidth, precision, func); \
@ -88,8 +85,6 @@ static const char rcsid[] =
(void)fputs(b, stdout); \ (void)fputs(b, stdout); \
free(b); \ free(b); \
} \ } \
if (dollar) \
*f = '$'; \
} while (0) } while (0)
static int asciicode(void); static int asciicode(void);
@ -104,9 +99,12 @@ static const char
static char *mknum(char *, char); static char *mknum(char *, char);
static void usage(void); static void usage(void);
static const char digits[] = "0123456789";
static int myargc; static int myargc;
static char **myargv; static char **myargv;
static char **gargv; static char **gargv;
static char **maxargv;
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
@ -158,7 +156,7 @@ main(int argc, char *argv[])
gargv = ++argv; gargv = ++argv;
for (;;) { for (;;) {
char **maxargv = gargv; maxargv = gargv;
myargv = gargv; myargv = gargv;
for (myargc = 0; gargv[myargc]; myargc++) for (myargc = 0; gargv[myargc]; myargc++)
@ -212,57 +210,115 @@ main(int argc, char *argv[])
static char * static char *
printf_doformat(char *start, int *rval) printf_doformat(char *fmt, int *rval)
{ {
static const char skip1[] = "#'-+ 0"; static const char skip1[] = "#'-+ 0";
static const char skip2[] = "0123456789";
char *fmt;
int fieldwidth, haveprec, havewidth, mod_ldbl, precision; int fieldwidth, haveprec, havewidth, mod_ldbl, precision;
char convch, nextch; char convch, nextch;
char *start;
char **fargv;
char *dptr;
int l;
fmt = start + 1; start = alloca(strlen(fmt) + 1);
dptr = start;
*dptr++ = '%';
*dptr = 0;
fmt++;
/* look for "n$" field index specifier */ /* look for "n$" field index specifier */
fmt += strspn(fmt, skip2); l = strspn(fmt, digits);
if ((*fmt == '$') && (fmt != (start + 1))) { if ((l > 0) && (fmt[l] == '$')) {
int idx = atoi(start + 1); int idx = atoi(fmt);
if (idx <= myargc) { if (idx <= myargc) {
gargv = &myargv[idx - 1]; gargv = &myargv[idx - 1];
} else { } else {
gargv = &myargv[myargc]; gargv = &myargv[myargc];
} }
start = fmt; if (gargv > maxargv)
fmt++; maxargv = gargv;
fmt += l + 1;
/* save format argument */
fargv = gargv;
} else { } else {
fmt = start + 1; fargv = NULL;
} }
/* skip to field width */ /* skip to field width */
fmt += strspn(fmt, skip1); while (strchr(skip1, *fmt) != NULL) {
*dptr++ = *fmt++;
*dptr = 0;
}
if (*fmt == '*') { 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)) if (getint(&fieldwidth))
return (NULL); return (NULL);
if (gargv > maxargv)
maxargv = gargv;
havewidth = 1; havewidth = 1;
++fmt;
*dptr++ = '*';
*dptr = 0;
} else { } else {
havewidth = 0; havewidth = 0;
/* skip to possible '.', get following precision */ /* skip to possible '.', get following precision */
fmt += strspn(fmt, skip2); while (isdigit(*fmt)) {
*dptr++ = *fmt++;
*dptr = 0;
}
} }
if (*fmt == '.') { if (*fmt == '.') {
/* precision present? */ /* precision present? */
++fmt; fmt++;
*dptr++ = '.';
if (*fmt == '*') { 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)) if (getint(&precision))
return (NULL); return (NULL);
if (gargv > maxargv)
maxargv = gargv;
haveprec = 1; haveprec = 1;
++fmt; *dptr++ = '*';
*dptr = 0;
} else { } else {
haveprec = 0; haveprec = 0;
/* skip to conversion char */ /* skip to conversion char */
fmt += strspn(fmt, skip2); while (isdigit(*fmt)) {
*dptr++ = *fmt++;
*dptr = 0;
}
} }
} else } else
haveprec = 0; haveprec = 0;
@ -270,6 +326,8 @@ printf_doformat(char *start, int *rval)
warnx("missing format character"); warnx("missing format character");
return (NULL); return (NULL);
} }
*dptr++ = *fmt;
*dptr = 0;
/* /*
* Look for a length modifier. POSIX doesn't have these, so * Look for a length modifier. POSIX doesn't have these, so
@ -292,8 +350,14 @@ printf_doformat(char *start, int *rval)
mod_ldbl = 0; mod_ldbl = 0;
} }
/* save the current arg offset, and set to the format arg */
if (fargv != NULL) {
gargv = fargv;
}
convch = *fmt; convch = *fmt;
nextch = *++fmt; nextch = *++fmt;
*fmt = '\0'; *fmt = '\0';
switch (convch) { switch (convch) {
case 'b': { case 'b': {
@ -365,6 +429,7 @@ printf_doformat(char *start, int *rval)
return (NULL); return (NULL);
} }
*fmt = nextch; *fmt = nextch;
/* return the gargv to the next element */
return (fmt); return (fmt);
} }
@ -508,7 +573,7 @@ getnum(intmax_t *ip, uintmax_t *uip, int signedconv)
int rval; int rval;
if (!*gargv) { if (!*gargv) {
*ip = *uip = 0; *ip = 0;
return (0); return (0);
} }
if (**gargv == '"' || **gargv == '\'') { if (**gargv == '"' || **gargv == '\'') {