X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fprintf-parse.c;h=b11f6bb22896103ba78413c4d1fc27328fe043ab;hb=2464bbfa7a6b82eca7bf3f7d517515ed462f1b23;hp=bdfe88fe8f8942f57fe26295a34bd2bc5e35e9c2;hpb=87b04f998fd3e668027074b5b5d37205d3cdfec3;p=gnulib.git diff --git a/lib/printf-parse.c b/lib/printf-parse.c index bdfe88fe8..b11f6bb22 100644 --- a/lib/printf-parse.c +++ b/lib/printf-parse.c @@ -1,61 +1,98 @@ /* Formatted output to strings. - Copyright (C) 1999-2000, 2002-2003 Free Software Foundation, Inc. + Copyright (C) 1999-2000, 2002-2003, 2006-2008 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU Library General Public License as published - by the Free Software Foundation; either version 2, or (at your option) + 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 + the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + 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., 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 #endif /* Specification. */ -#include "printf-parse.h" +#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 /* Get intmax_t. */ -#if HAVE_STDINT_H_WITH_UINTMAX +#if defined IN_LIBINTL || defined IN_LIBASPRINTF +# if HAVE_STDINT_H_WITH_UINTMAX +# include +# endif +# if HAVE_INTTYPES_H_WITH_UINTMAX +# include +# endif +#else # include #endif -#if HAVE_INTTYPES_H_WITH_UINTMAX -# include -#endif /* malloc(), realloc(), free(). */ #include +/* errno. */ +#include + +/* Checked size_t computations. */ +#include "xsize.h" + +#if CHAR_T_ONLY_ASCII +/* c_isascii(). */ +# include "c-ctype.h" +#endif + #ifdef STATIC STATIC #endif int -printf_parse (const char *format, char_directives *d, arguments *a) +PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a) { - const char *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; + const CHAR_T *cp = format; /* pointer into format */ + 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 (char_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; @@ -63,19 +100,25 @@ printf_parse (const char *format, char_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) \ @@ -89,39 +132,42 @@ printf_parse (const char *format, char_directives *d, arguments *a) while (*cp != '\0') { - char c = *cp++; + CHAR_T c = *cp++; if (c == '%') { - int arg_index = -1; - char_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') { - const char *np; + const CHAR_T *np; for (np = cp; *np >= '0' && *np <= '9'; np++) ; 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; } @@ -176,30 +222,38 @@ printf_parse (const char *format, char_directives *d, arguments *a) /* Test for positional argument. */ if (*cp >= '0' && *cp <= '9') { - const char *np; + const CHAR_T *np; for (np = cp; *np >= '0' && *np <= '9'; np++) ; 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++) @@ -225,30 +279,39 @@ printf_parse (const char *format, char_directives *d, arguments *a) /* Test for positional argument. */ if (*cp >= '0' && *cp <= '9') { - const char *np; + const CHAR_T *np; for (np = cp; *np >= '0' && *np <= '9'; np++) ; 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++) @@ -284,7 +347,6 @@ printf_parse (const char *format, char_directives *d, arguments *a) flags += 8; cp++; } -#ifdef HAVE_INTMAX_T else if (*cp == 'j') { if (sizeof (intmax_t) > sizeof (long)) @@ -299,7 +361,6 @@ printf_parse (const char *format, char_directives *d, arguments *a) } cp++; } -#endif else if (*cp == 'z' || *cp == 'Z') { /* 'z' is standardized in ISO C 99, but glibc uses 'Z' @@ -331,6 +392,44 @@ printf_parse (const char *format, char_directives *d, arguments *a) } cp++; } +#if defined __APPLE__ && defined __MACH__ + /* On MacOS X 10.3, PRIdMAX is defined as "qd". + We cannot change it to "lld" because PRIdMAX must also + be understood by the system's printf routines. */ + else if (*cp == 'q') + { + if (64 / 8 > sizeof (long)) + { + /* int64_t = long long */ + flags += 16; + } + else + { + /* int64_t = long */ + flags += 8; + } + cp++; + } +#endif +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + /* On native Win32, PRIdMAX is defined as "I64d". + We cannot change it to "lld" because PRIdMAX must also + be understood by the system's printf routines. */ + else if (*cp == 'I' && cp[1] == '6' && cp[2] == '4') + { + if (64 / 8 > sizeof (long)) + { + /* __int64 = long long */ + flags += 16; + } + else + { + /* __int64 = long */ + flags += 8; + } + cp += 3; + } +#endif else break; } @@ -340,11 +439,14 @@ printf_parse (const char *format, char_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) @@ -355,11 +457,14 @@ printf_parse (const char *format, char_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) @@ -371,16 +476,14 @@ printf_parse (const char *format, char_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; @@ -388,7 +491,7 @@ printf_parse (const char *format, char_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'; @@ -396,7 +499,7 @@ printf_parse (const char *format, char_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; @@ -404,7 +507,7 @@ printf_parse (const char *format, char_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'; @@ -414,11 +517,14 @@ printf_parse (const char *format, char_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) @@ -428,6 +534,17 @@ printf_parse (const char *format, char_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; @@ -440,8 +557,13 @@ printf_parse (const char *format, char_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; @@ -451,16 +573,28 @@ printf_parse (const char *format, char_directives *d, arguments *a) d->count++; if (d->count >= d_allocated) { - char_directive *memory; - - d_allocated = 2 * d_allocated; - memory = realloc (d->dir, d_allocated * sizeof (char_directive)); + size_t memory_size; + DIRECTIVE *memory; + + 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; @@ -473,5 +607,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