source: trunk/third/gettext/lib/printf-parse.h @ 16931

Revision 16931, 10.9 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r16930, which included commits to RCS files with non-trunk default branches.
Line 
1/* Internal header for parsing printf format strings.
2   Copyright (C) 1995, 1996, 1998, 2000 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
15   along 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/* We use some extension so define this here.  */
19#define _GNU_SOURCE     1
20
21#include <ctype.h>
22#include <printf.h>
23#if HAVE_STDDEF_H
24# include <stddef.h>
25#endif
26#if HAVE_INTTYPES_H
27# include <inttypes.h>
28#endif
29
30#if HAVE_STRING_H
31# include <string.h>
32#else
33# include <strings.h>
34#endif
35
36#if __GNUC__ >= 2
37# define long_double long double
38#else
39# define long_double double
40#endif
41
42#if HAVE_UNSIGNED_LONG_LONG
43# define long_long_int long long int
44#else
45# define long_long_int long int
46#endif
47
48#if !HAVE_PTRDIFF_T
49# define ptrdiff_t long int
50#endif
51
52#define NDEBUG 1
53#include <assert.h>
54
55#ifndef MAX
56# if defined __GNU__ && __GNUC__ >= 2
57#  define MAX(a,b)      ({typeof(a) _a = (a); typeof(b) _b = (b);             \
58                          _a > _b ? _a : _b; })
59# else
60#  define MAX(a,b)      ((a) > (b) ? (a) : (b))
61# endif
62#endif
63#ifndef MIN
64# if defined __GNU__ && __GNUC__ >= 2
65#  define MIN(a,b)      ({typeof(a) _a = (a); typeof(b) _b = (b);             \
66                          _a < _b ? _a : _b; })
67# else
68#  define MIN(a,b)      ((a) < (b) ? (a) : (b))
69# endif
70#endif
71
72struct printf_spec
73  {
74    /* Information parsed from the format spec.  */
75    struct printf_info info;
76
77    /* Pointers into the format string for the end of this format
78       spec and the next (or to the end of the string if no more).  */
79    const char *end_of_fmt, *next_fmt;
80
81    /* Position of arguments for precision and width, or -1 if `info' has
82       the constant value.  */
83    int prec_arg, width_arg;
84
85    int data_arg;               /* Position of data argument.  */
86    int data_arg_type;          /* Type of first argument.  */
87    /* Number of arguments consumed by this format specifier.  */
88    size_t ndata_args;
89  };
90
91
92/* The various kinds off arguments that can be passed to printf.  */
93union printf_arg
94  {
95    unsigned char pa_char;
96    short int pa_short_int;
97    int pa_int;
98    long int pa_long_int;
99    long_long_int pa_long_long_int;
100    unsigned short int pa_u_short_int;
101    unsigned int pa_u_int;
102    unsigned long int pa_u_long_int;
103    unsigned long_long_int pa_u_long_long_int;
104    float pa_float;
105    double pa_double;
106    long_double pa_long_double;
107    const char *pa_string;
108    void *pa_pointer;
109  };
110
111
112/* Prototype for local function.  */
113static unsigned int read_int PARAMS ((const char **pstr));
114static const char *find_spec PARAMS ((const char *format));
115static inline size_t parse_one_spec PARAMS ((const char *format,
116                                             size_t posn,
117                                             struct printf_spec *spec,
118                                             size_t *max_ref_arg));
119
120
121/* Read a simple integer from a string and update the string pointer.
122   It is assumed that the first character is a digit.  */
123static inline unsigned int
124read_int (pstr)
125     const char **pstr;
126{
127  unsigned int retval = **pstr - '0';
128
129  while (isdigit (*++(*pstr)))
130    {
131      retval *= 10;
132      retval += **pstr - '0';
133    }
134
135  return retval;
136}
137
138
139
140/* Find the next spec in FORMAT, or the end of the string.  Returns
141   a pointer into FORMAT, to a '%' or a '\0'.  */
142static inline const char *
143find_spec (format)
144     const char *format;
145{
146  while (*format != '\0' && *format != '%')
147    ++format;
148  return format;
149}
150
151
152/* FORMAT must point to a '%' at the beginning of a spec.  Fills in *SPEC
153   with the parsed details.  POSN is the number of arguments already
154   consumed.  At most MAXTYPES - POSN types are filled in TYPES.  Return
155   the number of args consumed by this spec; *MAX_REF_ARG is updated so it
156   remains the highest argument index used.  */
157static inline size_t
158parse_one_spec (format, posn, spec, max_ref_arg)
159     const char *format;
160     size_t posn;
161     struct printf_spec *spec;
162     size_t *max_ref_arg;
163{
164  unsigned int n;
165  size_t nargs = 0;
166
167  /* Skip the '%'.  */
168  ++format;
169
170  /* Clear information structure.  */
171  spec->data_arg = -1;
172  spec->info.alt = 0;
173  spec->info.space = 0;
174  spec->info.left = 0;
175  spec->info.showsign = 0;
176  spec->info.group = 0;
177  spec->info.pad = ' ';
178
179  /* Test for positional argument.  */
180  if (isdigit (*format))
181    {
182      const char *begin = format;
183
184      n = read_int (&format);
185
186      if (n > 0 && *format == '$')
187        /* Is positional parameter.  */
188        {
189          ++format;             /* Skip the '$'.  */
190          spec->data_arg = n - 1;
191          *max_ref_arg = MAX (*max_ref_arg, n);
192        }
193      else
194        /* Oops; that was actually the width and/or 0 padding flag.
195           Step back and read it again.  */
196        format = begin;
197    }
198
199  /* Check for spec modifiers.  */
200  while (*format == ' ' || *format == '+' || *format == '-' ||
201         *format == '#' || *format == '0' || *format == '\'')
202    switch (*format++)
203      {
204      case ' ':
205        /* Output a space in place of a sign, when there is no sign.  */
206        spec->info.space = 1;
207        break;
208      case '+':
209        /* Always output + or - for numbers.  */
210        spec->info.showsign = 1;
211        break;
212      case '-':
213        /* Left-justify things.  */
214        spec->info.left = 1;
215        break;
216      case '#':
217        /* Use the "alternate form":
218           Hex has 0x or 0X, FP always has a decimal point.  */
219        spec->info.alt = 1;
220        break;
221      case '0':
222        /* Pad with 0s.  */
223        spec->info.pad = '0';
224        break;
225      case '\'':
226        /* Show grouping in numbers if the locale information
227           indicates any.  */
228        spec->info.group = 1;
229        break;
230      }
231  if (spec->info.left)
232    spec->info.pad = ' ';
233
234  /* Get the field width.  */
235  spec->width_arg = -1;
236  spec->info.width = 0;
237  if (*format == '*')
238    {
239      /* The field width is given in an argument.
240         A negative field width indicates left justification.  */
241      const char *begin = ++format;
242
243      if (isdigit (*format))
244        {
245          /* The width argument might be found in a positional parameter.  */
246          n = read_int (&format);
247
248          if (n > 0 && *format == '$')
249            {
250              spec->width_arg = n - 1;
251              *max_ref_arg = MAX (*max_ref_arg, n);
252              ++format;         /* Skip '$'.  */
253            }
254        }
255
256      if (spec->width_arg < 0)
257        {
258          /* Not in a positional parameter.  Consume one argument.  */
259          spec->width_arg = posn++;
260          ++nargs;
261          format = begin;       /* Step back and reread.  */
262        }
263    }
264  else if (isdigit (*format))
265    /* Constant width specification.  */
266    spec->info.width = read_int (&format);
267
268  /* Get the precision.  */
269  spec->prec_arg = -1;
270  /* -1 means none given; 0 means explicit 0.  */
271  spec->info.prec = -1;
272  if (*format == '.')
273    {
274      ++format;
275      if (*format == '*')
276        {
277          /* The precision is given in an argument.  */
278          const char *begin = ++format;
279
280          if (isdigit (*format))
281            {
282              n = read_int (&format);
283
284              if (n > 0 && *format == '$')
285                {
286                  spec->prec_arg = n - 1;
287                  *max_ref_arg = MAX (*max_ref_arg, n);
288                  ++format;
289                }
290            }
291
292          if (spec->prec_arg < 0)
293            {
294              /* Not in a positional parameter.  */
295              spec->prec_arg = posn++;
296              ++nargs;
297              format = begin;
298            }
299        }
300      else if (isdigit (*format))
301        spec->info.prec = read_int (&format);
302      else
303        /* "%.?" is treated like "%.0?".  */
304        spec->info.prec = 0;
305    }
306
307  /* Check for type modifiers.  */
308  spec->info.is_long_double = 0;
309  spec->info.is_short = 0;
310  spec->info.is_char = 0;
311  spec->info.is_long = 0;
312  spec->info.is_longlong = 0;
313
314  while (*format == 'h' || *format == 'l' || *format == 'L' ||
315         *format == 'Z' || *format == 'q')
316    switch (*format++)
317      {
318      case 'h':
319        if (spec->info.is_short)
320          /* int's are char's.  */
321          spec->info.is_char = 1;
322        else
323          /* int's are short int's.  */
324          spec->info.is_short = 1;
325        break;
326      case 'l':
327        if (spec->info.is_long)
328          /* A double `l' is equivalent to an `L'.  */
329          spec->info.is_longlong = spec->info.is_long_double = 1;
330        else
331          /* int's are long int's.  */
332          spec->info.is_long = 1;
333        break;
334      case 'L':
335        /* double's are long double's, and int's are long long int's.  */
336        spec->info.is_long_double = spec->info.is_longlong = 1;
337        break;
338      case 'j':
339        /* int's are intmax_t's.  */
340        assert (sizeof(uintmax_t) <= sizeof(unsigned long_long_int));
341        spec->info.is_longlong = sizeof(uintmax_t) > sizeof(unsigned long int);
342        spec->info.is_long = sizeof(uintmax_t) > sizeof(unsigned int);
343        break;
344      case 't':
345        /* int's are ptrdiff_t's.  */
346        assert (sizeof(ptrdiff_t) <= sizeof(unsigned long_long_int));
347        spec->info.is_longlong = sizeof(ptrdiff_t) > sizeof(unsigned long int);
348        spec->info.is_long = sizeof(ptrdiff_t) > sizeof(unsigned int);
349        break;
350      case 'z':
351      case 'Z':
352        /* int's are size_t's.  */
353        assert (sizeof(size_t) <= sizeof(unsigned long_long_int));
354        spec->info.is_longlong = sizeof(size_t) > sizeof(unsigned long int);
355        spec->info.is_long = sizeof(size_t) > sizeof(unsigned int);
356        break;
357      case 'q':
358        /* 4.4 uses this for long long.  */
359        spec->info.is_longlong = 1;
360        break;
361      }
362
363  /* Get the format specification.  */
364  spec->info.spec = *format++;
365  /* Find the data argument types of a built-in spec.  */
366  spec->ndata_args = 1;
367
368  switch (spec->info.spec)
369    {
370    case 'i':
371    case 'd':
372    case 'u':
373    case 'o':
374    case 'X':
375    case 'x':
376      if (spec->info.is_longlong)
377        spec->data_arg_type = PA_INT|PA_FLAG_LONG_LONG;
378      else if (spec->info.is_long)
379        spec->data_arg_type = PA_INT|PA_FLAG_LONG;
380      else if (spec->info.is_char)
381        spec->data_arg_type = PA_INT|PA_FLAG_CHAR;
382      else if (spec->info.is_short)
383        spec->data_arg_type = PA_INT|PA_FLAG_SHORT;
384      else
385        spec->data_arg_type = PA_INT;
386      break;
387    case 'e':
388    case 'E':
389    case 'f':
390    case 'g':
391    case 'G':
392      if (spec->info.is_long_double)
393        spec->data_arg_type = PA_DOUBLE|PA_FLAG_LONG_DOUBLE;
394      else
395        spec->data_arg_type = PA_DOUBLE;
396      break;
397    case 'c':
398      spec->data_arg_type = PA_CHAR;
399      break;
400    case 's':
401      spec->data_arg_type = PA_STRING;
402      break;
403    case 'p':
404      spec->data_arg_type = PA_POINTER|PA_FLAG_PTR;
405      break;
406    case 'n':
407      spec->data_arg_type = PA_INT|PA_FLAG_PTR;
408      break;
409
410    case 'm':
411    default:
412      /* An unknown spec will consume no args.  */
413      spec->ndata_args = 0;
414      break;
415    }
416
417  if (spec->data_arg == -1 && spec->ndata_args > 0)
418    {
419      /* There are args consumed, but no positional spec.
420         Use the next sequential arg position.  */
421      spec->data_arg = posn;
422      posn += spec->ndata_args;
423      nargs += spec->ndata_args;
424    }
425
426  if (spec->info.spec == '\0')
427    /* Format ended before this spec was complete.  */
428    spec->end_of_fmt = spec->next_fmt = format - 1;
429  else
430    {
431      /* Find the next format spec.  */
432      spec->end_of_fmt = format;
433      spec->next_fmt = find_spec (format);
434    }
435
436  return nargs;
437}
Note: See TracBrowser for help on using the repository browser.