Merge remote-tracking branch 'gvdb/master'
[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_INT64_AND_I64
290                   else if (cp[0] == 'I' && 
291                            cp[1] == '6' &&
292                            cp[2] == '4')
293                     {
294                       flags = 64;
295                       cp += 3;
296                     }
297 #endif
298 #ifdef HAVE_INTMAX_T
299                   else if (*cp == 'j')
300                     {
301                       if (sizeof (intmax_t) > sizeof (long))
302                         {
303                           /* intmax_t = long long */
304                           flags += 16;
305                         }
306                       else if (sizeof (intmax_t) > sizeof (int))
307                         {
308                           /* intmax_t = long */
309                           flags += 8;
310                         }
311                       cp++;
312                     }
313 #endif
314                   else if (*cp == 'z' || *cp == 'Z')
315                     {
316                       /* 'z' is standardized in ISO C 99, but glibc uses 'Z'
317                          because the warning facility in gcc-2.95.2 understands
318                          only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784).  */
319                       if (sizeof (size_t) > sizeof (long))
320                         {
321                           /* size_t = long long */
322                           flags += 16;
323                         }
324                       else if (sizeof (size_t) > sizeof (int))
325                         {
326                           /* size_t = long */
327                           flags += 8;
328                         }
329                       cp++;
330                     }
331                   else if (*cp == 't')
332                     {
333                       if (sizeof (ptrdiff_t) > sizeof (long))
334                         {
335                           /* ptrdiff_t = long long */
336                           flags += 16;
337                         }
338                       else if (sizeof (ptrdiff_t) > sizeof (int))
339                         {
340                           /* ptrdiff_t = long */
341                           flags += 8;
342                         }
343                       cp++;
344                     }
345                   else
346                     break;
347                 }
348
349               /* Read the conversion character.  */
350               c = *cp++;
351               switch (c)
352                 {
353                 case 'd': case 'i':
354 #ifdef HAVE_INT64_AND_I64
355                   if (flags == 64) 
356                     type = TYPE_INT64;
357                   else
358 #endif
359 #ifdef HAVE_LONG_LONG
360                   if (flags >= 16 || (flags & 4))
361                     type = TYPE_LONGLONGINT;
362                   else
363 #endif
364                   if (flags >= 8)
365                     type = TYPE_LONGINT;
366                   else if (flags & 2)
367                     type = TYPE_SCHAR;
368                   else if (flags & 1)
369                     type = TYPE_SHORT;
370                   else
371                     type = TYPE_INT;
372                   break;
373                 case 'o': case 'u': case 'x': case 'X':
374 #ifdef HAVE_INT64_AND_I64
375                   if (flags == 64)
376                     type = TYPE_UINT64;
377                   else
378 #endif
379 #ifdef HAVE_LONG_LONG
380                   if (flags >= 16 || (flags & 4))
381                     type = TYPE_ULONGLONGINT;
382                   else
383 #endif
384                   if (flags >= 8)
385                     type = TYPE_ULONGINT;
386                   else if (flags & 2)
387                     type = TYPE_UCHAR;
388                   else if (flags & 1)
389                     type = TYPE_USHORT;
390                   else
391                     type = TYPE_UINT;
392                   break;
393                 case 'f': case 'F': case 'e': case 'E': case 'g': case 'G':
394                 case 'a': case 'A':
395 #ifdef HAVE_LONG_DOUBLE
396                   if (flags >= 16 || (flags & 4))
397                     type = TYPE_LONGDOUBLE;
398                   else
399 #endif
400                   type = TYPE_DOUBLE;
401                   break;
402                 case 'c':
403                   if (flags >= 8)
404 #ifdef HAVE_WINT_T
405                     type = TYPE_WIDE_CHAR;
406 #else
407                     goto error;
408 #endif
409                   else
410                     type = TYPE_CHAR;
411                   break;
412 #ifdef HAVE_WINT_T
413                 case 'C':
414                   type = TYPE_WIDE_CHAR;
415                   c = 'c';
416                   break;
417 #endif
418                 case 's':
419                   if (flags >= 8)
420 #ifdef HAVE_WCHAR_T
421                     type = TYPE_WIDE_STRING;
422 #else
423                     goto error;
424 #endif
425                   else
426                     type = TYPE_STRING;
427                   break;
428 #ifdef HAVE_WCHAR_T
429                 case 'S':
430                   type = TYPE_WIDE_STRING;
431                   c = 's';
432                   break;
433 #endif
434                 case 'p':
435                   type = TYPE_POINTER;
436                   break;
437                 case 'n':
438 #ifdef HAVE_LONG_LONG
439                   if (flags >= 16 || (flags & 4))
440                     type = TYPE_COUNT_LONGLONGINT_POINTER;
441                   else
442 #endif
443                   if (flags >= 8)
444                     type = TYPE_COUNT_LONGINT_POINTER;
445                   else if (flags & 2)
446                     type = TYPE_COUNT_SCHAR_POINTER;
447                   else if (flags & 1)
448                     type = TYPE_COUNT_SHORT_POINTER;
449                   else
450                     type = TYPE_COUNT_INT_POINTER;
451                   break;
452                 case '%':
453                   type = TYPE_NONE;
454                   break;
455                 default:
456                   /* Unknown conversion character.  */
457                   goto error;
458                 }
459             }
460
461             if (type != TYPE_NONE)
462               {
463                 dp->arg_index = arg_index;
464                 if (dp->arg_index < 0)
465                   dp->arg_index = arg_posn++;
466                 REGISTER_ARG (dp->arg_index, type);
467               }
468             dp->conversion = c;
469             dp->dir_end = cp;
470           }
471
472           d->count++;
473           if (d->count >= d_allocated)
474             {
475               char_directive *memory;
476
477               d_allocated = 2 * d_allocated;
478               memory = realloc (d->dir, d_allocated * sizeof (char_directive));
479               if (memory == NULL)
480                 /* Out of memory.  */
481                 goto error;
482               d->dir = memory;
483             }
484         }
485     }
486   d->dir[d->count].dir_start = cp;
487
488   d->max_width_length = max_width_length;
489   d->max_precision_length = max_precision_length;
490   return 0;
491
492 error:
493   if (a->arg)
494     free (a->arg);
495   if (d->dir)
496     free (d->dir);
497   return -1;
498 }