snprintf: guarantee %1$d, for libintl
authorEric Blake <eblake@redhat.com>
Fri, 1 Jul 2011 14:20:06 +0000 (08:20 -0600)
committerEric Blake <eblake@redhat.com>
Tue, 5 Jul 2011 16:13:40 +0000 (10:13 -0600)
Newer mingw (but not yet mingw64) provides two flavors of
snprintf: _snprintf defers straight to msvcrt, which has broken
return value and does not understand %llu or %zu; and snprintf,
which fixes these two bugs but does not understand %1$s.

Libintl specifically favors _snprintf, with broken return value,
even when compiled on mingw with a fixed snprintf, because the
only behavior which it wants to fix is %1$s handling.  But this
means that the replacement libintl_snprintf has a broken return.

If one uses the 'snprintf-posix' module, then the gnulib
replacement kicks in, and does everything that libintl needs, so
on mingw, <libintl.h> specifically avoids overriding snprintf if
it detected that gnulib replaced snprintf.  However, if one only
uses the 'snprintf' module and also uses libintl, this means
there are two problems:

1. The gnulib 'snprintf' module does not replace the mingw
snprintf function, because it has proper return values, while the
libintl.h header knows that %1$d is broken so snprintf must be
replaced, with the end result that the application gets the
libintl replacement snprintf with broken return values in spite
of the gnulib module.

2. Conversely, if the application did '#define snprintf snprintf',
that would be enough to make libintl avoid installing its own
replacement because libintl would see the define as a sign that
gnulib is happy with snprintf.  However, if gnulib didn't enforce
%1$s, users can end up with translated strings that break when
passed to the native snprintf.

Happily, the gnulib snprintf replacement already guarantees %1$s
without needing any further preprocessor macros defined, and
without dragging in the LGPLv3+ bulk of snprintf-posix, so the
problem boils down to guaranteeing that gnulib will replace
snprintf if it lacks %1$s support.  Basically, gnulib must
replace snprintf under all the same conditions as libintl, as
well as any other conditions of its own, if the libintl trick
of deferring to gnulib is to work correctly.

* m4/snprintf.m4 (gl_FUNC_SNPRINTF): Require %1$d support.
* m4/vsnprintf.m4 (gl_FUNC_VSNPRINTF): Likewise.
* doc/posix-functions/snprintf.texi (snprintf): Update.
* doc/posix-functions/vsnprintf.texi (vsnprintf): Likewise.
* tests/test-snprintf.c (main): Enhance test.
* tests/test-vsnprintf.c (main): Likewise.

Signed-off-by: Eric Blake <eblake@redhat.com>
ChangeLog
doc/posix-functions/snprintf.texi
doc/posix-functions/vsnprintf.texi
m4/snprintf.m4
m4/vsnprintf.m4
tests/test-snprintf.c
tests/test-vsnprintf.c

index e56cd8066552a79bab3111736f6a7eaccf0175b6..71c1560b6a0e74c6a4d38280ddcf8c028472d153 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2011-07-05  Eric Blake  <eblake@redhat.com>
+
+       snprintf: guarantee %1$d, for libintl
+       * m4/snprintf.m4 (gl_FUNC_SNPRINTF): Require %1$d support.
+       * m4/vsnprintf.m4 (gl_FUNC_VSNPRINTF): Likewise.
+       * doc/posix-functions/snprintf.texi (snprintf): Update.
+       * doc/posix-functions/vsnprintf.texi (vsnprintf): Likewise.
+       * tests/test-snprintf.c (main): Enhance test.
+       * tests/test-vsnprintf.c (main): Likewise.
+
 2011-07-05  Jim Meyering  <meyering@redhat.com>
 
        maint: exempt stdio-read.c and stdio-write.c from the cppi check
index d295cea850e97459717db3735ec79b4a970145e5..4e7a2941bf161353bc4bf21703617f1fbbf733fa 100644 (file)
@@ -12,12 +12,17 @@ Portability problems fixed by either Gnulib module @code{snprintf} or @code{snpr
 This function is missing on some platforms:
 IRIX 5.3, OSF/1 4.0, Solaris 2.5.1.
 @item
-This function does not return a byte count as specified in C99 on some platforms:
+This function does not support format directives that access arguments in an
+arbitrary order, such as @code{"%2$s"}, on some platforms:
+NetBSD 3.0, mingw, BeOS.
+@item
+This function does not return a byte count as specified in C99 on some
+platforms:
 HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 9, mingw.
 @item
 This function overwrites memory even when a size argument of 1 is passed on some
 platforms:
-Linux libc5.
+Linux libc5, BeOS.
 @end itemize
 
 Portability problems fixed by Gnulib module @code{snprintf-posix}:
@@ -50,10 +55,6 @@ This function does not support precisions in the @samp{ls} directive correctly
 on some platforms:
 Solaris 11 2010-11.
 @item
-This function does not support format directives that access arguments in an
-arbitrary order, such as @code{"%2$s"}, on some platforms:
-NetBSD 3.0, mingw, BeOS.
-@item
 This function doesn't support the @code{'} flag on some platforms:
 NetBSD 3.0, Cygwin 1.5.24, mingw.
 @item
@@ -83,10 +84,6 @@ mingw.
 This function does not fully support the @samp{n} directive on some platforms:
 HP-UX 11, mingw.
 @item
-This function overwrites memory when a size = 1 argument is passed on some
-platforms:
-BeOS.
-@item
 This function overwrites memory even when a zero size argument is passed on some
 platforms:
 OSF/1 5.1.
index e391f35e91087779ac869435040e463e946310d8..ca6e0927e8aa672cb26cf948877e1a3d7100a592 100644 (file)
@@ -12,11 +12,16 @@ Portability problems fixed by either Gnulib module @code{vsnprintf} or @code{vsn
 This function is missing on some platforms:
 IRIX 5.3, OSF/1 4.0, Solaris 2.5.1.
 @item
+This function does not support format directives that access arguments in an
+arbitrary order, such as @code{"%2$s"}, on some platforms:
+NetBSD 3.0, mingw, BeOS.
+@item
 This function overwrites memory even when a size argument of 1 is passed on some
 platforms:
-Linux libc5.
+Linux libc5, BeOS.
 @item
-This function does not return a byte count as specified in C99 on some platforms:
+This function does not return a byte count as specified in C99 on some
+platforms:
 HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 9, mingw.
 @end itemize
 
@@ -50,10 +55,6 @@ This function does not support precisions in the @samp{ls} directive correctly
 on some platforms:
 Solaris 11 2010-11.
 @item
-This function does not support format directives that access arguments in an
-arbitrary order, such as @code{"%2$s"}, on some platforms:
-NetBSD 3.0, mingw, BeOS.
-@item
 This function doesn't support the @code{'} flag on some platforms:
 NetBSD 3.0, Cygwin 1.5.24, mingw.
 @item
@@ -83,10 +84,6 @@ mingw.
 This function does not fully support the @samp{n} directive on some platforms:
 HP-UX 11, mingw.
 @item
-This function overwrites memory when a size = 1 argument is passed on some
-platforms:
-BeOS.
-@item
 This function overwrites memory even when a zero size argument is passed on some
 platforms:
 HP-UX 11, OSF/1 5.1.
index 8aa5dbe598fc5fd7fc5f1538eca94aa020c64e43..6f716239cb1be064edcbeaf5a004614549109dfb 100644 (file)
@@ -1,9 +1,13 @@
-# snprintf.m4 serial 5
+# snprintf.m4 serial 6
 dnl Copyright (C) 2002-2004, 2007-2011 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
 dnl with or without modifications, as long as this notice is preserved.
 
+dnl Libintl 0.17 will replace snprintf only if it does not support %1$s,
+dnl but defers to any gnulib snprintf replacements.  Therefore, gnulib
+dnl must guarantee that the decision for replacing snprintf is a superset
+dnl of the reasons checked by libintl.
 AC_DEFUN([gl_FUNC_SNPRINTF],
 [
   AC_REQUIRE([gl_STDIO_H_DEFAULTS])
@@ -16,7 +20,12 @@ AC_DEFUN([gl_FUNC_SNPRINTF],
         gl_SNPRINTF_RETVAL_C99
         case "$gl_cv_func_snprintf_retval_c99" in
           *yes)
-            gl_cv_func_snprintf_usable=yes
+            gl_PRINTF_POSITIONS
+            case "$gl_cv_func_printf_positions" in
+              *yes)
+                gl_cv_func_snprintf_usable=yes
+                ;;
+            esac
             ;;
         esac
         ;;
index e4725e453f4e20630e8df29f8c2eb919e8e1baf0..d6ce1e6a10a7a08461dc651997a1022da6f83beb 100644 (file)
@@ -1,9 +1,13 @@
-# vsnprintf.m4 serial 5
+# vsnprintf.m4 serial 6
 dnl Copyright (C) 2002-2004, 2007-2011 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
 dnl with or without modifications, as long as this notice is preserved.
 
+dnl Libintl 0.17 will replace vsnprintf only if it does not support %1$s,
+dnl but defers to any gnulib vsnprintf replacements.  Therefore, gnulib
+dnl must guarantee that the decision for replacing vsnprintf is a superset
+dnl of the reasons checked by libintl.
 AC_DEFUN([gl_FUNC_VSNPRINTF],
 [
   AC_REQUIRE([gl_STDIO_H_DEFAULTS])
@@ -16,7 +20,12 @@ AC_DEFUN([gl_FUNC_VSNPRINTF],
         gl_SNPRINTF_RETVAL_C99
         case "$gl_cv_func_snprintf_retval_c99" in
           *yes)
-            gl_cv_func_vsnprintf_usable=yes
+            gl_PRINTF_POSITIONS
+            case "$gl_cv_func_printf_positions" in
+              *yes)
+                gl_cv_func_vsnprintf_usable=yes
+                ;;
+            esac
             ;;
         esac
         ;;
index 95a352dc5e5b4c8cc64f7d5547df7e7cc564e998..18efed5185100dde35e7f4d61c8fd4d356bbaef7 100644 (file)
@@ -60,5 +60,13 @@ main (int argc, char *argv[])
         }
     }
 
+  /* Test the support of the POSIX/XSI format strings with positions.  */
+  {
+    char result[100];
+    int retval = snprintf (result, sizeof (result), "%2$d %1$d", 33, 55);
+    ASSERT (strcmp (result, "55 33") == 0);
+    ASSERT (retval == strlen (result));
+  }
+
   return 0;
 }
index 5060836e78533bfbd4b63611e829f62080bcbc60..6711f39b6ab459542a0d0523c970e16844fb3619 100644 (file)
@@ -73,5 +73,13 @@ main (int argc, char *argv[])
         }
     }
 
+  /* Test the support of the POSIX/XSI format strings with positions.  */
+  {
+    char result[100];
+    int retval = my_snprintf (result, sizeof (result), "%2$d %1$d", 33, 55);
+    ASSERT (strcmp (result, "55 33") == 0);
+    ASSERT (retval == strlen (result));
+  }
+
   return 0;
 }