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