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