Fix OpenBSD 4.0 <float.h> handling of long double.
[gnulib.git] / lib / printf-parse.c
index ad42e42..e3aa95a 100644 (file)
@@ -1,5 +1,5 @@
 /* Formatted output to strings.
-   Copyright (C) 1999-2000, 2002-2003 Free Software Foundation, Inc.
+   Copyright (C) 1999-2000, 2002-2003, 2006-2007 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation,
-   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
-
-#ifdef HAVE_CONFIG_H
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* This file can be parametrized with the following macros:
+     CHAR_T             The element type of the format string.
+     CHAR_T_ONLY_ASCII  Set to 1 to enable verification that all characters
+                        in the format string are ASCII.
+     DIRECTIVE          Structure denoting a format directive.
+                        Depends on CHAR_T.
+     DIRECTIVES         Structure denoting the set of format directives of a
+                        format string.  Depends on CHAR_T.
+     PRINTF_PARSE       Function that parses a format string.
+                        Depends on CHAR_T.
+     STATIC             Set to 'static' to declare the function static.
+     ENABLE_UNISTDIO    Set to 1 to enable the unistdio extensions.  */
+
+#ifndef PRINTF_PARSE
 # include <config.h>
 #endif
 
 /* Specification.  */
-#if WIDE_CHAR_VERSION
-# include "wprintf-parse.h"
-#else
+#ifndef PRINTF_PARSE
 # include "printf-parse.h"
 #endif
 
+/* Default parameters.  */
+#ifndef PRINTF_PARSE
+# define PRINTF_PARSE printf_parse
+# define CHAR_T char
+# define DIRECTIVE char_directive
+# define DIRECTIVES char_directives
+#endif
+
 /* Get size_t, NULL.  */
 #include <stddef.h>
 
 /* Get intmax_t.  */
-#if HAVE_STDINT_H_WITH_UINTMAX
+#if defined IN_LIBINTL || defined IN_LIBASPRINTF
+# if HAVE_STDINT_H_WITH_UINTMAX
+#  include <stdint.h>
+# endif
+# if HAVE_INTTYPES_H_WITH_UINTMAX
+#  include <inttypes.h>
+# endif
+#else
 # include <stdint.h>
 #endif
-#if HAVE_INTTYPES_H_WITH_UINTMAX
-# include <inttypes.h>
-#endif
 
 /* malloc(), realloc(), free().  */
 #include <stdlib.h>
 
-#if WIDE_CHAR_VERSION
-# define PRINTF_PARSE wprintf_parse
-# define CHAR_T wchar_t
-# define DIRECTIVE wchar_t_directive
-# define DIRECTIVES wchar_t_directives
-#else
-# define PRINTF_PARSE printf_parse
-# define CHAR_T char
-# define DIRECTIVE char_directive
-# define DIRECTIVES char_directives
+/* errno.  */
+#include <errno.h>
+
+/* Checked size_t computations.  */
+#include "xsize.h"
+
+#if CHAR_T_ONLY_ASCII
+/* c_isascii().  */
+# include "c-ctype.h"
 #endif
 
 #ifdef STATIC
@@ -59,18 +81,18 @@ int
 PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
 {
   const CHAR_T *cp = format;           /* pointer into format */
-  int arg_posn = 0;            /* number of regular arguments consumed */
-  unsigned int d_allocated;            /* allocated elements of d->dir */
-  unsigned int a_allocated;            /* allocated elements of a->arg */
-  unsigned int max_width_length = 0;
-  unsigned int max_precision_length = 0;
+  size_t arg_posn = 0;         /* number of regular arguments consumed */
+  size_t d_allocated;                  /* allocated elements of d->dir */
+  size_t a_allocated;                  /* allocated elements of a->arg */
+  size_t max_width_length = 0;
+  size_t max_precision_length = 0;
 
   d->count = 0;
   d_allocated = 1;
-  d->dir = malloc (d_allocated * sizeof (DIRECTIVE));
+  d->dir = (DIRECTIVE *) malloc (d_allocated * sizeof (DIRECTIVE));
   if (d->dir == NULL)
     /* Out of memory.  */
-    return -1;
+    goto out_of_memory_1;
 
   a->count = 0;
   a_allocated = 0;
@@ -78,19 +100,25 @@ PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
 
 #define REGISTER_ARG(_index_,_type_) \
   {                                                                    \
-    unsigned int n = (_index_);                                                \
+    size_t n = (_index_);                                              \
     if (n >= a_allocated)                                              \
       {                                                                        \
+       size_t memory_size;                                             \
        argument *memory;                                               \
-       a_allocated = 2 * a_allocated;                                  \
+                                                                       \
+       a_allocated = xtimes (a_allocated, 2);                          \
        if (a_allocated <= n)                                           \
-         a_allocated = n + 1;                                          \
-       memory = (a->arg                                                \
-                 ? realloc (a->arg, a_allocated * sizeof (argument))   \
-                 : malloc (a_allocated * sizeof (argument)));          \
+         a_allocated = xsum (n, 1);                                    \
+       memory_size = xtimes (a_allocated, sizeof (argument));          \
+       if (size_overflow_p (memory_size))                              \
+         /* Overflow, would lead to out of memory.  */                 \
+         goto out_of_memory;                                           \
+       memory = (argument *) (a->arg                                   \
+                              ? realloc (a->arg, memory_size)          \
+                              : malloc (memory_size));                 \
        if (memory == NULL)                                             \
          /* Out of memory.  */                                         \
-         goto error;                                                   \
+         goto out_of_memory;                                           \
        a->arg = memory;                                                \
       }                                                                        \
     while (a->count <= n)                                              \
@@ -107,19 +135,19 @@ PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
       CHAR_T c = *cp++;
       if (c == '%')
        {
-         int arg_index = -1;
-         DIRECTIVE *dp = &d->dir[d->count];/* pointer to next directive */
+         size_t arg_index = ARG_NONE;
+         DIRECTIVE *dp = &d->dir[d->count]; /* pointer to next directive */
 
          /* Initialize the next directive.  */
          dp->dir_start = cp - 1;
          dp->flags = 0;
          dp->width_start = NULL;
          dp->width_end = NULL;
-         dp->width_arg_index = -1;
+         dp->width_arg_index = ARG_NONE;
          dp->precision_start = NULL;
          dp->precision_end = NULL;
-         dp->precision_arg_index = -1;
-         dp->arg_index = -1;
+         dp->precision_arg_index = ARG_NONE;
+         dp->arg_index = ARG_NONE;
 
          /* Test for positional argument.  */
          if (*cp >= '0' && *cp <= '9')
@@ -130,13 +158,16 @@ PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
                ;
              if (*np == '$')
                {
-                 unsigned int n = 0;
+                 size_t n = 0;
 
                  for (np = cp; *np >= '0' && *np <= '9'; np++)
-                   n = 10 * n + (*np - '0');
+                   n = xsum (xtimes (n, 10), *np - '0');
                  if (n == 0)
                    /* Positional argument 0.  */
                    goto error;
+                 if (size_overflow_p (n))
+                   /* n too large, would lead to out of memory later.  */
+                   goto error;
                  arg_index = n - 1;
                  cp = np + 1;
                }
@@ -197,24 +228,32 @@ PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
                    ;
                  if (*np == '$')
                    {
-                     unsigned int n = 0;
+                     size_t n = 0;
 
                      for (np = cp; *np >= '0' && *np <= '9'; np++)
-                       n = 10 * n + (*np - '0');
+                       n = xsum (xtimes (n, 10), *np - '0');
                      if (n == 0)
                        /* Positional argument 0.  */
                        goto error;
+                     if (size_overflow_p (n))
+                       /* n too large, would lead to out of memory later.  */
+                       goto error;
                      dp->width_arg_index = n - 1;
                      cp = np + 1;
                    }
                }
-             if (dp->width_arg_index < 0)
-               dp->width_arg_index = arg_posn++;
+             if (dp->width_arg_index == ARG_NONE)
+               {
+                 dp->width_arg_index = arg_posn++;
+                 if (dp->width_arg_index == ARG_NONE)
+                   /* arg_posn wrapped around.  */
+                   goto error;
+               }
              REGISTER_ARG (dp->width_arg_index, TYPE_INT);
            }
          else if (*cp >= '0' && *cp <= '9')
            {
-             unsigned int width_length;
+             size_t width_length;
 
              dp->width_start = cp;
              for (; *cp >= '0' && *cp <= '9'; cp++)
@@ -246,24 +285,33 @@ PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
                        ;
                      if (*np == '$')
                        {
-                         unsigned int n = 0;
+                         size_t n = 0;
 
                          for (np = cp; *np >= '0' && *np <= '9'; np++)
-                           n = 10 * n + (*np - '0');
+                           n = xsum (xtimes (n, 10), *np - '0');
                          if (n == 0)
                            /* Positional argument 0.  */
                            goto error;
+                         if (size_overflow_p (n))
+                           /* n too large, would lead to out of memory
+                              later.  */
+                           goto error;
                          dp->precision_arg_index = n - 1;
                          cp = np + 1;
                        }
                    }
-                 if (dp->precision_arg_index < 0)
-                   dp->precision_arg_index = arg_posn++;
+                 if (dp->precision_arg_index == ARG_NONE)
+                   {
+                     dp->precision_arg_index = arg_posn++;
+                     if (dp->precision_arg_index == ARG_NONE)
+                       /* arg_posn wrapped around.  */
+                       goto error;
+                   }
                  REGISTER_ARG (dp->precision_arg_index, TYPE_INT);
                }
              else
                {
-                 unsigned int precision_length;
+                 size_t precision_length;
 
                  dp->precision_start = cp - 1;
                  for (; *cp >= '0' && *cp <= '9'; cp++)
@@ -299,7 +347,6 @@ PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
                      flags += 8;
                      cp++;
                    }
-#ifdef HAVE_INTMAX_T
                  else if (*cp == 'j')
                    {
                      if (sizeof (intmax_t) > sizeof (long))
@@ -314,7 +361,6 @@ PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
                        }
                      cp++;
                    }
-#endif
                  else if (*cp == 'z' || *cp == 'Z')
                    {
                      /* 'z' is standardized in ISO C 99, but glibc uses 'Z'
@@ -355,11 +401,14 @@ PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
              switch (c)
                {
                case 'd': case 'i':
-#ifdef HAVE_LONG_LONG
+#if HAVE_LONG_LONG_INT
+                 /* If 'long long' exists and is larger than 'long':  */
                  if (flags >= 16 || (flags & 4))
                    type = TYPE_LONGLONGINT;
                  else
 #endif
+                 /* If 'long long' exists and is the same as 'long', we parse
+                    "lld" into TYPE_LONGINT.  */
                  if (flags >= 8)
                    type = TYPE_LONGINT;
                  else if (flags & 2)
@@ -370,11 +419,14 @@ PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
                    type = TYPE_INT;
                  break;
                case 'o': case 'u': case 'x': case 'X':
-#ifdef HAVE_LONG_LONG
+#if HAVE_LONG_LONG_INT
+                 /* If 'long long' exists and is larger than 'long':  */
                  if (flags >= 16 || (flags & 4))
                    type = TYPE_ULONGLONGINT;
                  else
 #endif
+                 /* If 'unsigned long long' exists and is the same as
+                    'unsigned long', we parse "llu" into TYPE_ULONGINT.  */
                  if (flags >= 8)
                    type = TYPE_ULONGINT;
                  else if (flags & 2)
@@ -386,16 +438,14 @@ PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
                  break;
                case 'f': case 'F': case 'e': case 'E': case 'g': case 'G':
                case 'a': case 'A':
-#ifdef HAVE_LONG_DOUBLE
                  if (flags >= 16 || (flags & 4))
                    type = TYPE_LONGDOUBLE;
                  else
-#endif
-                 type = TYPE_DOUBLE;
+                   type = TYPE_DOUBLE;
                  break;
                case 'c':
                  if (flags >= 8)
-#ifdef HAVE_WINT_T
+#if HAVE_WINT_T
                    type = TYPE_WIDE_CHAR;
 #else
                    goto error;
@@ -403,7 +453,7 @@ PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
                  else
                    type = TYPE_CHAR;
                  break;
-#ifdef HAVE_WINT_T
+#if HAVE_WINT_T
                case 'C':
                  type = TYPE_WIDE_CHAR;
                  c = 'c';
@@ -411,7 +461,7 @@ PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
 #endif
                case 's':
                  if (flags >= 8)
-#ifdef HAVE_WCHAR_T
+#if HAVE_WCHAR_T
                    type = TYPE_WIDE_STRING;
 #else
                    goto error;
@@ -419,7 +469,7 @@ PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
                  else
                    type = TYPE_STRING;
                  break;
-#ifdef HAVE_WCHAR_T
+#if HAVE_WCHAR_T
                case 'S':
                  type = TYPE_WIDE_STRING;
                  c = 's';
@@ -429,11 +479,14 @@ PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
                  type = TYPE_POINTER;
                  break;
                case 'n':
-#ifdef HAVE_LONG_LONG
+#if HAVE_LONG_LONG_INT
+                 /* If 'long long' exists and is larger than 'long':  */
                  if (flags >= 16 || (flags & 4))
                    type = TYPE_COUNT_LONGLONGINT_POINTER;
                  else
 #endif
+                 /* If 'long long' exists and is the same as 'long', we parse
+                    "lln" into TYPE_COUNT_LONGINT_POINTER.  */
                  if (flags >= 8)
                    type = TYPE_COUNT_LONGINT_POINTER;
                  else if (flags & 2)
@@ -443,6 +496,17 @@ PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
                  else
                    type = TYPE_COUNT_INT_POINTER;
                  break;
+#if ENABLE_UNISTDIO
+               /* The unistdio extensions.  */
+               case 'U':
+                 if (flags >= 16)
+                   type = TYPE_U32_STRING;
+                 else if (flags >= 8)
+                   type = TYPE_U16_STRING;
+                 else
+                   type = TYPE_U8_STRING;
+                 break;
+#endif
                case '%':
                  type = TYPE_NONE;
                  break;
@@ -455,8 +519,13 @@ PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
            if (type != TYPE_NONE)
              {
                dp->arg_index = arg_index;
-               if (dp->arg_index < 0)
-                 dp->arg_index = arg_posn++;
+               if (dp->arg_index == ARG_NONE)
+                 {
+                   dp->arg_index = arg_posn++;
+                   if (dp->arg_index == ARG_NONE)
+                     /* arg_posn wrapped around.  */
+                     goto error;
+                 }
                REGISTER_ARG (dp->arg_index, type);
              }
            dp->conversion = c;
@@ -466,16 +535,28 @@ PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
          d->count++;
          if (d->count >= d_allocated)
            {
+             size_t memory_size;
              DIRECTIVE *memory;
 
-             d_allocated = 2 * d_allocated;
-             memory = realloc (d->dir, d_allocated * sizeof (DIRECTIVE));
+             d_allocated = xtimes (d_allocated, 2);
+             memory_size = xtimes (d_allocated, sizeof (DIRECTIVE));
+             if (size_overflow_p (memory_size))
+               /* Overflow, would lead to out of memory.  */
+               goto out_of_memory;
+             memory = (DIRECTIVE *) realloc (d->dir, memory_size);
              if (memory == NULL)
                /* Out of memory.  */
-               goto error;
+               goto out_of_memory;
              d->dir = memory;
            }
        }
+#if CHAR_T_ONLY_ASCII
+      else if (!c_isascii (c))
+       {
+         /* Non-ASCII character.  Not supported.  */
+         goto error;
+       }
+#endif
     }
   d->dir[d->count].dir_start = cp;
 
@@ -488,10 +569,21 @@ error:
     free (a->arg);
   if (d->dir)
     free (d->dir);
+  errno = EINVAL;
+  return -1;
+
+out_of_memory:
+  if (a->arg)
+    free (a->arg);
+  if (d->dir)
+    free (d->dir);
+out_of_memory_1:
+  errno = ENOMEM;
   return -1;
 }
 
+#undef PRINTF_PARSE
 #undef DIRECTIVES
 #undef DIRECTIVE
+#undef CHAR_T_ONLY_ASCII
 #undef CHAR_T
-#undef PRINTF_PARSE