Fix printf POSIX compatibility bug reported by Ben Harris in
authorJim Meyering <jim@meyering.net>
Mon, 21 Apr 2003 17:59:21 +0000 (17:59 +0000)
committerJim Meyering <jim@meyering.net>
Mon, 21 Apr 2003 17:59:21 +0000 (17:59 +0000)
<http://mail.gnu.org/archive/html/bug-coreutils/2003-04/msg00070.html>.

* doc/coreutils.texi (printf invocation): It's \NNN in the format,
\0NNN in the %b operand.
* src/printf.c (usage): Likewise.
(print_esc): New arg OCTAL0 to specify whether \0NNN or \NNN
is desired.  All uses changed.  Behave like Bash printf if %b
operand uses \NNN where the initial N is not 0.

src/printf.c

index 22e1b97..cc4bfd9 100644 (file)
    \r = carriage return
    \t = horizontal tab
    \v = vertical tab
-   \0ooo = octal number (ooo is 0 to 3 digits)
+   \ooo = octal number (ooo is 1 to 3 digits)
    \xhh = hexadecimal number (hhh is 1 to 2 digits)
    \uhhhh = 16-bit Unicode character (hhhh is 4 digits)
    \Uhhhhhhhh = 32-bit Unicode character (hhhhhhhh is 8 digits)
 
    Additional directive:
 
-   %b = print an argument string, interpreting backslash escapes
+   %b = print an argument string, interpreting backslash escapes,
+     except that octal escapes are of the form \0 or \0ooo.
 
    The `format' argument is re-used as many times as necessary
    to convert all of the given arguments.
@@ -113,7 +114,7 @@ Print ARGUMENT(s) according to FORMAT.\n\
 FORMAT controls the output as in C printf.  Interpreted sequences are:\n\
 \n\
   \\\"      double quote\n\
-  \\0NNN   character with octal value NNN (0 to 3 digits)\n\
+  \\NNN    character with octal value NNN (1 to 3 digits)\n\
   \\\\      backslash\n\
 "), stdout);
       fputs (_("\
@@ -136,7 +137,8 @@ FORMAT controls the output as in C printf.  Interpreted sequences are:\n\
 "), stdout);
       fputs (_("\
   %%      a single %\n\
-  %b      ARGUMENT as a string with `\\' escapes interpreted\n\
+  %b      ARGUMENT as a string with `\\' escapes interpreted,\n\
+            except that octal escapes are of the form \\0 or \\0NNN\n\
 \n\
 and all C format specifications ending with one of diouxXfeEgGcs, with\n\
 ARGUMENTs converted to proper type first.  Variable widths are handled.\n\
@@ -234,10 +236,12 @@ print_esc_char (int c)
 
 /* Print a \ escape sequence starting at ESCSTART.
    Return the number of characters in the escape sequence
-   besides the backslash. */
+   besides the backslash.
+   If OCTAL0 is nonzero, octal escapes are of the form \0ooo, where o
+   is an octal digit; otherwise they are of the form \ooo.  */
 
 static int
-print_esc (const char *escstart)
+print_esc (const char *escstart, bool octal0)
 {
   register const char *p = escstart + 1;
   int esc_value = 0;           /* Value of \nnn escape. */
@@ -254,11 +258,12 @@ print_esc (const char *escstart)
        error (EXIT_FAILURE, 0, _("missing hexadecimal number in escape"));
       putchar (esc_value);
     }
-  else if (*p == '0')
+  else if (isodigit (*p))
     {
-      /* An octal \0ooo escape sequence has 0 to 3 octal digits
-        after the leading \0.  */
-      for (esc_length = 0, ++p;
+      /* Parse \0ooo (if octal0 && *p == '0') or \ooo (otherwise).
+         Allow \ooo if octal0 && *p != '0'; this is an undocumented
+         extension to POSIX that is compatible with Bash 2.05b.  */
+      for (esc_length = 0, p += octal0 && *p == '0';
           esc_length < 3 && isodigit (*p);
           ++esc_length, ++p)
        esc_value = esc_value * 8 + octtobin (*p);
@@ -313,7 +318,7 @@ print_esc_string (const char *str)
 {
   for (; *str; str++)
     if (*str == '\\')
-      str += print_esc (str);
+      str += print_esc (str, true);
     else
       putchar (*str);
 }
@@ -531,7 +536,7 @@ print_formatted (const char *format, int argc, char **argv)
          break;
 
        case '\\':
-         f += print_esc (f);
+         f += print_esc (f, false);
          break;
 
        default: