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