f8d3475f91cc4d48983f4b39f3f15e5d3b63aa4d
[platform/upstream/gstreamer.git] / gst / printf / 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 "gst-printf.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       a->arg[a->count].ext_string = (char *) 0;         \
86       ++a->count;                                       \
87     }                                                   \
88     if (a->arg[n].type == TYPE_NONE)                                    \
89       a->arg[n].type = (_type_);                                        \
90     else if (a->arg[n].type != (_type_))                                \
91       /* Ambiguous type for positional argument.  */                    \
92       goto error;                                                       \
93   }
94
95   while (*cp != '\0') {
96     char c = *cp++;
97     if (c == '%') {
98       int arg_index = -1;
99       char_directive *dp = &d->dir[d->count];   /* pointer to next directive */
100
101       /* Initialize the next directive.  */
102       dp->dir_start = cp - 1;
103       dp->flags = 0;
104       dp->width_start = NULL;
105       dp->width_end = NULL;
106       dp->width_arg_index = -1;
107       dp->precision_start = NULL;
108       dp->precision_end = NULL;
109       dp->precision_arg_index = -1;
110       dp->arg_index = -1;
111
112       /* Test for positional argument.  */
113       if (*cp >= '0' && *cp <= '9') {
114         const char *np;
115
116         for (np = cp; *np >= '0' && *np <= '9'; np++);
117         if (*np == '$') {
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         if (*cp == '\'') {
133           dp->flags |= FLAG_GROUP;
134           cp++;
135         } else if (*cp == '-') {
136           dp->flags |= FLAG_LEFT;
137           cp++;
138         } else if (*cp == '+') {
139           dp->flags |= FLAG_SHOWSIGN;
140           cp++;
141         } else if (*cp == ' ') {
142           dp->flags |= FLAG_SPACE;
143           cp++;
144         } else if (*cp == '#') {
145           dp->flags |= FLAG_ALT;
146           cp++;
147         } else if (*cp == '0') {
148           dp->flags |= FLAG_ZERO;
149           cp++;
150         } else
151           break;
152       }
153
154       /* Parse the field width.  */
155       if (*cp == '*') {
156         dp->width_start = cp;
157         cp++;
158         dp->width_end = cp;
159         if (max_width_length < 1)
160           max_width_length = 1;
161
162         /* Test for positional argument.  */
163         if (*cp >= '0' && *cp <= '9') {
164           const char *np;
165
166           for (np = cp; *np >= '0' && *np <= '9'; np++);
167           if (*np == '$') {
168             unsigned int n = 0;
169
170             for (np = cp; *np >= '0' && *np <= '9'; np++)
171               n = 10 * n + (*np - '0');
172             if (n == 0)
173               /* Positional argument 0.  */
174               goto error;
175             dp->width_arg_index = n - 1;
176             cp = np + 1;
177           }
178         }
179         if (dp->width_arg_index < 0)
180           dp->width_arg_index = arg_posn++;
181         REGISTER_ARG (dp->width_arg_index, TYPE_INT);
182       } else if (*cp >= '0' && *cp <= '9') {
183         unsigned int width_length;
184
185         dp->width_start = cp;
186         for (; *cp >= '0' && *cp <= '9'; cp++);
187         dp->width_end = cp;
188         width_length = dp->width_end - dp->width_start;
189         if (max_width_length < width_length)
190           max_width_length = width_length;
191       }
192
193       /* Parse the precision.  */
194       if (*cp == '.') {
195         cp++;
196         if (*cp == '*') {
197           dp->precision_start = cp - 1;
198           cp++;
199           dp->precision_end = cp;
200           if (max_precision_length < 2)
201             max_precision_length = 2;
202
203           /* Test for positional argument.  */
204           if (*cp >= '0' && *cp <= '9') {
205             const char *np;
206
207             for (np = cp; *np >= '0' && *np <= '9'; np++);
208             if (*np == '$') {
209               unsigned int n = 0;
210
211               for (np = cp; *np >= '0' && *np <= '9'; np++)
212                 n = 10 * n + (*np - '0');
213               if (n == 0)
214                 /* Positional argument 0.  */
215                 goto error;
216               dp->precision_arg_index = n - 1;
217               cp = np + 1;
218             }
219           }
220           if (dp->precision_arg_index < 0)
221             dp->precision_arg_index = arg_posn++;
222           REGISTER_ARG (dp->precision_arg_index, TYPE_INT);
223         } else {
224           unsigned int precision_length;
225
226           dp->precision_start = cp - 1;
227           for (; *cp >= '0' && *cp <= '9'; cp++);
228           dp->precision_end = cp;
229           precision_length = dp->precision_end - dp->precision_start;
230           if (max_precision_length < precision_length)
231             max_precision_length = precision_length;
232         }
233       }
234
235       {
236         arg_type type;
237
238         /* Parse argument type/size specifiers.  */
239         {
240           int flags = 0;
241
242           for (;;) {
243             if (*cp == 'h') {
244               flags |= (1 << (flags & 1));
245               cp++;
246             } else if (*cp == 'L') {
247               flags |= 4;
248               cp++;
249             } else if (*cp == 'l') {
250               flags += 8;
251               cp++;
252             }
253 #ifdef HAVE_INT64_AND_I64
254             else if (cp[0] == 'I' && cp[1] == '6' && cp[2] == '4') {
255               flags = 64;
256               cp += 3;
257             }
258 #endif
259 #ifdef HAVE_INTMAX_T
260             else if (*cp == 'j') {
261               if (sizeof (intmax_t) > sizeof (long)) {
262                 /* intmax_t = long long */
263                 flags += 16;
264               } else if (sizeof (intmax_t) > sizeof (int)) {
265                 /* intmax_t = long */
266                 flags += 8;
267               }
268               cp++;
269             }
270 #endif
271             else if (*cp == 'z' || *cp == 'Z') {
272               /* 'z' is standardized in ISO C 99, but glibc uses 'Z'
273                  because the warning facility in gcc-2.95.2 understands
274                  only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784).  */
275               if (sizeof (size_t) > sizeof (long)) {
276                 /* size_t = long long */
277                 flags += 16;
278               } else if (sizeof (size_t) > sizeof (int)) {
279                 /* size_t = long */
280                 flags += 8;
281               }
282               cp++;
283             } else if (*cp == 't') {
284               if (sizeof (ptrdiff_t) > sizeof (long)) {
285                 /* ptrdiff_t = long long */
286                 flags += 16;
287               } else if (sizeof (ptrdiff_t) > sizeof (int)) {
288                 /* ptrdiff_t = long */
289                 flags += 8;
290               }
291               cp++;
292             } else
293               break;
294           }
295
296           /* Read the conversion character.  */
297           c = *cp++;
298           switch (c) {
299             case 'd':
300             case 'i':
301 #ifdef HAVE_INT64_AND_I64
302               if (flags == 64)
303                 type = TYPE_INT64;
304               else
305 #endif
306 #ifdef HAVE_LONG_LONG
307               if (flags >= 16 || (flags & 4))
308                 type = TYPE_LONGLONGINT;
309               else
310 #endif
311               if (flags >= 8)
312                 type = TYPE_LONGINT;
313               else if (flags & 2)
314                 type = TYPE_SCHAR;
315               else if (flags & 1)
316                 type = TYPE_SHORT;
317               else
318                 type = TYPE_INT;
319               break;
320             case 'o':
321             case 'u':
322             case 'x':
323             case 'X':
324 #ifdef HAVE_INT64_AND_I64
325               if (flags == 64)
326                 type = TYPE_UINT64;
327               else
328 #endif
329 #ifdef HAVE_LONG_LONG
330               if (flags >= 16 || (flags & 4))
331                 type = TYPE_ULONGLONGINT;
332               else
333 #endif
334               if (flags >= 8)
335                 type = TYPE_ULONGINT;
336               else if (flags & 2)
337                 type = TYPE_UCHAR;
338               else if (flags & 1)
339                 type = TYPE_USHORT;
340               else
341                 type = TYPE_UINT;
342               break;
343             case 'f':
344             case 'F':
345             case 'e':
346             case 'E':
347             case 'g':
348             case 'G':
349             case 'a':
350             case 'A':
351 #ifdef HAVE_LONG_DOUBLE
352               if (flags >= 16 || (flags & 4))
353                 type = TYPE_LONGDOUBLE;
354               else
355 #endif
356                 type = TYPE_DOUBLE;
357               break;
358             case 'c':
359               if (flags >= 8)
360 #ifdef HAVE_WINT_T
361                 type = TYPE_WIDE_CHAR;
362 #else
363                 goto error;
364 #endif
365               else
366                 type = TYPE_CHAR;
367               break;
368 #ifdef HAVE_WINT_T
369             case 'C':
370               type = TYPE_WIDE_CHAR;
371               c = 'c';
372               break;
373 #endif
374             case 's':
375               if (flags >= 8)
376 #ifdef HAVE_WCHAR_T
377                 type = TYPE_WIDE_STRING;
378 #else
379                 goto error;
380 #endif
381               else
382                 type = TYPE_STRING;
383               break;
384 #ifdef HAVE_WCHAR_T
385             case 'S':
386               type = TYPE_WIDE_STRING;
387               c = 's';
388               break;
389 #endif
390               /* Old GST_PTR_FORMAT, handle for binary backwards compatibility */
391             case 'P':
392               type = TYPE_POINTER_EXT;
393               dp->flags |= FLAG_PTR_EXT;
394               dp->ptr_ext_char = 'A';
395               dp->conversion = 'p';
396               break;
397             case 'p':
398               /* Note: cp points already to the char after the 'p' now */
399               if (cp[0] == POINTER_EXT_SIGNIFIER_CHAR && cp[1] != '\0') {
400                 type = TYPE_POINTER_EXT;
401                 dp->flags |= FLAG_PTR_EXT;
402                 dp->ptr_ext_char = cp[1];
403                 cp += 2;
404                 /* we do not use dp->conversion='s' on purpose here, so we
405                  * can fall back to printing just the pointer with %p if the
406                  * serialisation function returned NULL for some reason */
407               } else {
408                 type = TYPE_POINTER;
409               }
410               break;
411               /* Old GST_SEGMENT_FORMAT, handle for backwards compatibility */
412             case 'Q':
413               type = TYPE_POINTER_EXT;
414               dp->flags |= FLAG_PTR_EXT;
415               dp->ptr_ext_char = 'B';
416               dp->conversion = 'p';
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           dp->arg_index = arg_index;
444           if (dp->arg_index < 0)
445             dp->arg_index = arg_posn++;
446           REGISTER_ARG (dp->arg_index, type);
447         }
448         dp->conversion = c;
449         dp->dir_end = cp;
450       }
451
452       d->count++;
453       if (d->count >= d_allocated) {
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 }