Tizen 2.0 Release
[external/tizen-coreutils.git] / lib / printf-parse.c
1 /* Formatted output to strings.
2    This file is intended to provide exactly the same functionality
3    as the version in gnulib, but without the need for the xsize module.
4
5    Copyright (C) 1999-2000, 2002-2003, 2006-2007 Free Software Foundation, Inc.
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2, or (at your option)
10    any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License along
18    with this program; if not, write to the Free Software Foundation,
19    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
20
21 #include <config.h>
22
23 /* Specification.  */
24 #if WIDE_CHAR_VERSION
25 # include "wprintf-parse.h"
26 #else
27 # include "printf-parse.h"
28 #endif
29
30 /* Get size_t, NULL.  */
31 #include <stddef.h>
32
33 /* Get intmax_t, SIZE_MAX.  */
34 #include <stdint.h>
35
36 /* malloc(), realloc(), free().  */
37 #include <stdlib.h>
38
39 #if WIDE_CHAR_VERSION
40 # define PRINTF_PARSE wprintf_parse
41 # define CHAR_T wchar_t
42 # define DIRECTIVE wchar_t_directive
43 # define DIRECTIVES wchar_t_directives
44 #else
45 # define PRINTF_PARSE printf_parse
46 # define CHAR_T char
47 # define DIRECTIVE char_directive
48 # define DIRECTIVES char_directives
49 #endif
50
51 #ifdef STATIC
52 STATIC
53 #endif
54 int
55 PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
56 {
57   const CHAR_T *cp = format;            /* pointer into format */
58   size_t arg_posn = 0;          /* number of regular arguments consumed */
59   size_t d_allocated;                   /* allocated elements of d->dir */
60   size_t a_allocated;                   /* allocated elements of a->arg */
61   size_t max_width_length = 0;
62   size_t max_precision_length = 0;
63
64   d->count = 0;
65   d_allocated = 1;
66   d->dir = malloc (d_allocated * sizeof (DIRECTIVE));
67   if (d->dir == NULL)
68     /* Out of memory.  */
69     return -1;
70
71   a->count = 0;
72   a_allocated = 0;
73   a->arg = NULL;
74
75 #define REGISTER_ARG(_index_,_type_) \
76   {                                                                     \
77     size_t n = (_index_);                                               \
78     if (n >= a_allocated)                                               \
79       {                                                                 \
80         size_t memory_size;                                             \
81         argument *memory;                                               \
82                                                                         \
83         a_allocated *= 2;                                               \
84         if (a_allocated <= n)                                           \
85           a_allocated = n + 1;                                          \
86         if (SIZE_MAX / sizeof (argument) < a_allocated)                 \
87           /* Overflow, would lead to out of memory.  */                 \
88           goto error;                                                   \
89         memory_size = a_allocated * sizeof (argument);                  \
90         memory = (a->arg                                                \
91                   ? realloc (a->arg, memory_size)                       \
92                   : malloc (memory_size));                              \
93         if (memory == NULL)                                             \
94           /* Out of memory.  */                                         \
95           goto error;                                                   \
96         a->arg = memory;                                                \
97       }                                                                 \
98     while (a->count <= n)                                               \
99       a->arg[a->count++].type = TYPE_NONE;                              \
100     if (a->arg[n].type == TYPE_NONE)                                    \
101       a->arg[n].type = (_type_);                                        \
102     else if (a->arg[n].type != (_type_))                                \
103       /* Ambiguous type for positional argument.  */                    \
104       goto error;                                                       \
105   }
106
107   while (*cp != '\0')
108     {
109       CHAR_T c = *cp++;
110       if (c == '%')
111         {
112           size_t arg_index = ARG_NONE;
113           DIRECTIVE *dp = &d->dir[d->count];/* pointer to next directive */
114
115           /* Initialize the next directive.  */
116           dp->dir_start = cp - 1;
117           dp->flags = 0;
118           dp->width_start = NULL;
119           dp->width_end = NULL;
120           dp->width_arg_index = ARG_NONE;
121           dp->precision_start = NULL;
122           dp->precision_end = NULL;
123           dp->precision_arg_index = ARG_NONE;
124           dp->arg_index = ARG_NONE;
125
126           /* Test for positional argument.  */
127           if (*cp >= '0' && *cp <= '9')
128             {
129               const CHAR_T *np;
130
131               for (np = cp; *np >= '0' && *np <= '9'; np++)
132                 ;
133               if (*np == '$')
134                 {
135                   size_t n = 0;
136
137                   for (np = cp; *np >= '0' && *np <= '9'; np++)
138                     if (n < SIZE_MAX / 10)
139                       n = 10 * n + (*np - '0');
140                     else
141                       /* n too large for memory.  */
142                       goto error;
143                   if (n == 0)
144                     /* Positional argument 0.  */
145                     goto error;
146                   arg_index = n - 1;
147                   cp = np + 1;
148                 }
149             }
150
151           /* Read the flags.  */
152           for (;;)
153             {
154               if (*cp == '\'')
155                 {
156                   dp->flags |= FLAG_GROUP;
157                   cp++;
158                 }
159               else if (*cp == '-')
160                 {
161                   dp->flags |= FLAG_LEFT;
162                   cp++;
163                 }
164               else if (*cp == '+')
165                 {
166                   dp->flags |= FLAG_SHOWSIGN;
167                   cp++;
168                 }
169               else if (*cp == ' ')
170                 {
171                   dp->flags |= FLAG_SPACE;
172                   cp++;
173                 }
174               else if (*cp == '#')
175                 {
176                   dp->flags |= FLAG_ALT;
177                   cp++;
178                 }
179               else if (*cp == '0')
180                 {
181                   dp->flags |= FLAG_ZERO;
182                   cp++;
183                 }
184               else
185                 break;
186             }
187
188           /* Parse the field width.  */
189           if (*cp == '*')
190             {
191               dp->width_start = cp;
192               cp++;
193               dp->width_end = cp;
194               if (max_width_length < 1)
195                 max_width_length = 1;
196
197               /* Test for positional argument.  */
198               if (*cp >= '0' && *cp <= '9')
199                 {
200                   const CHAR_T *np;
201
202                   for (np = cp; *np >= '0' && *np <= '9'; np++)
203                     ;
204                   if (*np == '$')
205                     {
206                       size_t n = 0;
207
208                       for (np = cp; *np >= '0' && *np <= '9'; np++)
209                         if (n < SIZE_MAX / 10)
210                           n = 10 * n + (*np - '0');
211                         else
212                           /* n too large for memory.  */
213                           goto error;
214                       if (n == 0)
215                         /* Positional argument 0.  */
216                         goto error;
217                       dp->width_arg_index = n - 1;
218                       cp = np + 1;
219                     }
220                 }
221               if (dp->width_arg_index == ARG_NONE)
222                 {
223                   dp->width_arg_index = arg_posn++;
224                   if (dp->width_arg_index == ARG_NONE)
225                     /* arg_posn wrapped around.  */
226                     goto error;
227                 }
228               REGISTER_ARG (dp->width_arg_index, TYPE_INT);
229             }
230           else if (*cp >= '0' && *cp <= '9')
231             {
232               size_t width_length;
233
234               dp->width_start = cp;
235               for (; *cp >= '0' && *cp <= '9'; cp++)
236                 ;
237               dp->width_end = cp;
238               width_length = dp->width_end - dp->width_start;
239               if (max_width_length < width_length)
240                 max_width_length = width_length;
241             }
242
243           /* Parse the precision.  */
244           if (*cp == '.')
245             {
246               cp++;
247               if (*cp == '*')
248                 {
249                   dp->precision_start = cp - 1;
250                   cp++;
251                   dp->precision_end = cp;
252                   if (max_precision_length < 2)
253                     max_precision_length = 2;
254
255                   /* Test for positional argument.  */
256                   if (*cp >= '0' && *cp <= '9')
257                     {
258                       const CHAR_T *np;
259
260                       for (np = cp; *np >= '0' && *np <= '9'; np++)
261                         ;
262                       if (*np == '$')
263                         {
264                           size_t n = 0;
265
266                           for (np = cp; *np >= '0' && *np <= '9'; np++)
267                             if (n < SIZE_MAX / 10)
268                               n = 10 * n + (*np - '0');
269                             else
270                               /* n too large for memory.  */
271                               goto error;
272                           if (n == 0)
273                             /* Positional argument 0.  */
274                             goto error;
275                           dp->precision_arg_index = n - 1;
276                           cp = np + 1;
277                         }
278                     }
279                   if (dp->precision_arg_index == ARG_NONE)
280                     {
281                       dp->precision_arg_index = arg_posn++;
282                       if (dp->precision_arg_index == ARG_NONE)
283                         /* arg_posn wrapped around.  */
284                         goto error;
285                     }
286                   REGISTER_ARG (dp->precision_arg_index, TYPE_INT);
287                 }
288               else
289                 {
290                   size_t precision_length;
291
292                   dp->precision_start = cp - 1;
293                   for (; *cp >= '0' && *cp <= '9'; cp++)
294                     ;
295                   dp->precision_end = cp;
296                   precision_length = dp->precision_end - dp->precision_start;
297                   if (max_precision_length < precision_length)
298                     max_precision_length = precision_length;
299                 }
300             }
301
302           {
303             arg_type type;
304
305             /* Parse argument type/size specifiers.  */
306             {
307               int flags = 0;
308
309               for (;;)
310                 {
311                   if (*cp == 'h')
312                     {
313                       flags |= (1 << (flags & 1));
314                       cp++;
315                     }
316                   else if (*cp == 'L')
317                     {
318                       flags |= 4;
319                       cp++;
320                     }
321                   else if (*cp == 'l')
322                     {
323                       flags += 8;
324                       cp++;
325                     }
326 #if HAVE_INTMAX_T
327                   else if (*cp == 'j')
328                     {
329                       if (sizeof (intmax_t) > sizeof (long))
330                         {
331                           /* intmax_t = long long */
332                           flags += 16;
333                         }
334                       else if (sizeof (intmax_t) > sizeof (int))
335                         {
336                           /* intmax_t = long */
337                           flags += 8;
338                         }
339                       cp++;
340                     }
341 #endif
342                   else if (*cp == 'z' || *cp == 'Z')
343                     {
344                       /* 'z' is standardized in ISO C 99, but glibc uses 'Z'
345                          because the warning facility in gcc-2.95.2 understands
346                          only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784).  */
347                       if (sizeof (size_t) > sizeof (long))
348                         {
349                           /* size_t = long long */
350                           flags += 16;
351                         }
352                       else if (sizeof (size_t) > sizeof (int))
353                         {
354                           /* size_t = long */
355                           flags += 8;
356                         }
357                       cp++;
358                     }
359                   else if (*cp == 't')
360                     {
361                       if (sizeof (ptrdiff_t) > sizeof (long))
362                         {
363                           /* ptrdiff_t = long long */
364                           flags += 16;
365                         }
366                       else if (sizeof (ptrdiff_t) > sizeof (int))
367                         {
368                           /* ptrdiff_t = long */
369                           flags += 8;
370                         }
371                       cp++;
372                     }
373                   else
374                     break;
375                 }
376
377               /* Read the conversion character.  */
378               c = *cp++;
379               switch (c)
380                 {
381                 case 'd': case 'i':
382 #if HAVE_LONG_LONG_INT
383                   /* If 'long long' exists and is larger than 'long':  */
384                   if (flags >= 16 || (flags & 4))
385                     type = TYPE_LONGLONGINT;
386                   else
387 #endif
388                   /* If 'long long' exists and is the same as 'long', we parse
389                      "lld" into TYPE_LONGINT.  */
390                   if (flags >= 8)
391                     type = TYPE_LONGINT;
392                   else if (flags & 2)
393                     type = TYPE_SCHAR;
394                   else if (flags & 1)
395                     type = TYPE_SHORT;
396                   else
397                     type = TYPE_INT;
398                   break;
399                 case 'o': case 'u': case 'x': case 'X':
400 #if HAVE_LONG_LONG_INT
401                   /* If 'long long' exists and is larger than 'long':  */
402                   if (flags >= 16 || (flags & 4))
403                     type = TYPE_ULONGLONGINT;
404                   else
405 #endif
406                   /* If 'unsigned long long' exists and is the same as
407                      'unsigned long', we parse "llu" into TYPE_ULONGINT.  */
408                   if (flags >= 8)
409                     type = TYPE_ULONGINT;
410                   else if (flags & 2)
411                     type = TYPE_UCHAR;
412                   else if (flags & 1)
413                     type = TYPE_USHORT;
414                   else
415                     type = TYPE_UINT;
416                   break;
417                 case 'f': case 'F': case 'e': case 'E': case 'g': case 'G':
418                 case 'a': case 'A':
419 #if HAVE_LONG_DOUBLE
420                   if (flags >= 16 || (flags & 4))
421                     type = TYPE_LONGDOUBLE;
422                   else
423 #endif
424                   type = TYPE_DOUBLE;
425                   break;
426                 case 'c':
427                   if (flags >= 8)
428 #if HAVE_WINT_T
429                     type = TYPE_WIDE_CHAR;
430 #else
431                     goto error;
432 #endif
433                   else
434                     type = TYPE_CHAR;
435                   break;
436 #if HAVE_WINT_T
437                 case 'C':
438                   type = TYPE_WIDE_CHAR;
439                   c = 'c';
440                   break;
441 #endif
442                 case 's':
443                   if (flags >= 8)
444 #if HAVE_WCHAR_T
445                     type = TYPE_WIDE_STRING;
446 #else
447                     goto error;
448 #endif
449                   else
450                     type = TYPE_STRING;
451                   break;
452 #if HAVE_WCHAR_T
453                 case 'S':
454                   type = TYPE_WIDE_STRING;
455                   c = 's';
456                   break;
457 #endif
458                 case 'p':
459                   type = TYPE_POINTER;
460                   break;
461                 case 'n':
462 #if HAVE_LONG_LONG_INT
463                   /* If 'long long' exists and is larger than 'long':  */
464                   if (flags >= 16 || (flags & 4))
465                     type = TYPE_COUNT_LONGLONGINT_POINTER;
466                   else
467 #endif
468                   /* If 'long long' exists and is the same as 'long', we parse
469                      "lln" into TYPE_COUNT_LONGINT_POINTER.  */
470                   if (flags >= 8)
471                     type = TYPE_COUNT_LONGINT_POINTER;
472                   else if (flags & 2)
473                     type = TYPE_COUNT_SCHAR_POINTER;
474                   else if (flags & 1)
475                     type = TYPE_COUNT_SHORT_POINTER;
476                   else
477                     type = TYPE_COUNT_INT_POINTER;
478                   break;
479                 case '%':
480                   type = TYPE_NONE;
481                   break;
482                 default:
483                   /* Unknown conversion character.  */
484                   goto error;
485                 }
486             }
487
488             if (type != TYPE_NONE)
489               {
490                 dp->arg_index = arg_index;
491                 if (dp->arg_index == ARG_NONE)
492                   {
493                     dp->arg_index = arg_posn++;
494                     if (dp->arg_index == ARG_NONE)
495                       /* arg_posn wrapped around.  */
496                       goto error;
497                   }
498                 REGISTER_ARG (dp->arg_index, type);
499               }
500             dp->conversion = c;
501             dp->dir_end = cp;
502           }
503
504           d->count++;
505           if (d->count >= d_allocated)
506             {
507               DIRECTIVE *memory;
508
509               if (SIZE_MAX / (2 * sizeof (DIRECTIVE)) < d_allocated)
510                 /* Overflow, would lead to out of memory.  */
511                 goto error;
512               d_allocated *= 2;
513               memory = realloc (d->dir, d_allocated * sizeof (DIRECTIVE));
514               if (memory == NULL)
515                 /* Out of memory.  */
516                 goto error;
517               d->dir = memory;
518             }
519         }
520     }
521   d->dir[d->count].dir_start = cp;
522
523   d->max_width_length = max_width_length;
524   d->max_precision_length = max_precision_length;
525   return 0;
526
527 error:
528   if (a->arg)
529     free (a->arg);
530   if (d->dir)
531     free (d->dir);
532   return -1;
533 }
534
535 #undef DIRECTIVES
536 #undef DIRECTIVE
537 #undef CHAR_T
538 #undef PRINTF_PARSE