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