changeset 8335:2c9de3b6ba5b

New module 'vasnprintf-posix'.
author Bruno Haible <bruno@clisp.org>
date Sun, 04 Mar 2007 23:28:59 +0000
parents a601c63691bf
children 493708637f61
files ChangeLog lib/vasnprintf.c m4/printf.m4 m4/vasnprintf-posix.m4 m4/vasnprintf.m4 modules/vasnprintf-posix
diffstat 6 files changed, 918 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2007-03-04  Bruno Haible  <bruno@clisp.org>
+
+	* modules/vasnprintf-posix: New file.
+	* lib/vasnprintf.c: Include isnan.h, isnanl.h, printf-frexp.h,
+	printf-frexpl.h.
+	(VASNPRINTF): Handle the 'a' and 'A' directives here, if needed.
+	* m4/vasnprintf.m4 (gl_REPLACE_VASNPRINTF): New macro, extracted from
+	gl_FUNC_VASNPRINTF.
+	(gl_FUNC_VASNPRINTF): Invoke it.
+	* m4/vasnprintf-posix.m4: New file.
+	* m4/printf.m4: New file.
+
 2007-03-04  Bruno Haible  <bruno@clisp.org>
 
 	Compile progreloc.c only if --enable-relocatable is specified.
--- a/lib/vasnprintf.c
+++ b/lib/vasnprintf.c
@@ -49,6 +49,15 @@
 /* Checked size_t computations.  */
 #include "xsize.h"
 
+#if NEED_PRINTF_DIRECTIVE_A && !defined IN_LIBINTL
+# include "isnan.h"
+# include "isnanl.h"
+# if HAVE_LONG_DOUBLE
+#  include "printf-frexp.h"
+#  include "printf-frexpl.h"
+# endif
+#endif
+
 #if HAVE_WCHAR_T
 # if HAVE_WCSLEN
 #  define local_wcslen wcslen
@@ -257,6 +266,472 @@
 		    abort ();
 		  }
 	      }
+#if NEED_PRINTF_DIRECTIVE_A && !defined IN_LIBINTL
+	    else if (dp->conversion == 'a' || dp->conversion == 'A')
+	      {
+		arg_type type = a.arg[dp->arg_index].type;
+		int flags = dp->flags;
+		int has_width;
+		size_t width;
+		int has_precision;
+		size_t precision;
+		size_t tmp_length;
+		CHAR_T tmpbuf[700];
+		CHAR_T *tmp;
+		CHAR_T *pad_ptr;
+		CHAR_T *p;
+
+		has_width = 0;
+		width = 0;
+		if (dp->width_start != dp->width_end)
+		  {
+		    if (dp->width_arg_index != ARG_NONE)
+		      {
+			int arg;
+
+			if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
+			  abort ();
+			arg = a.arg[dp->width_arg_index].a.a_int;
+			if (arg < 0)
+			  {
+			    /* "A negative field width is taken as a '-' flag
+			        followed by a positive field width."  */
+			    flags |= FLAG_LEFT;
+			    width = (unsigned int) (-arg);
+			  }
+			else
+			  width = arg;
+		      }
+		    else
+		      {
+			const CHAR_T *digitp = dp->width_start;
+
+			do
+			  width = xsum (xtimes (width, 10), *digitp++ - '0');
+			while (digitp != dp->width_end);
+		      }
+		    has_width = 1;
+		  }
+
+		has_precision = 0;
+		precision = 0;
+		if (dp->precision_start != dp->precision_end)
+		  {
+		    if (dp->precision_arg_index != ARG_NONE)
+		      {
+			int arg;
+
+			if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
+			  abort ();
+			arg = a.arg[dp->precision_arg_index].a.a_int;
+			/* "A negative precision is taken as if the precision
+			    were omitted."  */
+			if (arg >= 0)
+			  {
+			    precision = arg;
+			    has_precision = 1;
+			  }
+		      }
+		    else
+		      {
+			const CHAR_T *digitp = dp->precision_start + 1;
+
+			precision = 0;
+			while (digitp != dp->precision_end)
+			  precision = xsum (xtimes (precision, 10), *digitp++ - '0');
+			has_precision = 1;
+		      }
+		  }
+
+		/* Allocate a temporary buffer of sufficient size.  */
+# if HAVE_LONG_DOUBLE
+		if (type == TYPE_LONGDOUBLE)
+		  tmp_length =
+		    (unsigned int) ((LDBL_DIG + 1)
+				    * 0.831 /* decimal -> hexadecimal */
+				   )
+		    + 1; /* turn floor into ceil */
+		else
+# endif
+		  tmp_length =
+		    (unsigned int) ((DBL_DIG + 1)
+				    * 0.831 /* decimal -> hexadecimal */
+				   )
+		    + 1; /* turn floor into ceil */
+		if (tmp_length < precision)
+		  tmp_length = precision;
+		/* Account for sign, decimal point etc. */
+		tmp_length = xsum (tmp_length, 12);
+
+		if (tmp_length < width)
+		  tmp_length = width;
+
+		tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */
+
+		if (tmp_length <= sizeof (tmpbuf) / sizeof (CHAR_T))
+		  tmp = tmpbuf;
+		else
+		  {
+		    size_t tmp_memsize = xtimes (tmp_length, sizeof (CHAR_T));
+
+		    if (size_overflow_p (tmp_memsize))
+		      /* Overflow, would lead to out of memory.  */
+		      goto out_of_memory;
+		    tmp = (CHAR_T *) malloc (tmp_memsize);
+		    if (tmp == NULL)
+		      /* Out of memory.  */
+		      goto out_of_memory;
+		  }
+
+		pad_ptr = NULL;
+		p = tmp;
+# if HAVE_LONG_DOUBLE
+		if (type == TYPE_LONGDOUBLE)
+		  {
+		    long double arg = a.arg[dp->arg_index].a.a_longdouble;
+
+		    if (isnanl (arg))
+		      {
+			if (dp->conversion == 'A')
+			  {
+			    *p++ = 'N'; *p++ = 'A'; *p++ = 'N';
+			  }
+			else
+			  {
+			    *p++ = 'n'; *p++ = 'a'; *p++ = 'n';
+			  }
+		      }
+		    else
+		      {
+			int sign = 0;
+
+			if (arg < 0.0L)
+			  {
+			    sign = -1;
+			    arg = -arg;
+			  }
+			else if (arg == 0.0L)
+			  {
+			    /* Distinguish 0.0L and -0.0L.  */
+			    static long double plus_zero = 0.0L;
+			    long double arg_mem = arg;
+			    if (memcmp (&plus_zero, &arg_mem, sizeof (long double)) != 0)
+			      {
+				sign = -1;
+				arg = -arg;
+			      }
+			  }
+
+			if (sign < 0)
+			  *p++ = '-';
+			else if (flags & FLAG_SHOWSIGN)
+			  *p++ = '+';
+			else if (flags & FLAG_SPACE)
+			  *p++ = ' ';
+
+			if (x > 0.0L && x + x == x)
+			  {
+			    if (dp->conversion == 'A')
+			      {
+				*p++ = 'I'; *p++ = 'N'; *p++ = 'F';
+			      }
+			    else
+			      {
+				*p++ = 'i'; *p++ = 'n'; *p++ = 'f';
+			      }
+			  }
+			else
+			  {
+			    int exponent;
+			    long double mantissa;
+
+			    if (x > 0.0L)
+			      mantissa = printf_frexpl (arg, &exponent);
+			    else
+			      {
+				exponent = 0;
+				mantissa = 0.0L;
+			      }
+
+			    if (has_precision
+				&& precision < (unsigned int) ((LDBL_DIG + 1) * 0.831) + 1)
+			      {
+				/* Round the mantissa.  */
+				long double tail = arg;
+				size_t q;
+
+				for (q = precision; ; q--)
+				  {
+				    int digit = (int) tail;
+				    tail -= digit;
+				    if (q == 0)
+				      {
+					if (digit & 1 ? tail >= 0.5L : tail > 0.5L)
+					  tail = 1 - tail;
+					else
+					  tail = 0;
+					break;
+				      }
+				    tail *= 16.0L;
+				  }
+				if (tail > 0.0L)
+				  for (q = precision; q > 0; q--)
+				    tail *= 0.0625L;
+				arg += tail;
+			      }
+
+			    *p++ = '0';
+			    *p++ = dp->conversion - 'A' + 'X';
+			    pad_ptr = p;
+			    {
+			      int digit;
+
+			      digit = (int) arg;
+			      arg -= digit;
+			      *p++ = '0' + digit;
+			      if ((flags & FLAG_ALT) || arg > 0.0L)
+				{
+				  *p++ = '.';
+				  /* This loop terminates because we assume
+				     that FLT_RADIX is a power of 2.  */
+				  while (arg > 0.0L)
+				    {
+				      arg *= 16.0L;
+				      digit = (int) arg;
+				      arg -= digit;
+				      *p++ = digit
+					     + (digit < 10
+						? '0'
+						: dp->conversion - 10);
+				      if (precision > 0)
+					precision--;
+				    }
+				  while (precision > 0)
+				    {
+				      *p++ = '0';
+				      precision--;
+				    }
+				}
+			      }
+			      *p++ = dp->conversion - 'A' + 'P';
+#  if WIDE_CHAR_VERSION
+			      {
+				static const wchar_t decimal_format[] =
+				  { '%', 'd', '\0' };
+				SNPRINTF (p, 6 + 1, decimal_format, exponent);
+			      }
+#  else
+			      sprintf (p, "%d", exponent);
+#  endif
+			      while (*p != '\0')
+				p++;
+			  }
+		      }
+		  }
+		else
+# endif
+		  {
+		    double arg = a.arg[dp->arg_index].a.a_double;
+
+		    if (isnan (arg))
+		      {
+			if (dp->conversion == 'A')
+			  {
+			    *p++ = 'N'; *p++ = 'A'; *p++ = 'N';
+			  }
+			else
+			  {
+			    *p++ = 'n'; *p++ = 'a'; *p++ = 'n';
+			  }
+		      }
+		    else
+		      {
+			int sign = 0;
+
+			if (arg < 0.0)
+			  {
+			    sign = -1;
+			    arg = -arg;
+			  }
+			else if (arg == 0.0)
+			  {
+			    /* Distinguish 0.0 and -0.0.  */
+			    static double plus_zero = 0.0;
+			    double arg_mem = arg;
+			    if (memcmp (&plus_zero, &arg_mem, sizeof (double)) != 0)
+			      {
+				sign = -1;
+				arg = -arg;
+			      }
+			  }
+
+			if (sign < 0)
+			  *p++ = '-';
+			else if (flags & FLAG_SHOWSIGN)
+			  *p++ = '+';
+			else if (flags & FLAG_SPACE)
+			  *p++ = ' ';
+
+			if (x > 0.0 && x + x == x)
+			  {
+			    if (dp->conversion == 'A')
+			      {
+				*p++ = 'I'; *p++ = 'N'; *p++ = 'F';
+			      }
+			    else
+			      {
+				*p++ = 'i'; *p++ = 'n'; *p++ = 'f';
+			      }
+			  }
+			else
+			  {
+			    int exponent;
+			    double mantissa;
+
+			    if (x > 0.0)
+			      mantissa = printf_frexp (arg, &exponent);
+			    else
+			      {
+				exponent = 0;
+				mantissa = 0.0;
+			      }
+
+			    if (has_precision
+				&& precision < (unsigned int) ((DBL_DIG + 1) * 0.831) + 1)
+			      {
+				/* Round the mantissa.  */
+				double tail = arg;
+				size_t q;
+
+				for (q = precision; ; q--)
+				  {
+				    int digit = (int) tail;
+				    tail -= digit;
+				    if (q == 0)
+				      {
+					if (digit & 1 ? tail >= 0.5 : tail > 0.5)
+					  tail = 1 - tail;
+					else
+					  tail = 0;
+					break;
+				      }
+				    tail *= 16.0;
+				  }
+				if (tail > 0.0)
+				  for (q = precision; q > 0; q--)
+				    tail *= 0.0625;
+				arg += tail;
+			      }
+
+			    *p++ = '0';
+			    *p++ = dp->conversion - 'A' + 'X';
+			    pad_ptr = p;
+			    {
+			      int digit;
+
+			      digit = (int) arg;
+			      arg -= digit;
+			      *p++ = '0' + digit;
+			      if ((flags & FLAG_ALT) || arg > 0.0)
+				{
+				  *p++ = '.';
+				  /* This loop terminates because we assume
+				     that FLT_RADIX is a power of 2.  */
+				  while (arg > 0.0)
+				    {
+				      arg *= 16.0;
+				      digit = (int) arg;
+				      arg -= digit;
+				      *p++ = digit
+					     + (digit < 10
+						? '0'
+						: dp->conversion - 10);
+				      if (precision > 0)
+					precision--;
+				    }
+				  while (precision > 0)
+				    {
+				      *p++ = '0';
+				      precision--;
+				    }
+				}
+			      }
+			      *p++ = dp->conversion - 'A' + 'P';
+# if WIDE_CHAR_VERSION
+			      {
+				static const wchar_t decimal_format[] =
+				  { '%', 'd', '\0' };
+				SNPRINTF (p, 6 + 1, decimal_format, exponent);
+			      }
+# else
+			      sprintf (p, "%d", exponent);
+# endif
+			      while (*p != '\0')
+				p++;
+			  }
+		      }
+		  }
+		/* The generated string now extends from tmp to p, with the
+		   zero padding insertion point being at pad_ptr.  */
+		if (has_width && p - tmp < width)
+		  {
+		    size_t pad = width - (p - tmp);
+		    CHAR_T *end = p + pad;
+
+		    if (flags & FLAG_LEFT)
+		      {
+			/* Pad with spaces on the right.  */
+			for (; pad > 0; pad--)
+			  *p++ = ' ';
+		      }
+		    else if ((flags & FLAG_ZERO) && pad_ptr != NULL)
+		      {
+			/* Pad with zeroes.  */
+			CHAR_T *q = end;
+
+			while (p > pad_ptr)
+			  *--q = *--p;
+			for (; pad > 0; pad--)
+			  *p++ = '0';
+		      }
+		    else
+		      {
+			/* Pad with spaces on the left.  */
+			CHAR_T *q = end;
+
+			while (p > tmp)
+			  *--q = *--p;
+			for (; pad > 0; pad--)
+			  *p++ = ' ';
+		      }
+
+		    p = end;
+		  }
+
+		{
+		  size_t count = p - tmp;
+
+		  if (count >= tmp_length)
+		    /* tmp_length was incorrectly calculated - fix the
+		       code above!  */
+		    abort ();
+
+		  /* Make room for the result.  */
+		  if (count >= allocated - length)
+		    {
+		      size_t n = xsum (length, count);
+
+		      ENSURE_ALLOCATION (n);
+		    }
+
+		  /* Append the result.  */
+		  memcpy (result + length, tmp, count * sizeof (CHAR_T));
+		  if (tmp != tmpbuf)
+		    free (tmp);
+		  length += count;
+		}
+	      }
+#endif
 	    else
 	      {
 		arg_type type = a.arg[dp->arg_index].type;
new file mode 100644
--- /dev/null
+++ b/m4/printf.m4
@@ -0,0 +1,357 @@
+# printf.m4 serial 1
+dnl Copyright (C) 2003, 2007 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 Test whether the *printf family of functions supports the 'j', 'z', 't',
+dnl 'L' size specifiers. (ISO C99, POSIX:2001)
+dnl Result is gl_cv_func_printf_sizes_c99.
+
+AC_DEFUN([gl_PRINTF_SIZES_C99],
+[
+  AC_REQUIRE([AC_PROG_CC])
+  AC_REQUIRE([gl_AC_HEADER_STDINT_H])
+  AC_REQUIRE([gl_AC_HEADER_INTTYPES_H])
+  AC_REQUIRE([gt_TYPE_LONGDOUBLE])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+  AC_CACHE_CHECK([whether printf supports size specifiers as in C99],
+    [gl_cv_func_printf_sizes_c99], 
+    [
+      AC_TRY_RUN([
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#if HAVE_STDINT_H_WITH_UINTMAX
+# include <stdint.h>
+#endif
+#if HAVE_INTTYPES_H_WITH_UINTMAX
+# include <inttypes.h>
+#endif
+static char buf[100];
+int main ()
+{
+#if HAVE_STDINT_H_WITH_UINTMAX || HAVE_INTTYPES_H_WITH_UINTMAX
+  buf[0] = '\0';
+  if (sprintf (buf, "%ju %d", (uintmax_t) 12345671, 33, 44, 55) < 0
+      || strcmp (buf, "12345671 33") != 0)
+    return 1;
+#endif
+  buf[0] = '\0';
+  if (sprintf (buf, "%zu %d", (size_t) 12345672, 33, 44, 55) < 0
+      || strcmp (buf, "12345672 33") != 0)
+    return 1;
+  buf[0] = '\0';
+  if (sprintf (buf, "%tu %d", (ptrdiff_t) 12345673, 33, 44, 55) < 0
+      || strcmp (buf, "12345673 33") != 0)
+    return 1;
+#if HAVE_LONG_DOUBLE
+  buf[0] = '\0';
+  if (sprintf (buf, "%Lg %d", (long double) 1.5, 33, 44, 55) < 0
+      || strcmp (buf, "1.5 33") != 0)
+    return 1;
+#endif
+  return 0;
+}], [gl_cv_func_printf_sizes_c99=yes], [gl_cv_func_printf_sizes_c99=no],
+      [
+changequote(,)dnl
+       case "$host_os" in
+                               dnl Guess yes on glibc systems.
+         *-gnu*)               gl_cv_func_printf_sizes_c99="guessing yes";;
+                               dnl Guess yes on FreeBSD >= 5.
+         freebsd[1-4]*)        gl_cv_func_printf_sizes_c99="guessing no";;
+         freebsd* | kfreebsd*) gl_cv_func_printf_sizes_c99="guessing yes";;
+                               dnl Gusss yes on MacOS X >= 10.3.
+         darwin[1-6].*)        gl_cv_func_printf_sizes_c99="guessing no";;
+         darwin*)              gl_cv_func_printf_sizes_c99="guessing yes";;
+                               dnl Guess yes on Solaris >= 2.10.
+         solaris2.[0-9]*)      gl_cv_func_printf_sizes_c99="guessing no";;
+         solaris*)             gl_cv_func_printf_sizes_c99="guessing yes";;
+                               dnl Guess yes on NetBSD >= 3.
+         netbsd[1-2]*)         gl_cv_func_printf_sizes_c99="guessing no";;
+         netbsd*)              gl_cv_func_printf_sizes_c99="guessing yes";;
+                               dnl If we don't know, assume the worst.
+         *)                    gl_cv_func_printf_sizes_c99="guessing no";;
+       esac
+changequote([,])dnl
+      ])
+    ])
+])
+
+dnl Test whether the *printf family of functions supports the 'a' and 'A'
+dnl conversion specifier for hexadecimal output of floating-point numbers.
+dnl (ISO C99, POSIX:2001)
+dnl Result is gl_cv_func_printf_directive_a.
+
+AC_DEFUN([gl_PRINTF_DIRECTIVE_A],
+[
+  AC_REQUIRE([AC_PROG_CC])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+  AC_CACHE_CHECK([whether printf supports the 'a' and 'A' directives],
+    [gl_cv_func_printf_directive_a], 
+    [
+      AC_TRY_RUN([
+#include <stdio.h>
+#include <string.h>
+static char buf[100];
+int main ()
+{
+  if (sprintf (buf, "%a %d", 3.1416015625, 33, 44, 55) < 0
+      || strcmp (buf, "0x1.922p+1 33") != 0)
+    return 1;
+  if (sprintf (buf, "%A %d", -3.1416015625, 33, 44, 55) < 0
+      || strcmp (buf, "-0X1.922P+1 33") != 0)
+    return 1;
+  return 0;
+}], [gl_cv_func_printf_directive_a=yes], [gl_cv_func_printf_directive_a=no],
+      [
+changequote(,)dnl
+       case "$host_os" in
+                               dnl Guess yes on glibc systems.
+         *-gnu*)               gl_cv_func_printf_directive_a="guessing yes";;
+                               dnl Guess yes on FreeBSD >= 5.
+         freebsd[1-4]*)        gl_cv_func_printf_directive_a="guessing no";;
+         freebsd* | kfreebsd*) gl_cv_func_printf_directive_a="guessing yes";;
+                               dnl Gusss yes on MacOS X >= 10.3.
+         darwin[1-6].*)        gl_cv_func_printf_directive_a="guessing no";;
+         darwin*)              gl_cv_func_printf_directive_a="guessing yes";;
+                               dnl If we don't know, assume the worst.
+         *)                    gl_cv_func_printf_directive_a="guessing no";;
+       esac
+changequote([,])dnl
+      ])
+    ])
+])
+
+dnl Test whether the *printf family of functions supports the %n format
+dnl directive. (ISO C99, POSIX:2001)
+dnl Result is gl_cv_func_printf_directive_n.
+
+AC_DEFUN([gl_PRINTF_DIRECTIVE_N],
+[
+  AC_REQUIRE([AC_PROG_CC])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+  AC_CACHE_CHECK([whether printf supports the 'n' directive],
+    [gl_cv_func_printf_directive_n], 
+    [
+      AC_TRY_RUN([
+#include <stdio.h>
+#include <string.h>
+static char buf[100];
+int main ()
+{
+  int count = -1;
+  if (sprintf (buf, "%d %n", 123, &count, 33, 44, 55) < 0
+      || strcmp (buf, "123 ") != 0
+      || count != 4)
+    return 1;
+  return 0;
+}], [gl_cv_func_printf_directive_n=yes], [gl_cv_func_printf_directive_n=no],
+      [
+changequote(,)dnl
+       case "$host_os" in
+         hpux*) gl_cv_func_printf_directive_n="guessing no";;
+         *)     gl_cv_func_printf_directive_n="guessing yes";;
+       esac
+changequote([,])dnl
+      ])
+    ])
+])
+
+dnl Test whether the *printf family of functions supports POSIX/XSI format
+dnl strings with positions. (POSIX:2001)
+dnl Result is gl_cv_func_printf_positions.
+
+AC_DEFUN([gl_PRINTF_POSITIONS],
+[
+  AC_REQUIRE([AC_PROG_CC])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+  AC_CACHE_CHECK([whether printf supports POSIX/XSI format strings with positions],
+    [gl_cv_func_printf_positions], 
+    [
+      AC_TRY_RUN([
+#include <stdio.h>
+#include <string.h>
+/* The string "%2$d %1$d", with dollar characters protected from the shell's
+   dollar expansion (possibly an autoconf bug).  */
+static char format[] = { '%', '2', '$', 'd', ' ', '%', '1', '$', 'd', '\0' };
+static char buf[100];
+int main ()
+{
+  sprintf (buf, format, 33, 55);
+  return (strcmp (buf, "55 33") != 0);
+}], [gl_cv_func_printf_positions=yes], [gl_cv_func_printf_positions=no],
+      [
+changequote(,)dnl
+       case "$host_os" in
+         netbsd*)      gl_cv_func_printf_positions="guessing no";;
+         beos*)        gl_cv_func_printf_positions="guessing no";;
+         mingw* | pw*) gl_cv_func_printf_positions="guessing no";;
+         *)            gl_cv_func_printf_positions="guessing yes";;
+       esac
+changequote([,])dnl
+      ])
+    ])
+])
+
+dnl Test whether the snprintf function exists. (ISO C99, POSIX:2001)
+dnl Result is ac_cv_func_snprintf.
+
+AC_DEFUN([gl_SNPRINTF_PRESENCE],
+[
+  AC_CHECK_FUNCS_ONCE([snprintf])
+])
+
+dnl Test whether the string produced by the snprintf function is always NUL
+dnl terminated. (ISO C99, POSIX:2001)
+dnl Result is gl_cv_func_snprintf_truncation_c99.
+
+AC_DEFUN([gl_SNPRINTF_TRUNCATION_C99],
+[
+  AC_REQUIRE([AC_PROG_CC])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+  AC_CACHE_CHECK([whether snprintf truncates the result as in C99],
+    [gl_cv_func_snprintf_truncation_c99], 
+    [
+      AC_TRY_RUN([
+#include <stdio.h>
+#include <string.h>
+static char buf[100];
+int main ()
+{
+  strcpy (buf, "ABCDEF");
+  snprintf (buf, 3, "%d %d", 4567, 89);
+  if (memcmp (buf, "45\0DEF", 6) != 0)
+    return 1;
+  return 0;
+}], [gl_cv_func_snprintf_truncation_c99=yes], [gl_cv_func_snprintf_truncation_c99=no],
+      [
+changequote(,)dnl
+       case "$host_os" in
+                               dnl Guess yes on glibc systems.
+         *-gnu*)               gl_cv_func_snprintf_truncation_c99="guessing yes";;
+                               dnl Guess yes on FreeBSD >= 5.
+         freebsd[1-4]*)        gl_cv_func_snprintf_truncation_c99="guessing no";;
+         freebsd* | kfreebsd*) gl_cv_func_snprintf_truncation_c99="guessing yes";;
+                               dnl Gusss yes on MacOS X >= 10.3.
+         darwin[1-6].*)        gl_cv_func_snprintf_truncation_c99="guessing no";;
+         darwin*)              gl_cv_func_snprintf_truncation_c99="guessing yes";;
+                               dnl Guess yes on Solaris >= 2.6.
+         solaris2.[0-5]*)      gl_cv_func_snprintf_truncation_c99="guessing no";;
+         solaris*)             gl_cv_func_snprintf_truncation_c99="guessing yes";;
+                               dnl Guess yes on AIX >= 4.
+         aix[1-3]*)            gl_cv_func_snprintf_truncation_c99="guessing no";;
+         aix*)                 gl_cv_func_snprintf_truncation_c99="guessing yes";;
+                               dnl Guess yes on HP-UX >= 11.
+         hpux[7-9]* | hpux10*) gl_cv_func_snprintf_truncation_c99="guessing no";;
+         hpux*)                gl_cv_func_snprintf_truncation_c99="guessing yes";;
+                               dnl Guess yes on IRIX >= 6.5.
+         irix6.5)              gl_cv_func_snprintf_truncation_c99="guessing yes";;
+                               dnl Guess yes on OSF/1 >= 5.
+         osf[3-4]*)            gl_cv_func_snprintf_truncation_c99="guessing no";;
+         osf*)                 gl_cv_func_snprintf_truncation_c99="guessing yes";;
+                               dnl Guess yes on NetBSD >= 3.
+         netbsd[1-2]*)         gl_cv_func_snprintf_truncation_c99="guessing no";;
+         netbsd*)              gl_cv_func_snprintf_truncation_c99="guessing yes";;
+                               dnl Guess yes on BeOS.
+         beos*)                gl_cv_func_snprintf_truncation_c99="guessing yes";;
+                               dnl If we don't know, assume the worst.
+         *)                    gl_cv_func_snprintf_truncation_c99="guessing no";;
+       esac
+changequote([,])dnl
+      ])
+    ])
+])
+
+dnl Test whether the return value of the snprintf function is the number
+dnl of bytes (excluding the terminating NUL) that would have been produced
+dnl if the buffer had been large enough. (ISO C99, POSIX:2001)
+dnl Result is gl_cv_func_printf_retval_c99.
+
+AC_DEFUN([gl_SNPRINTF_RETVAL_C99],
+[
+  AC_REQUIRE([AC_PROG_CC])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+  AC_CACHE_CHECK([whether snprintf returns a byte count as in C99],
+    [gl_cv_func_printf_retval_c99], 
+    [
+      AC_TRY_RUN([
+#include <stdio.h>
+#include <string.h>
+static char buf[100];
+int main ()
+{
+  strcpy (buf, "ABCDEF");
+  if (snprintf (buf, 3, "%d %d", 4567, 89) != 7)
+    return 1;
+  return 0;
+}], [gl_cv_func_printf_retval_c99=yes], [gl_cv_func_printf_retval_c99=no],
+      [
+changequote(,)dnl
+       case "$host_os" in
+                               dnl Guess yes on glibc systems.
+         *-gnu*)               gl_cv_func_printf_retval_c99="guessing yes";;
+                               dnl Guess yes on FreeBSD >= 5.
+         freebsd[1-4]*)        gl_cv_func_printf_retval_c99="guessing no";;
+         freebsd* | kfreebsd*) gl_cv_func_printf_retval_c99="guessing yes";;
+                               dnl Gusss yes on MacOS X >= 10.3.
+         darwin[1-6].*)        gl_cv_func_printf_retval_c99="guessing no";;
+         darwin*)              gl_cv_func_printf_retval_c99="guessing yes";;
+                               dnl Guess yes on Solaris >= 2.6.
+         solaris2.[0-5]*)      gl_cv_func_printf_retval_c99="guessing no";;
+         solaris*)             gl_cv_func_printf_retval_c99="guessing yes";;
+                               dnl Guess yes on AIX >= 4.
+         aix[1-3]*)            gl_cv_func_printf_retval_c99="guessing no";;
+         aix*)                 gl_cv_func_printf_retval_c99="guessing yes";;
+                               dnl Guess yes on NetBSD >= 3.
+         netbsd[1-2]*)         gl_cv_func_printf_retval_c99="guessing no";;
+         netbsd*)              gl_cv_func_printf_retval_c99="guessing yes";;
+                               dnl Guess yes on BeOS.
+         beos*)                gl_cv_func_printf_retval_c99="guessing yes";;
+                               dnl If we don't know, assume the worst.
+         *)                    gl_cv_func_printf_retval_c99="guessing no";;
+       esac
+changequote([,])dnl
+      ])
+    ])
+])
+
+dnl The results of these tests on various platforms are:
+dnl
+dnl 1 = gl_PRINTF_SIZES_C99
+dnl 2 = gl_PRINTF_DIRECTIVE_A
+dnl 3 = gl_PRINTF_DIRECTIVE_N
+dnl 4 = gl_PRINTF_POSITIONS
+dnl 5 = gl_SNPRINTF_PRESENCE
+dnl 6 = gl_SNPRINTF_TRUNCATION_C99
+dnl 7 = gl_SNPRINTF_RETVAL_C99
+dnl
+dnl 1 = checking whether printf supports size specifiers as in C99...
+dnl 2 = checking whether printf supports the 'a' and 'A' directives...
+dnl 3 = checking whether printf supports the 'n' directive...
+dnl 4 = checking whether printf supports POSIX/XSI format strings with positions...
+dnl 5 = checking for snprintf...
+dnl 6 = checking whether snprintf truncates the result as in C99...
+dnl 7 = checking whether snprintf returns a byte count as in C99...
+dnl
+dnl . = yes, # = no.
+dnl
+dnl                                   1  2  3  4  5  6  7
+dnl   glibc 2.3.6                     .  .  .  .  .  .  .
+dnl   FreeBSD 5.4, 6.1                .  .  .  .  .  .  .
+dnl   MacOS X 10.3.9                  .  .  .  .  .  .  .
+dnl   Cygwin 2007                     .  #  .  .  .  .  .
+dnl   Solaris 10                      .  #  .  .  .  .  .
+dnl   Solaris 2.6 ... 9               #  #  .  .  .  .  .
+dnl   Solaris 2.5.1                   #  #  .  .  #  #  #
+dnl   AIX 4.3.2, 5.1                  #  #  .  .  .  .  .
+dnl   HP-UX 11.31                     .  #  .  .  .  .  #
+dnl   HP-UX 11.00, 11.11, 11.23       #  #  .  .  .  .  #
+dnl   HP-UX 10.20                     #  #  #  ?  .  ?  #
+dnl   IRIX 6.5                        #  #  .  .  .  .  #
+dnl   OSF/1 5.1                       #  #  .  .  .  .  #
+dnl   OSF/1 4.0d                      #  #  .  .  #  #  #
+dnl   NetBSD 3.0                      .  #  .  #  .  .  .
+dnl   BeOS                            #  #  .  #  .  .  .
+dnl   mingw                           #  #  .  #  .  #  #
new file mode 100644
--- /dev/null
+++ b/m4/vasnprintf-posix.m4
@@ -0,0 +1,29 @@
+# vasnprintf-posix.m4 serial 1
+dnl Copyright (C) 2007 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.
+
+AC_DEFUN([gl_FUNC_VASNPRINTF_POSIX],
+[
+  AC_REQUIRE([gl_EOVERFLOW])
+  AC_REQUIRE([gl_PRINTF_SIZES_C99])
+  AC_REQUIRE([gl_PRINTF_DIRECTIVE_A])
+  AC_REQUIRE([gl_PRINTF_DIRECTIVE_N])
+  AC_REQUIRE([gl_PRINTF_POSITIONS])
+  AC_CHECK_FUNCS([vasnprintf])
+  if expr "$gl_cv_func_printf_sizes_c99" : ".*yes" > /dev/null \
+     && expr "$gl_cv_func_printf_directive_a" : ".*yes" > /dev/null \
+     && expr "$gl_cv_func_printf_directive_n" : ".*yes" > /dev/null \
+     && expr "$gl_cv_func_printf_positions" : ".*yes" > /dev/null \
+     && test $ac_cv_func_vasnprintf = yes; then
+    : # vasnprintf exists and is already POSIX compliant.
+  else
+    if ! expr "$gl_cv_func_printf_directive_a" : ".*yes" > /dev/null; then
+      AC_DEFINE([NEED_PRINTF_DIRECTIVE_A], 1,
+        [Define if the vasnprintf implementation needs special code for
+         the 'a' and 'A' directives.])
+    fi
+    gl_REPLACE_VASNPRINTF
+  fi
+])
--- a/m4/vasnprintf.m4
+++ b/m4/vasnprintf.m4
@@ -1,5 +1,5 @@
-# vasnprintf.m4 serial 7
-dnl Copyright (C) 2002-2004, 2006 Free Software Foundation, Inc.
+# vasnprintf.m4 serial 8
+dnl Copyright (C) 2002-2004, 2006-2007 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.
@@ -7,18 +7,24 @@
 AC_DEFUN([gl_FUNC_VASNPRINTF],
 [
   AC_REQUIRE([gl_EOVERFLOW])
-  AC_REPLACE_FUNCS(vasnprintf)
+  AC_CHECK_FUNCS([vasnprintf])
   if test $ac_cv_func_vasnprintf = no; then
-    AC_LIBOBJ(printf-args)
-    AC_LIBOBJ(printf-parse)
-    AC_LIBOBJ(asnprintf)
-    gl_PREREQ_PRINTF_ARGS
-    gl_PREREQ_PRINTF_PARSE
-    gl_PREREQ_VASNPRINTF
-    gl_PREREQ_ASNPRINTF
+    gl_REPLACE_VASNPRINTF
   fi
 ])
 
+AC_DEFUN([gl_REPLACE_VASNPRINTF],
+[
+  AC_LIBOBJ([vasnprintf])
+  AC_LIBOBJ([printf-args])
+  AC_LIBOBJ([printf-parse])
+  AC_LIBOBJ([asnprintf])
+  gl_PREREQ_PRINTF_ARGS
+  gl_PREREQ_PRINTF_PARSE
+  gl_PREREQ_VASNPRINTF
+  gl_PREREQ_ASNPRINTF
+])
+
 # Prequisites of lib/printf-args.h, lib/printf-args.c.
 AC_DEFUN([gl_PREREQ_PRINTF_ARGS],
 [
new file mode 100644
--- /dev/null
+++ b/modules/vasnprintf-posix
@@ -0,0 +1,29 @@
+Description:
+POSIX compatible vsprintf with automatic memory allocation and bounded output
+size.
+
+Files:
+m4/vasnprintf-posix.m4
+m4/printf.m4
+
+Depends-on:
+vasnprintf
+isnan-nolibm
+isnanl-nolibm
+printf-frexp
+printf-frexpl
+
+configure.ac:
+gl_FUNC_VASNPRINTF_POSIX
+
+Makefile.am:
+
+Include:
+"vasnprintf.h"
+
+License:
+LGPL
+
+Maintainer:
+Bruno Haible
+