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
60STATIC
61#endif
62int
63PRINTF_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
526error:
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