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