Date: Thu, 7 Mar 2002 14:08:45 +1100 (EST) From: "Tim J. Robbins" <tim@robbins.dropbear.id.au> To: FreeBSD-gnats-submit@freebsd.org Subject: bin/35616: Patch to bring printf(1) up to POSIX.2 (1992) conformance Message-ID: <200203070308.g2738jh33226@descent.robbins.dropbear.id.au>
next in thread | raw e-mail | index | archive | help
>Number: 35616
>Category: bin
>Synopsis: Patch to bring printf(1) up to POSIX.2 (1992) conformance
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: change-request
>Submitter-Id: current-users
>Arrival-Date: Wed Mar 06 19:10:00 PST 2002
>Closed-Date:
>Last-Modified:
>Originator: Tim J. Robbins
>Release: FreeBSD 4.5-STABLE i386
>Organization:
>Environment:
System: FreeBSD descent.robbins.dropbear.id.au 4.5-STABLE FreeBSD 4.5-STABLE #5: Sat Feb 16 18:56:18 EST 2002 tim@descent.robbins.dropbear.id.au:/usr/obj/usr/src/sys/DESCENT i386
>Description:
FreeBSD's printf(1) command is not POSIX.2 conformant. The missing features
are:
- 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.
I believe this patch also brings printf(1) up to P1003.1-2001 conformance.
>How-To-Repeat:
$ printf %d A
65
(should write 0 and warn that it expected a number)
$ printf %b "hello\n"
printf: illegal format character b
(should be equivalent to printf "hello\n")
$ printf 'hello\n\cworld\n'
hello
cworld
(should be "hello" and a newline)
If you are trying to reproduce these problems, make sure you use
/usr/bin/printf - my shell, ksh93, has a printf builtin.
>Fix:
Index: printf/printf.1
===================================================================
RCS file: /home/ncvs/src/usr.bin/printf/printf.1,v
retrieving revision 1.19
diff -u -r1.19 printf.1
--- printf/printf.1 2002/01/16 14:55:18 1.19
+++ printf/printf.1 2002/03/07 02:55:11
@@ -61,7 +61,8 @@
.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 +71,8 @@
.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 +81,8 @@
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 +91,8 @@
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 +108,7 @@
.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 +191,7 @@
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,6 +248,11 @@
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
@@ -256,8 +266,20 @@
the actual width.
.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 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
Index: printf/printf.c
===================================================================
RCS file: /home/ncvs/src/usr.bin/printf/printf.c,v
retrieving revision 1.18
diff -u -r1.18 printf.c
--- printf/printf.c 2001/12/03 21:17:45 1.18
+++ printf/printf.c 2002/03/07 02:55:12
@@ -89,7 +89,7 @@
}
static int asciicode __P((void));
-static void escape __P((char *));
+static int escape __P((char *));
static int getchr __P((void));
static double getdouble __P((void));
static int getint __P((int *));
@@ -209,6 +209,19 @@
nextch = *++fmt;
*fmt = '\0';
switch(convch) {
+ case 'b': {
+ char *p;
+ int getout;
+
+ if ((p = strdup(getstr())) == NULL)
+ err(1, NULL);
+ getout = escape(p);
+ PF("%s", p);
+ free(p);
+ if (getout)
+ return (0);
+ break;
+ }
case 'c': {
char p;
@@ -227,11 +240,10 @@
quad_t p;
char *f;
- if ((f = mklong(start, convch)) == NULL)
- return (1);
- if (getquad(&p))
- return (1);
- PF(f, p);
+ if ((f = mklong(start, convch)) != NULL &&
+ !getquad(&p)) {
+ PF(f, p);
+ }
break;
}
case 'e': case 'E': case 'f': case 'g': case 'G': {
@@ -280,7 +292,7 @@
return (copy);
}
-static void
+static int
escape(fmt)
register char *fmt;
{
@@ -296,7 +308,7 @@
case '\0': /* EOS, user error */
*store = '\\';
*++store = '\0';
- return;
+ return (0);
case '\\': /* backslash */
case '\'': /* single quote */
*store = *fmt;
@@ -307,6 +319,9 @@
case 'b': /* backspace */
*store = '\b';
break;
+ case 'c':
+ *store = '\0';
+ return (1);
case 'f': /* form-feed */
*store = '\f';
break;
@@ -325,7 +340,7 @@
/* 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 +354,7 @@
}
}
*store = '\0';
+ return (0);
}
static int
@@ -357,7 +373,6 @@
return (*gargv++);
}
-static const char *Number = "+-.0123456789";
static int
getint(ip)
int *ip;
@@ -366,10 +381,8 @@
if (getquad(&val))
return (1);
- if (val < INT_MIN || val > INT_MAX) {
+ if (val < INT_MIN || val > INT_MAX)
warnx3("%s: %s", *gargv, strerror(ERANGE));
- return (1);
- }
*ip = (int)val;
return (0);
}
@@ -385,39 +398,51 @@
*lp = 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;
+ if (**gargv == '"' || **gargv == '\'') {
+ *lp = (quad_t)asciicode();
++gargv;
return (0);
}
- *lp = (long)asciicode();
+
+ errno = 0;
+ val = strtoq(*gargv, &ep, 0);
+ if (ep == *gargv)
+ warnx2("%s: expected numeric value", *gargv, NULL);
+ else if (*ep != '\0')
+ warnx2("%s: not completely converted", *gargv, NULL);
+ if (errno == ERANGE)
+ warnx3("%s: %s", *gargv, strerror(ERANGE));
+ *lp = val;
+ ++gargv;
return (0);
}
static double
getdouble()
{
+ double val;
+ char *ep;
+
if (!*gargv)
return ((double)0);
- if (strchr(Number, **gargv))
- return (atof(*gargv++));
- return ((double)asciicode());
+
+ if (**gargv == '"' || **gargv == '\'') {
+ val = (double)asciicode();
+ ++gargv;
+ return (val);
+ }
+
+ errno = 0;
+ val = strtod(*gargv, &ep);
+ if (ep == *gargv)
+ warnx2("%s: expected numeric value", *gargv, NULL);
+ else if (*ep != '\0')
+ warnx2("%s: not completely converted", *gargv, NULL);
+ if (errno == ERANGE)
+ warnx3("%s: %s", *gargv, strerror(ERANGE));
+ ++gargv;
+ return (val);
}
static int
>Release-Note:
>Audit-Trail:
>Unformatted:
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200203070308.g2738jh33226>
