Print this page
4818 printf(1) should support n$ width and precision specifiers
4854 printf(1) doesn't support %b and \c properly
Reviewed by: Keith Wesolowski <keith.wesolowski@joyent.com>
Approved by: TBD
*** 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,101 ****
static const char
*getstr(void);
static char *mknum(char *, char);
static void usage(void);
static int myargc;
static char **myargv;
static char **gargv;
int
main(int argc, char *argv[])
{
size_t len;
! int chopped, end, rval;
char *format, *fmt, *start;
(void) setlocale(LC_ALL, "");
argv++;
--- 79,100 ----
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;
! int end, rval;
char *format, *fmt, *start;
(void) setlocale(LC_ALL, "");
argv++;
*** 123,138 ****
* format strings are reused as necessary to use up the provided
* arguments, arguments of zero/null string are provided to use
* up the format string.
*/
fmt = format = *argv;
! 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;
--- 122,137 ----
* format strings are reused as necessary to use up the provided
* arguments, arguments of zero/null string are provided to use
* up the format string.
*/
fmt = format = *argv;
! (void) 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;
*** 161,171 ****
if (end == 1) {
warnx1(_("missing format character"), NULL, NULL);
return (1);
}
(void) fwrite(start, 1, PTRDIFF(fmt, start), stdout);
! if (chopped || !*gargv)
return (rval);
/* Restart at the beginning of the format string. */
fmt = format;
end = 1;
}
--- 160,170 ----
if (end == 1) {
warnx1(_("missing format character"), NULL, NULL);
return (1);
}
(void) fwrite(start, 1, PTRDIFF(fmt, start), stdout);
! if (!*gargv)
return (rval);
/* Restart at the beginning of the format string. */
fmt = format;
end = 1;
}
*** 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,317 ----
/* NOTREACHED */
}
static char *
! doformat(char *fmt, int *rval)
{
static const char skip1[] = "#'-+ 0";
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++;
+
/* 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 format argument */
! fargv = gargv;
} else {
! fargv = NULL;
}
/* skip to field width */
! while (*fmt && 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 (fargv == NULL) {
+ warnx1(_("incomplete use of n$"), NULL, NULL);
+ return (NULL);
+ }
+ if (idx <= myargc) {
+ gargv = &myargv[idx - 1];
+ } else {
+ gargv = &myargv[myargc];
+ }
+ fmt += l + 1;
+ } else if (fargv != NULL) {
+ warnx1(_("incomplete use of n$"), NULL, NULL);
+ return (NULL);
+ }
+
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 (fargv == NULL) {
+ warnx1(_("incomplete use of n$"),
+ NULL, NULL);
+ return (NULL);
+ }
+ if (idx <= myargc) {
+ gargv = &myargv[idx - 1];
+ } else {
+ gargv = &myargv[myargc];
+ }
+ fmt += l + 1;
+ } else if (fargv != NULL) {
+ warnx1(_("incomplete use of n$"), NULL, NULL);
+ return (NULL);
+ }
+
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 ****
--- 330,347 ----
}
} else {
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': {
size_t len;
char *p;
*** 267,283 ****
if (p == NULL) {
warnx2("%s", strerror(ENOMEM), NULL);
return (NULL);
}
getout = escape(p, 0, &len);
! *(fmt - 1) = 's';
! PF(start, p);
! *(fmt - 1) = 'b';
free(p);
if (getout)
! return (fmt);
break;
}
case 'c': {
char p;
--- 351,365 ----
if (p == NULL) {
warnx2("%s", strerror(ENOMEM), NULL);
return (NULL);
}
getout = escape(p, 0, &len);
! (void) fputs(p, stdout);
free(p);
if (getout)
! exit(*rval);
break;
}
case 'c': {
char p;
*** 326,335 ****
--- 408,419 ----
default:
warnx2(_("illegal format character %c"), convch, NULL);
return (NULL);
}
*fmt = nextch;
+
+ /* return the gargv to the next element */
return (fmt);
}
static char *
mknum(char *str, char ch)
*** 383,395 ****
--- 467,483 ----
break;
case 'b': /* backspace */
*store = '\b';
break;
case 'c':
+ if (!percent) {
*store = '\0';
*len = PTRDIFF(store, save);
return (1);
+ }
+ *store = 'c';
+ break;
case 'f': /* form-feed */
*store = '\f';
break;
case 'n': /* newline */
*store = '\n';