]> git.deb.at Git - pkg/abook.git/blob - intl/printf-parse.c
3f3174d06e0bdebce22f015ab2d6317d8f9f72ac
[pkg/abook.git] / intl / printf-parse.c
1 /* Formatted output to strings.
2    Copyright (C) 1999-2000, 2002-2003, 2006-2008 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 /* This file can be parametrized with the following macros:
20      CHAR_T             The element type of the format string.
21      CHAR_T_ONLY_ASCII  Set to 1 to enable verification that all characters
22                         in the format string are ASCII.
23      DIRECTIVE          Structure denoting a format directive.
24                         Depends on CHAR_T.
25      DIRECTIVES         Structure denoting the set of format directives of a
26                         format string.  Depends on CHAR_T.
27      PRINTF_PARSE       Function that parses a format string.
28                         Depends on CHAR_T.
29      STATIC             Set to 'static' to declare the function static.
30      ENABLE_UNISTDIO    Set to 1 to enable the unistdio extensions.  */
31
32 #ifndef PRINTF_PARSE
33 # include <config.h>
34 #endif
35
36 /* Specification.  */
37 #ifndef PRINTF_PARSE
38 # include "printf-parse.h"
39 #endif
40
41 /* Default parameters.  */
42 #ifndef PRINTF_PARSE
43 # define PRINTF_PARSE printf_parse
44 # define CHAR_T char
45 # define DIRECTIVE char_directive
46 # define DIRECTIVES char_directives
47 #endif
48
49 /* Get size_t, NULL.  */
50 #include <stddef.h>
51
52 /* Get intmax_t.  */
53 #if defined IN_LIBINTL || defined IN_LIBASPRINTF
54 # if HAVE_STDINT_H_WITH_UINTMAX
55 #  include <stdint.h>
56 # endif
57 # if HAVE_INTTYPES_H_WITH_UINTMAX
58 #  include <inttypes.h>
59 # endif
60 #else
61 # include <stdint.h>
62 #endif
63
64 /* malloc(), realloc(), free().  */
65 #include <stdlib.h>
66
67 /* errno.  */
68 #include <errno.h>
69
70 /* Checked size_t computations.  */
71 #include "xsize.h"
72
73 #if CHAR_T_ONLY_ASCII
74 /* c_isascii().  */
75 # include "c-ctype.h"
76 #endif
77
78 #ifdef STATIC
79 STATIC
80 #endif
81 int
82 PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
83 {
84   const CHAR_T *cp = format;            /* pointer into format */
85   size_t arg_posn = 0;          /* number of regular arguments consumed */
86   size_t d_allocated;                   /* allocated elements of d->dir */
87   size_t a_allocated;                   /* allocated elements of a->arg */
88   size_t max_width_length = 0;
89   size_t max_precision_length = 0;
90
91   d->count = 0;
92   d_allocated = 1;
93   d->dir = (DIRECTIVE *) malloc (d_allocated * sizeof (DIRECTIVE));
94   if (d->dir == NULL)
95     /* Out of memory.  */
96     goto out_of_memory_1;
97
98   a->count = 0;
99   a_allocated = 0;
100   a->arg = NULL;
101
102 #define REGISTER_ARG(_index_,_type_) \
103   {                                                                     \
104     size_t n = (_index_);                                               \
105     if (n >= a_allocated)                                               \
106       {                                                                 \
107         size_t memory_size;                                             \
108         argument *memory;                                               \
109                                                                         \
110         a_allocated = xtimes (a_allocated, 2);                          \
111         if (a_allocated <= n)                                           \
112           a_allocated = xsum (n, 1);                                    \
113         memory_size = xtimes (a_allocated, sizeof (argument));          \
114         if (size_overflow_p (memory_size))                              \
115           /* Overflow, would lead to out of memory.  */                 \
116           goto out_of_memory;                                           \
117         memory = (argument *) (a->arg                                   \
118                                ? realloc (a->arg, memory_size)          \
119                                : malloc (memory_size));                 \
120         if (memory == NULL)                                             \
121           /* Out of memory.  */                                         \
122           goto out_of_memory;                                           \
123         a->arg = memory;                                                \
124       }                                                                 \
125     while (a->count <= n)                                               \
126       a->arg[a->count++].type = TYPE_NONE;                              \
127     if (a->arg[n].type == TYPE_NONE)                                    \
128       a->arg[n].type = (_type_);                                        \
129     else if (a->arg[n].type != (_type_))                                \
130       /* Ambiguous type for positional argument.  */                    \
131       goto error;                                                       \
132   }
133
134   while (*cp != '\0')
135     {
136       CHAR_T c = *cp++;
137       if (c == '%')
138         {
139           size_t arg_index = ARG_NONE;
140           DIRECTIVE *dp = &d->dir[d->count]; /* pointer to next directive */
141
142           /* Initialize the next directive.  */
143           dp->dir_start = cp - 1;
144           dp->flags = 0;
145           dp->width_start = NULL;
146           dp->width_end = NULL;
147           dp->width_arg_index = ARG_NONE;
148           dp->precision_start = NULL;
149           dp->precision_end = NULL;
150           dp->precision_arg_index = ARG_NONE;
151           dp->arg_index = ARG_NONE;
152
153           /* Test for positional argument.  */
154           if (*cp >= '0' && *cp <= '9')
155             {
156               const CHAR_T *np;
157
158               for (np = cp; *np >= '0' && *np <= '9'; np++)
159                 ;
160               if (*np == '$')
161                 {
162                   size_t n = 0;
163
164                   for (np = cp; *np >= '0' && *np <= '9'; np++)
165                     n = xsum (xtimes (n, 10), *np - '0');
166                   if (n == 0)
167                     /* Positional argument 0.  */
168                     goto error;
169                   if (size_overflow_p (n))
170                     /* n too large, would lead to out of memory later.  */
171                     goto error;
172                   arg_index = n - 1;
173                   cp = np + 1;
174                 }
175             }
176
177           /* Read the flags.  */
178           for (;;)
179             {
180               if (*cp == '\'')
181                 {
182                   dp->flags |= FLAG_GROUP;
183                   cp++;
184                 }
185               else if (*cp == '-')
186                 {
187                   dp->flags |= FLAG_LEFT;
188                   cp++;
189                 }
190               else if (*cp == '+')
191                 {
192                   dp->flags |= FLAG_SHOWSIGN;
193                   cp++;
194                 }
195               else if (*cp == ' ')
196                 {
197                   dp->flags |= FLAG_SPACE;
198                   cp++;
199                 }
200               else if (*cp == '#')
201                 {
202                   dp->flags |= FLAG_ALT;
203                   cp++;
204                 }
205               else if (*cp == '0')
206                 {
207                   dp->flags |= FLAG_ZERO;
208                   cp++;
209                 }
210               else
211                 break;
212             }
213
214           /* Parse the field width.  */
215           if (*cp == '*')
216             {
217               dp->width_start = cp;
218               cp++;
219               dp->width_end = cp;
220               if (max_width_length < 1)
221                 max_width_length = 1;
222
223               /* Test for positional argument.  */
224               if (*cp >= '0' && *cp <= '9')
225                 {
226                   const CHAR_T *np;
227
228                   for (np = cp; *np >= '0' && *np <= '9'; np++)
229                     ;
230                   if (*np == '$')
231                     {
232                       size_t n = 0;
233
234                       for (np = cp; *np >= '0' && *np <= '9'; np++)
235                         n = xsum (xtimes (n, 10), *np - '0');
236                       if (n == 0)
237                         /* Positional argument 0.  */
238                         goto error;
239                       if (size_overflow_p (n))
240                         /* n too large, would lead to out of memory later.  */
241                         goto error;
242                       dp->width_arg_index = n - 1;
243                       cp = np + 1;
244                     }
245                 }
246               if (dp->width_arg_index == ARG_NONE)
247                 {
248                   dp->width_arg_index = arg_posn++;
249                   if (dp->width_arg_index == ARG_NONE)
250                     /* arg_posn wrapped around.  */
251                     goto error;
252                 }
253               REGISTER_ARG (dp->width_arg_index, TYPE_INT);
254             }
255           else if (*cp >= '0' && *cp <= '9')
256             {
257               size_t width_length;
258
259               dp->width_start = cp;
260               for (; *cp >= '0' && *cp <= '9'; cp++)
261                 ;
262               dp->width_end = cp;
263               width_length = dp->width_end - dp->width_start;
264               if (max_width_length < width_length)
265                 max_width_length = width_length;
266             }
267
268           /* Parse the precision.  */
269           if (*cp == '.')
270             {
271               cp++;
272               if (*cp == '*')
273                 {
274                   dp->precision_start = cp - 1;
275                   cp++;
276                   dp->precision_end = cp;
277                   if (max_precision_length < 2)
278                     max_precision_length = 2;
279
280                   /* Test for positional argument.  */
281                   if (*cp >= '0' && *cp <= '9')
282                     {
283                       const CHAR_T *np;
284
285                       for (np = cp; *np >= '0' && *np <= '9'; np++)
286                         ;
287                       if (*np == '$')
288                         {
289                           size_t n = 0;
290
291                           for (np = cp; *np >= '0' && *np <= '9'; np++)
292                             n = xsum (xtimes (n, 10), *np - '0');
293                           if (n == 0)
294                             /* Positional argument 0.  */
295                             goto error;
296                           if (size_overflow_p (n))
297                             /* n too large, would lead to out of memory
298                                later.  */
299                             goto error;
300                           dp->precision_arg_index = n - 1;
301                           cp = np + 1;
302                         }
303                     }
304                   if (dp->precision_arg_index == ARG_NONE)
305                     {
306                       dp->precision_arg_index = arg_posn++;
307                       if (dp->precision_arg_index == ARG_NONE)
308                         /* arg_posn wrapped around.  */
309                         goto error;
310                     }
311                   REGISTER_ARG (dp->precision_arg_index, TYPE_INT);
312                 }
313               else
314                 {
315                   size_t precision_length;
316
317                   dp->precision_start = cp - 1;
318                   for (; *cp >= '0' && *cp <= '9'; cp++)
319                     ;
320                   dp->precision_end = cp;
321                   precision_length = dp->precision_end - dp->precision_start;
322                   if (max_precision_length < precision_length)
323                     max_precision_length = precision_length;
324                 }
325             }
326
327           {
328             arg_type type;
329
330             /* Parse argument type/size specifiers.  */
331             {
332               int flags = 0;
333
334               for (;;)
335                 {
336                   if (*cp == 'h')
337                     {
338                       flags |= (1 << (flags & 1));
339                       cp++;
340                     }
341                   else if (*cp == 'L')
342                     {
343                       flags |= 4;
344                       cp++;
345                     }
346                   else if (*cp == 'l')
347                     {
348                       flags += 8;
349                       cp++;
350                     }
351                   else if (*cp == 'j')
352                     {
353                       if (sizeof (intmax_t) > sizeof (long))
354                         {
355                           /* intmax_t = long long */
356                           flags += 16;
357                         }
358                       else if (sizeof (intmax_t) > sizeof (int))
359                         {
360                           /* intmax_t = long */
361                           flags += 8;
362                         }
363                       cp++;
364                     }
365                   else if (*cp == 'z' || *cp == 'Z')
366                     {
367                       /* 'z' is standardized in ISO C 99, but glibc uses 'Z'
368                          because the warning facility in gcc-2.95.2 understands
369                          only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784).  */
370                       if (sizeof (size_t) > sizeof (long))
371                         {
372                           /* size_t = long long */
373                           flags += 16;
374                         }
375                       else if (sizeof (size_t) > sizeof (int))
376                         {
377                           /* size_t = long */
378                           flags += 8;
379                         }
380                       cp++;
381                     }
382                   else if (*cp == 't')
383                     {
384                       if (sizeof (ptrdiff_t) > sizeof (long))
385                         {
386                           /* ptrdiff_t = long long */
387                           flags += 16;
388                         }
389                       else if (sizeof (ptrdiff_t) > sizeof (int))
390                         {
391                           /* ptrdiff_t = long */
392                           flags += 8;
393                         }
394                       cp++;
395                     }
396 #if defined __APPLE__ && defined __MACH__
397                   /* On MacOS X 10.3, PRIdMAX is defined as "qd".
398                      We cannot change it to "lld" because PRIdMAX must also
399                      be understood by the system's printf routines.  */
400                   else if (*cp == 'q')
401                     {
402                       if (64 / 8 > sizeof (long))
403                         {
404                           /* int64_t = long long */
405                           flags += 16;
406                         }
407                       else
408                         {
409                           /* int64_t = long */
410                           flags += 8;
411                         }
412                       cp++;
413                     }
414 #endif
415 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
416                   /* On native Win32, PRIdMAX is defined as "I64d".
417                      We cannot change it to "lld" because PRIdMAX must also
418                      be understood by the system's printf routines.  */
419                   else if (*cp == 'I' && cp[1] == '6' && cp[2] == '4')
420                     {
421                       if (64 / 8 > sizeof (long))
422                         {
423                           /* __int64 = long long */
424                           flags += 16;
425                         }
426                       else
427                         {
428                           /* __int64 = long */
429                           flags += 8;
430                         }
431                       cp += 3;
432                     }
433 #endif
434                   else
435                     break;
436                 }
437
438               /* Read the conversion character.  */
439               c = *cp++;
440               switch (c)
441                 {
442                 case 'd': case 'i':
443 #if HAVE_LONG_LONG_INT
444                   /* If 'long long' exists and is larger than 'long':  */
445                   if (flags >= 16 || (flags & 4))
446                     type = TYPE_LONGLONGINT;
447                   else
448 #endif
449                   /* If 'long long' exists and is the same as 'long', we parse
450                      "lld" into TYPE_LONGINT.  */
451                   if (flags >= 8)
452                     type = TYPE_LONGINT;
453                   else if (flags & 2)
454                     type = TYPE_SCHAR;
455                   else if (flags & 1)
456                     type = TYPE_SHORT;
457                   else
458                     type = TYPE_INT;
459                   break;
460                 case 'o': case 'u': case 'x': case 'X':
461 #if HAVE_LONG_LONG_INT
462                   /* If 'long long' exists and is larger than 'long':  */
463                   if (flags >= 16 || (flags & 4))
464                     type = TYPE_ULONGLONGINT;
465                   else
466 #endif
467                   /* If 'unsigned long long' exists and is the same as
468                      'unsigned long', we parse "llu" into TYPE_ULONGINT.  */
469                   if (flags >= 8)
470                     type = TYPE_ULONGINT;
471                   else if (flags & 2)
472                     type = TYPE_UCHAR;
473                   else if (flags & 1)
474                     type = TYPE_USHORT;
475                   else
476                     type = TYPE_UINT;
477                   break;
478                 case 'f': case 'F': case 'e': case 'E': case 'g': case 'G':
479                 case 'a': case 'A':
480                   if (flags >= 16 || (flags & 4))
481                     type = TYPE_LONGDOUBLE;
482                   else
483                     type = TYPE_DOUBLE;
484                   break;
485                 case 'c':
486                   if (flags >= 8)
487 #if HAVE_WINT_T
488                     type = TYPE_WIDE_CHAR;
489 #else
490                     goto error;
491 #endif
492                   else
493                     type = TYPE_CHAR;
494                   break;
495 #if HAVE_WINT_T
496                 case 'C':
497                   type = TYPE_WIDE_CHAR;
498                   c = 'c';
499                   break;
500 #endif
501                 case 's':
502                   if (flags >= 8)
503 #if HAVE_WCHAR_T
504                     type = TYPE_WIDE_STRING;
505 #else
506                     goto error;
507 #endif
508                   else
509                     type = TYPE_STRING;
510                   break;
511 #if HAVE_WCHAR_T
512                 case 'S':
513                   type = TYPE_WIDE_STRING;
514                   c = 's';
515                   break;
516 #endif
517                 case 'p':
518                   type = TYPE_POINTER;
519                   break;
520                 case 'n':
521 #if HAVE_LONG_LONG_INT
522                   /* If 'long long' exists and is larger than 'long':  */
523                   if (flags >= 16 || (flags & 4))
524                     type = TYPE_COUNT_LONGLONGINT_POINTER;
525                   else
526 #endif
527                   /* If 'long long' exists and is the same as 'long', we parse
528                      "lln" into TYPE_COUNT_LONGINT_POINTER.  */
529                   if (flags >= 8)
530                     type = TYPE_COUNT_LONGINT_POINTER;
531                   else if (flags & 2)
532                     type = TYPE_COUNT_SCHAR_POINTER;
533                   else if (flags & 1)
534                     type = TYPE_COUNT_SHORT_POINTER;
535                   else
536                     type = TYPE_COUNT_INT_POINTER;
537                   break;
538 #if ENABLE_UNISTDIO
539                 /* The unistdio extensions.  */
540                 case 'U':
541                   if (flags >= 16)
542                     type = TYPE_U32_STRING;
543                   else if (flags >= 8)
544                     type = TYPE_U16_STRING;
545                   else
546                     type = TYPE_U8_STRING;
547                   break;
548 #endif
549                 case '%':
550                   type = TYPE_NONE;
551                   break;
552                 default:
553                   /* Unknown conversion character.  */
554                   goto error;
555                 }
556             }
557
558             if (type != TYPE_NONE)
559               {
560                 dp->arg_index = arg_index;
561                 if (dp->arg_index == ARG_NONE)
562                   {
563                     dp->arg_index = arg_posn++;
564                     if (dp->arg_index == ARG_NONE)
565                       /* arg_posn wrapped around.  */
566                       goto error;
567                   }
568                 REGISTER_ARG (dp->arg_index, type);
569               }
570             dp->conversion = c;
571             dp->dir_end = cp;
572           }
573
574           d->count++;
575           if (d->count >= d_allocated)
576             {
577               size_t memory_size;
578               DIRECTIVE *memory;
579
580               d_allocated = xtimes (d_allocated, 2);
581               memory_size = xtimes (d_allocated, sizeof (DIRECTIVE));
582               if (size_overflow_p (memory_size))
583                 /* Overflow, would lead to out of memory.  */
584                 goto out_of_memory;
585               memory = (DIRECTIVE *) realloc (d->dir, memory_size);
586               if (memory == NULL)
587                 /* Out of memory.  */
588                 goto out_of_memory;
589               d->dir = memory;
590             }
591         }
592 #if CHAR_T_ONLY_ASCII
593       else if (!c_isascii (c))
594         {
595           /* Non-ASCII character.  Not supported.  */
596           goto error;
597         }
598 #endif
599     }
600   d->dir[d->count].dir_start = cp;
601
602   d->max_width_length = max_width_length;
603   d->max_precision_length = max_precision_length;
604   return 0;
605
606 error:
607   if (a->arg)
608     free (a->arg);
609   if (d->dir)
610     free (d->dir);
611   errno = EINVAL;
612   return -1;
613
614 out_of_memory:
615   if (a->arg)
616     free (a->arg);
617   if (d->dir)
618     free (d->dir);
619 out_of_memory_1:
620   errno = ENOMEM;
621   return -1;
622 }
623
624 #undef PRINTF_PARSE
625 #undef DIRECTIVES
626 #undef DIRECTIVE
627 #undef CHAR_T_ONLY_ASCII
628 #undef CHAR_T