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