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