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