aboutsummaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
authorJuli Mallett <jmallett@FreeBSD.org>2002-04-29 16:45:16 +0000
committerJuli Mallett <jmallett@FreeBSD.org>2002-04-29 16:45:16 +0000
commitd5c6a4d909d7a76e652b256e463e78fe82f9f7b1 (patch)
tree2825191aea280ae5bad8d76c39c7ec25ea3afa33 /usr.bin
parentc7ae02febb3c8af4d5d65d6d29c51da624736fad (diff)
downloadsrc-d5c6a4d909d7a76e652b256e463e78fe82f9f7b1.tar.gz
src-d5c6a4d909d7a76e652b256e463e78fe82f9f7b1.zip
MFC printf(1) changes.
Notably revision 1.20 of printf.c and revision 1.21 of printf.1: - printf shouldn't bail out if a conversion fails, it should just keep processing them. - \c escape to immediately stop output (similar to echo's \c) - \0NNN should be allowed for octal character escapes (instead of just \NNN) - %b conversion, which is like %s but interprets \n \t etc. inside the string is missing. And revision 1.21 of printf.c: Handle numbers larger than QUAD_MAX for unsigned conversions correctly. Exit with nonzero status if a conversion failed. Play nice if used as a shell builtin (currently disabled). PR: 35616 (some of it)
Notes
Notes: svn path=/stable/4/; revision=95736
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/printf/Makefile1
-rw-r--r--usr.bin/printf/printf.155
-rw-r--r--usr.bin/printf/printf.c203
3 files changed, 174 insertions, 85 deletions
diff --git a/usr.bin/printf/Makefile b/usr.bin/printf/Makefile
index 52b20f4d3599..c91c4e3f53c1 100644
--- a/usr.bin/printf/Makefile
+++ b/usr.bin/printf/Makefile
@@ -1,4 +1,5 @@
# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
PROG= printf
diff --git a/usr.bin/printf/printf.1 b/usr.bin/printf/printf.1
index 14370e143c68..cea67da7264d 100644
--- a/usr.bin/printf/printf.1
+++ b/usr.bin/printf/printf.1
@@ -45,8 +45,9 @@
.Nm
.Ar format Op Ar arguments ...
.Sh DESCRIPTION
-.Nm Printf
-formats and prints its arguments, after the first, under control
+The
+.Nm
+utility formats and prints its arguments, after the first, under control
of the
.Ar format .
The
@@ -61,7 +62,8 @@ The
.Ar arguments
after the first are treated as strings if the corresponding format is
either
-.Cm c
+.Cm c ,
+.Cm b
or
.Cm s ;
otherwise it is evaluated as a C constant, with the following extensions:
@@ -70,8 +72,8 @@ otherwise it is evaluated as a C constant, with the following extensions:
.It
A leading plus or minus sign is allowed.
.It
-If the leading character is a single or double quote, or not a digit,
-plus, or minus sign, the value is the ASCII code of the next character.
+If the leading character is a single or double quote the value is the ASCII
+code of the next character.
.El
.Pp
The format string is reused as often as necessary to satisfy the
@@ -80,7 +82,8 @@ Any extra format specifications are evaluated with zero or the null
string.
.Pp
Character escape sequences are in backslash notation as defined in the
-.St -ansiC .
+.St -ansiC ,
+with extensions.
The characters and their meanings
are as follows:
.Pp
@@ -89,6 +92,8 @@ are as follows:
Write a <bell> character.
.It Cm \eb
Write a <backspace> character.
+.It Cm \ec
+Ignore remaining characters in this string.
.It Cm \ef
Write a <form-feed> character.
.It Cm \en
@@ -104,6 +109,7 @@ Write a <single quote> character.
.It Cm \e\e
Write a backslash character.
.It Cm \e Ns Ar num
+.It Cm \e0 Ns Ar num
Write an 8-bit character whose
.Tn ASCII
value is the 1-, 2-, or 3-digit
@@ -186,7 +192,7 @@ from a string; if the digit string is missing, the precision is treated
as zero;
.It Format:
A character which indicates the type of format to use (one of
-.Cm diouxXfwEgGcs ) .
+.Cm diouxXfwEgGcsb ) .
.El
.Pp
A field width or precision may be
@@ -243,31 +249,38 @@ Characters from the string
are printed until the end is reached or until the number of characters
indicated by the precision specification is reached; however if the
precision is 0 or missing, all characters in the string are printed.
+.It Cm b
+As for
+.Cm s ,
+but interpret character escapes in backslash notation in the string
+.Ar argument .
.It Cm \&%
Print a `%'; no argument is used.
.El
.Pp
The decimal point
-character is defined in the program's locale (category LC_NUMERIC).
+character is defined in the program's locale (category
+.Dv LC_NUMERIC ) .
.Pp
In no case does a non-existent or small field width cause truncation of
a field; padding takes place only if the specified field width exceeds
the actual width.
-.Pp
-Some shells may provide a builtin
-.Nm
-command which is similar or identical to this utility.
-Consult the
-.Xr builtin 1
-manual page.
-.Sh RETURN VALUES
-.Nm Printf
-exits 0 on success, 1 on failure.
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh COMPATIBILITY
+The traditional
+.Bx
+behavior of converting arguments of numeric formats not beginning
+with a digit to the ASCII code of the first characer is not supported.
.Sh SEE ALSO
-.Xr builtin 1 ,
-.Xr csh 1 ,
-.Xr sh 1 ,
+.Xr echo 1 ,
.Xr printf 3
+.Sh STANDARDS
+The
+.Nm
+command is expected to be compatible with the
+.St -p1003.2
+specification.
.Sh HISTORY
The
.Nm
diff --git a/usr.bin/printf/printf.c b/usr.bin/printf/printf.c
index 897a063fd9ce..e0d1591b50c4 100644
--- a/usr.bin/printf/printf.c
+++ b/usr.bin/printf/printf.c
@@ -71,7 +71,7 @@ static const char rcsid[] =
#include <locale.h>
#endif
-#define PF(f, func) { \
+#define PF(f, func) do { \
char *b = NULL; \
if (fieldwidth) \
if (precision) \
@@ -86,17 +86,18 @@ static const char rcsid[] =
(void)fputs(b, stdout); \
free(b); \
} \
-}
-
-static int asciicode __P((void));
-static void escape __P((char *));
-static int getchr __P((void));
-static double getdouble __P((void));
-static int getint __P((int *));
-static int getquad __P((quad_t *));
-static char *getstr __P((void));
-static char *mklong __P((char *, int));
-static void usage __P((void));
+} while (0)
+
+static int asciicode(void);
+static int escape(char *);
+static int getchr(void);
+static int getdouble(double *);
+static int getint(int *);
+static int getquads(quad_t *, u_quad_t *, int);
+static const char
+ *getstr(void);
+static char *mkquad(char *, int);
+static void usage(void);
static char **gargv;
@@ -109,8 +110,8 @@ main(argc, argv)
int argc;
char *argv[];
{
- static char *skip1, *skip2;
- int ch, end, fieldwidth, precision;
+ static const char *skip1, *skip2;
+ int ch, chopped, end, fieldwidth, precision, rval;
char convch, nextch, *format, *fmt, *start;
#ifndef BUILTIN
@@ -142,7 +143,8 @@ main(argc, argv)
skip1 = "#-+ 0";
skip2 = "0123456789";
- escape(fmt = format = *argv); /* backslash interpretation */
+ chopped = escape(fmt = format = *argv); /* backslash interpretation */
+ rval = 0;
gargv = ++argv;
for (;;) {
end = 0;
@@ -150,6 +152,10 @@ main(argc, argv)
next: for (start = fmt;; ++fmt) {
if (!*fmt) {
/* avoid infinite loop */
+ if (chopped) {
+ (void)printf("%s", start);
+ return (rval);
+ }
if (end == 1) {
warnx1("missing format character",
NULL, NULL);
@@ -159,7 +165,7 @@ next: for (start = fmt;; ++fmt) {
if (fmt > start)
(void)printf("%s", start);
if (!*gargv)
- return (0);
+ return (rval);
fmt = format;
goto next;
}
@@ -209,6 +215,32 @@ next: for (start = fmt;; ++fmt) {
nextch = *++fmt;
*fmt = '\0';
switch(convch) {
+ case 'b': {
+ char *p;
+ int getout;
+
+#ifdef SHELL
+ p = savestr(getstr());
+#else
+ p = strdup(getstr());
+#endif
+ if (p == NULL) {
+ warnx2("%s", strerror(ENOMEM), NULL);
+ return (1);
+ }
+ getout = escape(p);
+ *(fmt - 1) = 's';
+ PF(start, p);
+ *(fmt - 1) = 'b';
+#ifdef SHELL
+ ckfree(p);
+#else
+ free(p);
+#endif
+ if (getout)
+ return (rval);
+ break;
+ }
case 'c': {
char p;
@@ -217,27 +249,34 @@ next: for (start = fmt;; ++fmt) {
break;
}
case 's': {
- char *p;
+ const char *p;
p = getstr();
PF(start, p);
break;
}
case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
- quad_t p;
char *f;
+ quad_t val;
+ u_quad_t uval;
+ int signedconv;
- if ((f = mklong(start, convch)) == NULL)
+ signedconv = (convch == 'd' || convch == 'i');
+ if ((f = mkquad(start, convch)) == NULL)
return (1);
- if (getquad(&p))
- return (1);
- PF(f, p);
+ if (getquads(&val, &uval, signedconv))
+ rval = 1;
+ if (signedconv)
+ PF(f, val);
+ else
+ PF(f, uval);
break;
}
case 'e': case 'E': case 'f': case 'g': case 'G': {
double p;
- p = getdouble();
+ if (getdouble(&p))
+ rval = 1;
PF(start, p);
break;
}
@@ -251,14 +290,14 @@ next: for (start = fmt;; ++fmt) {
}
static char *
-mklong(str, ch)
+mkquad(str, ch)
char *str;
int ch;
{
static char *copy;
static size_t copy_size;
- size_t len, newlen;
char *newcopy;
+ size_t len, newlen;
len = strlen(str) + 2;
if (len > copy_size) {
@@ -268,7 +307,10 @@ mklong(str, ch)
#else
if ((newcopy = realloc(copy, newlen)) == NULL)
#endif
+ {
+ warnx2("%s", strerror(ENOMEM), NULL);
return (NULL);
+ }
copy = newcopy;
copy_size = newlen;
}
@@ -280,7 +322,7 @@ mklong(str, ch)
return (copy);
}
-static void
+static int
escape(fmt)
register char *fmt;
{
@@ -296,7 +338,7 @@ escape(fmt)
case '\0': /* EOS, user error */
*store = '\\';
*++store = '\0';
- return;
+ return (0);
case '\\': /* backslash */
case '\'': /* single quote */
*store = *fmt;
@@ -307,6 +349,9 @@ escape(fmt)
case 'b': /* backspace */
*store = '\b';
break;
+ case 'c':
+ *store = '\0';
+ return (1);
case 'f': /* form-feed */
*store = '\f';
break;
@@ -325,7 +370,7 @@ escape(fmt)
/* octal constant */
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
- for (c = 3, value = 0;
+ for (c = *fmt == '0' ? 4 : 3, value = 0;
c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
value <<= 3;
value += *fmt - '0';
@@ -339,6 +384,7 @@ escape(fmt)
}
}
*store = '\0';
+ return (0);
}
static int
@@ -349,7 +395,7 @@ getchr()
return ((int)**gargv++);
}
-static char *
+static const char *
getstr()
{
if (!*gargv)
@@ -357,67 +403,96 @@ getstr()
return (*gargv++);
}
-static char *Number = "+-.0123456789";
static int
getint(ip)
int *ip;
{
quad_t val;
+ u_quad_t uval;
+ int rval;
- if (getquad(&val))
+ if (getquads(&val, &uval, 1))
return (1);
+ rval = 0;
if (val < INT_MIN || val > INT_MAX) {
warnx3("%s: %s", *gargv, strerror(ERANGE));
- return (1);
+ rval = 1;
}
*ip = (int)val;
- return (0);
+ return (rval);
}
static int
-getquad(lp)
- quad_t *lp;
+getquads(qp, uqp, signedconv)
+ quad_t *qp;
+ u_quad_t *uqp;
+ int signedconv;
{
- quad_t val;
char *ep;
+ int rval;
if (!*gargv) {
- *lp = 0;
+ *qp = 0;
return (0);
}
- if (strchr(Number, **gargv)) {
- errno = 0;
- val = strtoq(*gargv, &ep, 0);
- if (*ep != '\0') {
- warnx2("%s: illegal number", *gargv, NULL);
- return (1);
- }
- if (errno == ERANGE)
- if (val == QUAD_MAX) {
- warnx3("%s: %s", *gargv, strerror(ERANGE));
- return (1);
- }
- if (val == QUAD_MIN) {
- warnx3("%s: %s", *gargv, strerror(ERANGE));
- return (1);
- }
-
- *lp = val;
- ++gargv;
+ if (**gargv == '"' || **gargv == '\'') {
+ if (signedconv)
+ *qp = asciicode();
+ else
+ *uqp = asciicode();
return (0);
}
- *lp = (long)asciicode();
- return (0);
+ rval = 0;
+ errno = 0;
+ if (signedconv)
+ *qp = strtoq(*gargv, &ep, 0);
+ else
+ *uqp = strtouq(*gargv, &ep, 0);
+ if (ep == *gargv) {
+ warnx2("%s: expected numeric value", *gargv, NULL);
+ rval = 1;
+ }
+ else if (*ep != '\0') {
+ warnx2("%s: not completely converted", *gargv, NULL);
+ rval = 1;
+ }
+ if (errno == ERANGE) {
+ warnx3("%s: %s", *gargv, strerror(ERANGE));
+ rval = 1;
+ }
+ ++gargv;
+ return (rval);
}
-static double
-getdouble()
+static int
+getdouble(dp)
+ double *dp;
{
+ char *ep;
+ int rval;
+
if (!*gargv)
- return ((double)0);
- if (strchr(Number, **gargv))
- return (atof(*gargv++));
- return ((double)asciicode());
+ return (0);
+ if (**gargv == '"' || **gargv == '\'') {
+ *dp = asciicode();
+ return (0);
+ }
+ rval = 1;
+ errno = 0;
+ *dp = strtod(*gargv, &ep);
+ if (ep == *gargv) {
+ warnx2("%s: expected numeric value", *gargv, NULL);
+ rval = 1;
+ } else if (*ep != '\0') {
+ warnx2("%s: not completely converted", *gargv, NULL);
+ rval = 1;
+ }
+ if (errno == ERANGE) {
+ warnx3("%s: %s", *gargv, strerror(ERANGE));
+ rval = 1;
+ }
+ ++gargv;
+ return (rval);
}
static int