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