Home | History | Annotate | Download | only in intl
      1 /* Formatted output to strings.
      2    Copyright (C) 1999-2000, 2002-2003 Free Software Foundation, Inc.
      3 
      4    This program is free software; you can redistribute it and/or modify it
      5    under the terms of the GNU Library General Public License as published
      6    by the Free Software Foundation; either version 2, or (at your option)
      7    any later version.
      8 
      9    This program is distributed in the hope that it will be useful,
     10    but WITHOUT ANY WARRANTY; without even the implied warranty of
     11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12    Library General Public License for more details.
     13 
     14    You should have received a copy of the GNU Library General Public
     15    License along with this program; if not, write to the Free Software
     16    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
     17    USA.  */
     18 
     19 #ifdef HAVE_CONFIG_H
     20 # include <config.h>
     21 #endif
     22 
     23 /* Specification.  */
     24 #if WIDE_CHAR_VERSION
     25 # include "wprintf-parse.h"
     26 #else
     27 # include "printf-parse.h"
     28 #endif
     29 
     30 /* Get size_t, NULL.  */
     31 #include <stddef.h>
     32 
     33 /* Get intmax_t.  */
     34 #if HAVE_STDINT_H_WITH_UINTMAX
     35 # include <stdint.h>
     36 #endif
     37 #if HAVE_INTTYPES_H_WITH_UINTMAX
     38 # include <inttypes.h>
     39 #endif
     40 
     41 /* malloc(), realloc(), free().  */
     42 #include <stdlib.h>
     43 
     44 /* Checked size_t computations.  */
     45 #include "xsize.h"
     46 
     47 #if WIDE_CHAR_VERSION
     48 # define PRINTF_PARSE wprintf_parse
     49 # define CHAR_T wchar_t
     50 # define DIRECTIVE wchar_t_directive
     51 # define DIRECTIVES wchar_t_directives
     52 #else
     53 # define PRINTF_PARSE printf_parse
     54 # define CHAR_T char
     55 # define DIRECTIVE char_directive
     56 # define DIRECTIVES char_directives
     57 #endif
     58 
     59 #ifdef STATIC
     60 STATIC
     61 #endif
     62 int
     63 PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
     64 {
     65   const CHAR_T *cp = format;		/* pointer into format */
     66   size_t arg_posn = 0;		/* number of regular arguments consumed */
     67   size_t d_allocated;			/* allocated elements of d->dir */
     68   size_t a_allocated;			/* allocated elements of a->arg */
     69   size_t max_width_length = 0;
     70   size_t max_precision_length = 0;
     71 
     72   d->count = 0;
     73   d_allocated = 1;
     74   d->dir = malloc (d_allocated * sizeof (DIRECTIVE));
     75   if (d->dir == NULL)
     76     /* Out of memory.  */
     77     return -1;
     78 
     79   a->count = 0;
     80   a_allocated = 0;
     81   a->arg = NULL;
     82 
     83 #define REGISTER_ARG(_index_,_type_) \
     84   {									\
     85     size_t n = (_index_);						\
     86     if (n >= a_allocated)						\
     87       {									\
     88 	size_t memory_size;						\
     89 	argument *memory;						\
     90 									\
     91 	a_allocated = xtimes (a_allocated, 2);				\
     92 	if (a_allocated <= n)						\
     93 	  a_allocated = xsum (n, 1);					\
     94 	memory_size = xtimes (a_allocated, sizeof (argument));		\
     95 	if (size_overflow_p (memory_size))				\
     96 	  /* Overflow, would lead to out of memory.  */			\
     97 	  goto error;							\
     98 	memory = (a->arg						\
     99 		  ? realloc (a->arg, memory_size)			\
    100 		  : malloc (memory_size));				\
    101 	if (memory == NULL)						\
    102 	  /* Out of memory.  */						\
    103 	  goto error;							\
    104 	a->arg = memory;						\
    105       }									\
    106     while (a->count <= n)						\
    107       a->arg[a->count++].type = TYPE_NONE;				\
    108     if (a->arg[n].type == TYPE_NONE)					\
    109       a->arg[n].type = (_type_);					\
    110     else if (a->arg[n].type != (_type_))				\
    111       /* Ambiguous type for positional argument.  */			\
    112       goto error;							\
    113   }
    114 
    115   while (*cp != '\0')
    116     {
    117       CHAR_T c = *cp++;
    118       if (c == '%')
    119 	{
    120 	  size_t arg_index = ARG_NONE;
    121 	  DIRECTIVE *dp = &d->dir[d->count];/* pointer to next directive */
    122 
    123 	  /* Initialize the next directive.  */
    124 	  dp->dir_start = cp - 1;
    125 	  dp->flags = 0;
    126 	  dp->width_start = NULL;
    127 	  dp->width_end = NULL;
    128 	  dp->width_arg_index = ARG_NONE;
    129 	  dp->precision_start = NULL;
    130 	  dp->precision_end = NULL;
    131 	  dp->precision_arg_index = ARG_NONE;
    132 	  dp->arg_index = ARG_NONE;
    133 
    134 	  /* Test for positional argument.  */
    135 	  if (*cp >= '0' && *cp <= '9')
    136 	    {
    137 	      const CHAR_T *np;
    138 
    139 	      for (np = cp; *np >= '0' && *np <= '9'; np++)
    140 		;
    141 	      if (*np == '$')
    142 		{
    143 		  size_t n = 0;
    144 
    145 		  for (np = cp; *np >= '0' && *np <= '9'; np++)
    146 		    n = xsum (xtimes (n, 10), *np - '0');
    147 		  if (n == 0)
    148 		    /* Positional argument 0.  */
    149 		    goto error;
    150 		  if (size_overflow_p (n))
    151 		    /* n too large, would lead to out of memory later.  */
    152 		    goto error;
    153 		  arg_index = n - 1;
    154 		  cp = np + 1;
    155 		}
    156 	    }
    157 
    158 	  /* Read the flags.  */
    159 	  for (;;)
    160 	    {
    161 	      if (*cp == '\'')
    162 		{
    163 		  dp->flags |= FLAG_GROUP;
    164 		  cp++;
    165 		}
    166 	      else if (*cp == '-')
    167 		{
    168 		  dp->flags |= FLAG_LEFT;
    169 		  cp++;
    170 		}
    171 	      else if (*cp == '+')
    172 		{
    173 		  dp->flags |= FLAG_SHOWSIGN;
    174 		  cp++;
    175 		}
    176 	      else if (*cp == ' ')
    177 		{
    178 		  dp->flags |= FLAG_SPACE;
    179 		  cp++;
    180 		}
    181 	      else if (*cp == '#')
    182 		{
    183 		  dp->flags |= FLAG_ALT;
    184 		  cp++;
    185 		}
    186 	      else if (*cp == '0')
    187 		{
    188 		  dp->flags |= FLAG_ZERO;
    189 		  cp++;
    190 		}
    191 	      else
    192 		break;
    193 	    }
    194 
    195 	  /* Parse the field width.  */
    196 	  if (*cp == '*')
    197 	    {
    198 	      dp->width_start = cp;
    199 	      cp++;
    200 	      dp->width_end = cp;
    201 	      if (max_width_length < 1)
    202 		max_width_length = 1;
    203 
    204 	      /* Test for positional argument.  */
    205 	      if (*cp >= '0' && *cp <= '9')
    206 		{
    207 		  const CHAR_T *np;
    208 
    209 		  for (np = cp; *np >= '0' && *np <= '9'; np++)
    210 		    ;
    211 		  if (*np == '$')
    212 		    {
    213 		      size_t n = 0;
    214 
    215 		      for (np = cp; *np >= '0' && *np <= '9'; np++)
    216 			n = xsum (xtimes (n, 10), *np - '0');
    217 		      if (n == 0)
    218 			/* Positional argument 0.  */
    219 			goto error;
    220 		      if (size_overflow_p (n))
    221 			/* n too large, would lead to out of memory later.  */
    222 			goto error;
    223 		      dp->width_arg_index = n - 1;
    224 		      cp = np + 1;
    225 		    }
    226 		}
    227 	      if (dp->width_arg_index == ARG_NONE)
    228 		{
    229 		  dp->width_arg_index = arg_posn++;
    230 		  if (dp->width_arg_index == ARG_NONE)
    231 		    /* arg_posn wrapped around.  */
    232 		    goto error;
    233 		}
    234 	      REGISTER_ARG (dp->width_arg_index, TYPE_INT);
    235 	    }
    236 	  else if (*cp >= '0' && *cp <= '9')
    237 	    {
    238 	      size_t width_length;
    239 
    240 	      dp->width_start = cp;
    241 	      for (; *cp >= '0' && *cp <= '9'; cp++)
    242 		;
    243 	      dp->width_end = cp;
    244 	      width_length = dp->width_end - dp->width_start;
    245 	      if (max_width_length < width_length)
    246 		max_width_length = width_length;
    247 	    }
    248 
    249 	  /* Parse the precision.  */
    250 	  if (*cp == '.')
    251 	    {
    252 	      cp++;
    253 	      if (*cp == '*')
    254 		{
    255 		  dp->precision_start = cp - 1;
    256 		  cp++;
    257 		  dp->precision_end = cp;
    258 		  if (max_precision_length < 2)
    259 		    max_precision_length = 2;
    260 
    261 		  /* Test for positional argument.  */
    262 		  if (*cp >= '0' && *cp <= '9')
    263 		    {
    264 		      const CHAR_T *np;
    265 
    266 		      for (np = cp; *np >= '0' && *np <= '9'; np++)
    267 			;
    268 		      if (*np == '$')
    269 			{
    270 			  size_t n = 0;
    271 
    272 			  for (np = cp; *np >= '0' && *np <= '9'; np++)
    273 			    n = xsum (xtimes (n, 10), *np - '0');
    274 			  if (n == 0)
    275 			    /* Positional argument 0.  */
    276 			    goto error;
    277 			  if (size_overflow_p (n))
    278 			    /* n too large, would lead to out of memory
    279 			       later.  */
    280 			    goto error;
    281 			  dp->precision_arg_index = n - 1;
    282 			  cp = np + 1;
    283 			}
    284 		    }
    285 		  if (dp->precision_arg_index == ARG_NONE)
    286 		    {
    287 		      dp->precision_arg_index = arg_posn++;
    288 		      if (dp->precision_arg_index == ARG_NONE)
    289 			/* arg_posn wrapped around.  */
    290 			goto error;
    291 		    }
    292 		  REGISTER_ARG (dp->precision_arg_index, TYPE_INT);
    293 		}
    294 	      else
    295 		{
    296 		  size_t precision_length;
    297 
    298 		  dp->precision_start = cp - 1;
    299 		  for (; *cp >= '0' && *cp <= '9'; cp++)
    300 		    ;
    301 		  dp->precision_end = cp;
    302 		  precision_length = dp->precision_end - dp->precision_start;
    303 		  if (max_precision_length < precision_length)
    304 		    max_precision_length = precision_length;
    305 		}
    306 	    }
    307 
    308 	  {
    309 	    arg_type type;
    310 
    311 	    /* Parse argument type/size specifiers.  */
    312 	    {
    313 	      int flags = 0;
    314 
    315 	      for (;;)
    316 		{
    317 		  if (*cp == 'h')
    318 		    {
    319 		      flags |= (1 << (flags & 1));
    320 		      cp++;
    321 		    }
    322 		  else if (*cp == 'L')
    323 		    {
    324 		      flags |= 4;
    325 		      cp++;
    326 		    }
    327 		  else if (*cp == 'l')
    328 		    {
    329 		      flags += 8;
    330 		      cp++;
    331 		    }
    332 #ifdef HAVE_INTMAX_T
    333 		  else if (*cp == 'j')
    334 		    {
    335 		      if (sizeof (intmax_t) > sizeof (long))
    336 			{
    337 			  /* intmax_t = long long */
    338 			  flags += 16;
    339 			}
    340 		      else if (sizeof (intmax_t) > sizeof (int))
    341 			{
    342 			  /* intmax_t = long */
    343 			  flags += 8;
    344 			}
    345 		      cp++;
    346 		    }
    347 #endif
    348 		  else if (*cp == 'z' || *cp == 'Z')
    349 		    {
    350 		      /* 'z' is standardized in ISO C 99, but glibc uses 'Z'
    351 			 because the warning facility in gcc-2.95.2 understands
    352 			 only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784).  */
    353 		      if (sizeof (size_t) > sizeof (long))
    354 			{
    355 			  /* size_t = long long */
    356 			  flags += 16;
    357 			}
    358 		      else if (sizeof (size_t) > sizeof (int))
    359 			{
    360 			  /* size_t = long */
    361 			  flags += 8;
    362 			}
    363 		      cp++;
    364 		    }
    365 		  else if (*cp == 't')
    366 		    {
    367 		      if (sizeof (ptrdiff_t) > sizeof (long))
    368 			{
    369 			  /* ptrdiff_t = long long */
    370 			  flags += 16;
    371 			}
    372 		      else if (sizeof (ptrdiff_t) > sizeof (int))
    373 			{
    374 			  /* ptrdiff_t = long */
    375 			  flags += 8;
    376 			}
    377 		      cp++;
    378 		    }
    379 		  else
    380 		    break;
    381 		}
    382 
    383 	      /* Read the conversion character.  */
    384 	      c = *cp++;
    385 	      switch (c)
    386 		{
    387 		case 'd': case 'i':
    388 #ifdef HAVE_LONG_LONG
    389 		  if (flags >= 16 || (flags & 4))
    390 		    type = TYPE_LONGLONGINT;
    391 		  else
    392 #endif
    393 		  if (flags >= 8)
    394 		    type = TYPE_LONGINT;
    395 		  else if (flags & 2)
    396 		    type = TYPE_SCHAR;
    397 		  else if (flags & 1)
    398 		    type = TYPE_SHORT;
    399 		  else
    400 		    type = TYPE_INT;
    401 		  break;
    402 		case 'o': case 'u': case 'x': case 'X':
    403 #ifdef HAVE_LONG_LONG
    404 		  if (flags >= 16 || (flags & 4))
    405 		    type = TYPE_ULONGLONGINT;
    406 		  else
    407 #endif
    408 		  if (flags >= 8)
    409 		    type = TYPE_ULONGINT;
    410 		  else if (flags & 2)
    411 		    type = TYPE_UCHAR;
    412 		  else if (flags & 1)
    413 		    type = TYPE_USHORT;
    414 		  else
    415 		    type = TYPE_UINT;
    416 		  break;
    417 		case 'f': case 'F': case 'e': case 'E': case 'g': case 'G':
    418 		case 'a': case 'A':
    419 #ifdef HAVE_LONG_DOUBLE
    420 		  if (flags >= 16 || (flags & 4))
    421 		    type = TYPE_LONGDOUBLE;
    422 		  else
    423 #endif
    424 		  type = TYPE_DOUBLE;
    425 		  break;
    426 		case 'c':
    427 		  if (flags >= 8)
    428 #ifdef HAVE_WINT_T
    429 		    type = TYPE_WIDE_CHAR;
    430 #else
    431 		    goto error;
    432 #endif
    433 		  else
    434 		    type = TYPE_CHAR;
    435 		  break;
    436 #ifdef HAVE_WINT_T
    437 		case 'C':
    438 		  type = TYPE_WIDE_CHAR;
    439 		  c = 'c';
    440 		  break;
    441 #endif
    442 		case 's':
    443 		  if (flags >= 8)
    444 #ifdef HAVE_WCHAR_T
    445 		    type = TYPE_WIDE_STRING;
    446 #else
    447 		    goto error;
    448 #endif
    449 		  else
    450 		    type = TYPE_STRING;
    451 		  break;
    452 #ifdef HAVE_WCHAR_T
    453 		case 'S':
    454 		  type = TYPE_WIDE_STRING;
    455 		  c = 's';
    456 		  break;
    457 #endif
    458 		case 'p':
    459 		  type = TYPE_POINTER;
    460 		  break;
    461 		case 'n':
    462 #ifdef HAVE_LONG_LONG
    463 		  if (flags >= 16 || (flags & 4))
    464 		    type = TYPE_COUNT_LONGLONGINT_POINTER;
    465 		  else
    466 #endif
    467 		  if (flags >= 8)
    468 		    type = TYPE_COUNT_LONGINT_POINTER;
    469 		  else if (flags & 2)
    470 		    type = TYPE_COUNT_SCHAR_POINTER;
    471 		  else if (flags & 1)
    472 		    type = TYPE_COUNT_SHORT_POINTER;
    473 		  else
    474 		    type = TYPE_COUNT_INT_POINTER;
    475 		  break;
    476 		case '%':
    477 		  type = TYPE_NONE;
    478 		  break;
    479 		default:
    480 		  /* Unknown conversion character.  */
    481 		  goto error;
    482 		}
    483 	    }
    484 
    485 	    if (type != TYPE_NONE)
    486 	      {
    487 		dp->arg_index = arg_index;
    488 		if (dp->arg_index == ARG_NONE)
    489 		  {
    490 		    dp->arg_index = arg_posn++;
    491 		    if (dp->arg_index == ARG_NONE)
    492 		      /* arg_posn wrapped around.  */
    493 		      goto error;
    494 		  }
    495 		REGISTER_ARG (dp->arg_index, type);
    496 	      }
    497 	    dp->conversion = c;
    498 	    dp->dir_end = cp;
    499 	  }
    500 
    501 	  d->count++;
    502 	  if (d->count >= d_allocated)
    503 	    {
    504 	      size_t memory_size;
    505 	      DIRECTIVE *memory;
    506 
    507 	      d_allocated = xtimes (d_allocated, 2);
    508 	      memory_size = xtimes (d_allocated, sizeof (DIRECTIVE));
    509 	      if (size_overflow_p (memory_size))
    510 		/* Overflow, would lead to out of memory.  */
    511 		goto error;
    512 	      memory = realloc (d->dir, memory_size);
    513 	      if (memory == NULL)
    514 		/* Out of memory.  */
    515 		goto error;
    516 	      d->dir = memory;
    517 	    }
    518 	}
    519     }
    520   d->dir[d->count].dir_start = cp;
    521 
    522   d->max_width_length = max_width_length;
    523   d->max_precision_length = max_precision_length;
    524   return 0;
    525 
    526 error:
    527   free (a->arg);
    528   free (d->dir);
    529   return -1;
    530 }
    531 
    532 #undef DIRECTIVES
    533 #undef DIRECTIVE
    534 #undef CHAR_T
    535 #undef PRINTF_PARSE
    536