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