Skip site navigation (1)Skip section navigation (2)
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>