Rev 36792 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/* Formatted output to strings.Copyright (C) 1999-2000, 2002-2003 Free Software Foundation, Inc.This program is free software; you can redistribute it and/or modify itunder the terms of the GNU Library General Public License as publishedby 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNULibrary General Public License for more details.You should have received a copy of the GNU Library General PublicLicense along with this program; if not, write to the Free SoftwareFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA. */#ifdef HAVE_CONFIG_H# include <config.h>#endif/* Specification. */#if WIDE_CHAR_VERSION# include "wprintf-parse.h"#else# include "printf-parse.h"#endif/* Get size_t, NULL. */#include <stddef.h>/* Get intmax_t. */#if HAVE_STDINT_H_WITH_UINTMAX# include <stdint.h>#endif#if HAVE_INTTYPES_H_WITH_UINTMAX# include <inttypes.h>#endif/* malloc(), realloc(), free(). */#include <stdlib.h>/* Checked size_t computations. */#include "xsize.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#endif#ifdef STATICSTATIC#endifintPRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a){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 (DIRECTIVE));if (d->dir == NULL)/* Out of memory. */return -1;a->count = 0;a_allocated = 0;a->arg = NULL;#define REGISTER_ARG(_index_,_type_) \{ \size_t n = (_index_); \if (n >= a_allocated) \{ \size_t memory_size; \argument *memory; \\a_allocated = xtimes (a_allocated, 2); \if (a_allocated <= n) \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 error; \memory = (a->arg \? realloc (a->arg, memory_size) \: malloc (memory_size)); \if (memory == NULL) \/* Out of memory. */ \goto error; \a->arg = memory; \} \while (a->count <= n) \a->arg[a->count++].type = TYPE_NONE; \if (a->arg[n].type == TYPE_NONE) \a->arg[n].type = (_type_); \else if (a->arg[n].type != (_type_)) \/* Ambiguous type for positional argument. */ \goto error; \}while (*cp != '\0'){CHAR_T c = *cp++;if (c == '%'){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 = ARG_NONE;dp->precision_start = NULL;dp->precision_end = NULL;dp->precision_arg_index = ARG_NONE;dp->arg_index = ARG_NONE;/* Test for positional argument. */if (*cp >= '0' && *cp <= '9'){const CHAR_T *np;for (np = cp; *np >= '0' && *np <= '9'; np++);if (*np == '$'){size_t n = 0;for (np = cp; *np >= '0' && *np <= '9'; np++)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;}}/* Read the flags. */for (;;){if (*cp == '\''){dp->flags |= FLAG_GROUP;cp++;}else if (*cp == '-'){dp->flags |= FLAG_LEFT;cp++;}else if (*cp == '+'){dp->flags |= FLAG_SHOWSIGN;cp++;}else if (*cp == ' '){dp->flags |= FLAG_SPACE;cp++;}else if (*cp == '#'){dp->flags |= FLAG_ALT;cp++;}else if (*cp == '0'){dp->flags |= FLAG_ZERO;cp++;}elsebreak;}/* Parse the field width. */if (*cp == '*'){dp->width_start = cp;cp++;dp->width_end = cp;if (max_width_length < 1)max_width_length = 1;/* Test for positional argument. */if (*cp >= '0' && *cp <= '9'){const CHAR_T *np;for (np = cp; *np >= '0' && *np <= '9'; np++);if (*np == '$'){size_t n = 0;for (np = cp; *np >= '0' && *np <= '9'; np++)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 == 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'){size_t width_length;dp->width_start = cp;for (; *cp >= '0' && *cp <= '9'; cp++);dp->width_end = cp;width_length = dp->width_end - dp->width_start;if (max_width_length < width_length)max_width_length = width_length;}/* Parse the precision. */if (*cp == '.'){cp++;if (*cp == '*'){dp->precision_start = cp - 1;cp++;dp->precision_end = cp;if (max_precision_length < 2)max_precision_length = 2;/* Test for positional argument. */if (*cp >= '0' && *cp <= '9'){const CHAR_T *np;for (np = cp; *np >= '0' && *np <= '9'; np++);if (*np == '$'){size_t n = 0;for (np = cp; *np >= '0' && *np <= '9'; np++)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 memorylater. */goto error;dp->precision_arg_index = n - 1;cp = np + 1;}}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{size_t precision_length;dp->precision_start = cp - 1;for (; *cp >= '0' && *cp <= '9'; cp++);dp->precision_end = cp;precision_length = dp->precision_end - dp->precision_start;if (max_precision_length < precision_length)max_precision_length = precision_length;}}{arg_type type;/* Parse argument type/size specifiers. */{int flags = 0;for (;;){if (*cp == 'h'){flags |= (1 << (flags & 1));cp++;}else if (*cp == 'L'){flags |= 4;cp++;}else if (*cp == 'l'){flags += 8;cp++;}#ifdef HAVE_INTMAX_Telse if (*cp == 'j'){if (sizeof (intmax_t) > sizeof (long)){/* intmax_t = long long */flags += 16;}else if (sizeof (intmax_t) > sizeof (int)){/* intmax_t = long */flags += 8;}cp++;}#endifelse if (*cp == 'z' || *cp == 'Z'){/* 'z' is standardized in ISO C 99, but glibc uses 'Z'because the warning facility in gcc-2.95.2 understandsonly 'Z' (see gcc-2.95.2/gcc/c-common.c:1784). */if (sizeof (size_t) > sizeof (long)){/* size_t = long long */flags += 16;}else if (sizeof (size_t) > sizeof (int)){/* size_t = long */flags += 8;}cp++;}else if (*cp == 't'){if (sizeof (ptrdiff_t) > sizeof (long)){/* ptrdiff_t = long long */flags += 16;}else if (sizeof (ptrdiff_t) > sizeof (int)){/* ptrdiff_t = long */flags += 8;}cp++;}elsebreak;}/* Read the conversion character. */c = *cp++;switch (c){case 'd': case 'i':#ifdef HAVE_LONG_LONGif (flags >= 16 || (flags & 4))type = TYPE_LONGLONGINT;else#endifif (flags >= 8)type = TYPE_LONGINT;else if (flags & 2)type = TYPE_SCHAR;else if (flags & 1)type = TYPE_SHORT;elsetype = TYPE_INT;break;case 'o': case 'u': case 'x': case 'X':#ifdef HAVE_LONG_LONGif (flags >= 16 || (flags & 4))type = TYPE_ULONGLONGINT;else#endifif (flags >= 8)type = TYPE_ULONGINT;else if (flags & 2)type = TYPE_UCHAR;else if (flags & 1)type = TYPE_USHORT;elsetype = TYPE_UINT;break;case 'f': case 'F': case 'e': case 'E': case 'g': case 'G':case 'a': case 'A':#ifdef HAVE_LONG_DOUBLEif (flags >= 16 || (flags & 4))type = TYPE_LONGDOUBLE;else#endiftype = TYPE_DOUBLE;break;case 'c':if (flags >= 8)#ifdef HAVE_WINT_Ttype = TYPE_WIDE_CHAR;#elsegoto error;#endifelsetype = TYPE_CHAR;break;#ifdef HAVE_WINT_Tcase 'C':type = TYPE_WIDE_CHAR;c = 'c';break;#endifcase 's':if (flags >= 8)#ifdef HAVE_WCHAR_Ttype = TYPE_WIDE_STRING;#elsegoto error;#endifelsetype = TYPE_STRING;break;#ifdef HAVE_WCHAR_Tcase 'S':type = TYPE_WIDE_STRING;c = 's';break;#endifcase 'p':type = TYPE_POINTER;break;case 'n':#ifdef HAVE_LONG_LONGif (flags >= 16 || (flags & 4))type = TYPE_COUNT_LONGLONGINT_POINTER;else#endifif (flags >= 8)type = TYPE_COUNT_LONGINT_POINTER;else if (flags & 2)type = TYPE_COUNT_SCHAR_POINTER;else if (flags & 1)type = TYPE_COUNT_SHORT_POINTER;elsetype = TYPE_COUNT_INT_POINTER;break;case '%':type = TYPE_NONE;break;default:/* Unknown conversion character. */goto error;}}if (type != TYPE_NONE){dp->arg_index = arg_index;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;dp->dir_end = cp;}d->count++;if (d->count >= d_allocated){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 error;memory = realloc (d->dir, memory_size);if (memory == NULL)/* Out of memory. */goto error;d->dir = memory;}}}d->dir[d->count].dir_start = cp;d->max_width_length = max_width_length;d->max_precision_length = max_precision_length;return 0;error:if (a->arg)free (a->arg);if (d->dir)free (d->dir);return -1;}#undef DIRECTIVES#undef DIRECTIVE#undef CHAR_T#undef PRINTF_PARSE