avoid undefined behavior in format(%c)

Eric Blake <[email protected]>
Newsgroups gmane.comp.gnu.m4.patches
Message-ID <[email protected]>
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I introduced a regression in 1.4.9b where format(%c,65) would call
printf("%*.*c",0,-1,'a') under the hood, but that is undefined in POSIX.
This is just a stop-gap measure so that I can release 1.4.10 without known
undefined behavior; I still intend to make 1.4.11 where the format builtin
has more capabilities bringing it more in line with printf(1), but want to
get a GPLv3 version out now rather than after completing that patch.

I'm applying this to both branch and head.  My goal is to get 1.4.10 out
by tomorrow.

2007-07-09  Eric Blake  <[email protected]>

	Avoid undefined behavior of %.*c in printf.
	* src/format.c (format): Special case %c.
	* TODO: Document that more remains to be done.
	* NEWS: Document the fix.

- --
Don't work too hard, make some time for fun as well!

Eric Blake             [email protected]
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFGki+k84KuGfSFAYARAiJPAJwJMnYYR3/K6SpeKR5VMmetyBSjYwCgqqFU
97SHUuO1CrJlcNjXa4RQBwY=
=iTY5
-----END PGP SIGNATURE-----

_______________________________________________
M4-patches mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/m4-patches
m4.patch272 (text/plain, 4.3 KB)
Index: NEWS
===================================================================
RCS file: /sources/m4/m4/NEWS,v
retrieving revision 1.1.1.1.2.106
diff -u -p -r1.1.1.1.2.106 NEWS
--- NEWS	5 Jul 2007 03:53:05 -0000	1.1.1.1.2.106
+++ NEWS	9 Jul 2007 12:50:54 -0000
@@ -5,6 +5,11 @@ Foundation, Inc.
 Version 1.4.10 - ?? ??? 2007, by ????  (CVS version 1.4.9c)
 
 * Upgrade from GPL version 2 to GPL version 3 or later.
+* A number of portability improvements inherited from gnulib.
+* Avoid undefined behavior introduced in 1.4.9b in the `format' builtin
+  when handling %c.  However, this area of code has never been documented,
+  and currently does not match the POSIX behavior of printf(1), so it may
+  have further changes in the next version.
 
 Version 1.4.9b - 29 May 2007, by Eric Blake  (CVS version 1.4.9a)
 
@@ -19,7 +24,9 @@ Version 1.4.9b - 29 May 2007, by Eric Bl
   more predictably when stdin is seekable.
 * The `format' builtin now understands formats such as %a, %A, and %'hhd,
   and works around a number of platform printf bugs.  Furthermore, the
-  sequence format(%*.*d,-1,-1,1) no longer outputs random data.
+  sequence format(%*.*d,-1,-1,1) no longer outputs random data.  However,
+  some non-compliant platforms such as mingw still have known bugs in
+  strtod that may cause testsuite failures.
 * The testsuite is improved to also run gnulib portability tests for the
   features that M4 imports from gnulib.
 
Index: TODO
===================================================================
RCS file: /sources/m4/m4/TODO,v
retrieving revision 1.1.1.1.2.2
diff -u -p -r1.1.1.1.2.2 TODO
--- TODO	22 Aug 2006 16:13:38 -0000	1.1.1.1.2.2
+++ TODO	9 Jul 2007 12:50:54 -0000
@@ -4,6 +4,9 @@ Tell <[email protected]> if you feel like v
 of these ideas, listed more or less in decreasing order of priority.
 
 * Features or problems
+  - Fix format() builtin to handle %c more like printf(1) in the shell
+        http://lists.gnu.org/archive/html/bug-m4/2007-05/msg00047.html
+    Also, gnulib needs help to overcome mingw bugs related to format().
   - Update documentation from accumulated mail about it
   - Study synclines at the very beginning of each diverted sequence
   - Make eval work on bignums - the 32 bits limit is artificial
@@ -34,7 +37,7 @@ mode: outline
 outline-regexp: " *[-+*.] \\|"
 End:
 
-Copyright (C) 2000, 2006 Free Software Foundation, Inc.
+Copyright (C) 2000, 2006, 2007 Free Software Foundation, Inc.
 
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.2 or
Index: src/format.c
===================================================================
RCS file: /sources/m4/m4/src/Attic/format.c,v
retrieving revision 1.1.1.1.2.7
diff -u -p -r1.1.1.1.2.7 format.c
--- src/format.c	29 May 2007 13:19:47 -0000	1.1.1.1.2.7
+++ src/format.c	9 Jul 2007 12:50:54 -0000
@@ -85,9 +85,9 @@ format (struct obstack *obs, int argc, t
 
   /* Buffer and stuff.  */
   char *str;			/* malloc'd buffer of formatted text */
-  enum {INT, LONG, DOUBLE, STR} datatype;
+  enum {CHAR, INT, LONG, DOUBLE, STR} datatype;
 
-  f = fmt = (const char *) ARG_STR (argc, argv);
+  f = fmt = ARG_STR (argc, argv);
   memset (ok, 0, sizeof ok);
   for (;;)
     {
@@ -176,13 +176,11 @@ format (struct obstack *obs, int argc, t
 	  fmt++;
 	}
       else
-	{
-	  while (isdigit (to_uchar (*fmt)))
-	    {
-	      width = 10 * width + *fmt - '0';
-	      fmt++;
-	    }
-	}
+	while (isdigit (to_uchar (*fmt)))
+	  {
+	    width = 10 * width + *fmt - '0';
+	    fmt++;
+	  }
 
       /* Maximum precision; an explicit negative precision is the same
 	 as not giving the precision.  A lone '.' is a precision of 0.  */
@@ -238,14 +236,13 @@ format (struct obstack *obs, int argc, t
 	    fmt--;
 	  continue;
 	}
-      *p++ = c;
-      *p = '\0';
 
       /* Specifiers.  We don't yet recognize C, S, n, or p.  */
       switch (c)
 	{
 	case 'c':
-	  datatype = INT;
+	  datatype = CHAR;
+	  p -= 2; /* %.*c is undefined, so undo the '.*'.  */
 	  break;
 
 	case 's':
@@ -275,9 +272,15 @@ format (struct obstack *obs, int argc, t
 	default:
 	  abort ();
 	}
+      *p++ = c;
+      *p = '\0';
 
-      switch(datatype)
+      switch (datatype)
 	{
+	case CHAR:
+	  str = xasprintf (fstart, width, ARG_INT(argc, argv));
+	  break;
+
 	case INT:
 	  str = xasprintf (fstart, width, prec, ARG_INT(argc, argv));
 	  break;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.