43a23cdd82eaa82cce6e0760d2f5dbe04f21b34b
[platform/upstream/m4.git] / src / format.c
1 /* GNU m4 -- A simple macro processor
2
3    Copyright (C) 1989-1994, 2006-2011 Free Software Foundation, Inc.
4
5    This file is part of GNU M4.
6
7    GNU M4 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 3 of the License, or
10    (at your option) any later version.
11
12    GNU M4 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
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /* printf like formatting for m4.  */
22
23 #include "m4.h"
24 #include "xvasprintf.h"
25
26 /* Simple varargs substitute.  We assume int and unsigned int are the
27    same size; likewise for long and unsigned long.  */
28
29 /* Parse STR as an integer, reporting warnings.  */
30 static int
31 arg_int (const char *str)
32 {
33   char *endp;
34   long value;
35   size_t len = strlen (str);
36
37   if (!len)
38     {
39       M4ERROR ((warning_status, 0, _("empty string treated as 0")));
40       return 0;
41     }
42   errno = 0;
43   value = strtol (str, &endp, 10);
44   if (endp - str - len)
45     M4ERROR ((warning_status, 0, _("non-numeric argument %s"), str));
46   else if (isspace (to_uchar (*str)))
47     M4ERROR ((warning_status, 0, _("leading whitespace ignored")));
48   else if (errno == ERANGE || (int) value != value)
49     M4ERROR ((warning_status, 0, _("numeric overflow detected")));
50   return value;
51 }
52
53 /* Parse STR as a long, reporting warnings.  */
54 static long
55 arg_long (const char *str)
56 {
57   char *endp;
58   long value;
59   size_t len = strlen (str);
60
61   if (!len)
62     {
63       M4ERROR ((warning_status, 0, _("empty string treated as 0")));
64       return 0L;
65     }
66   errno = 0;
67   value = strtol (str, &endp, 10);
68   if (endp - str - len)
69     M4ERROR ((warning_status, 0, _("non-numeric argument %s"), str));
70   else if (isspace (to_uchar (*str)))
71     M4ERROR ((warning_status, 0, _("leading whitespace ignored")));
72   else if (errno == ERANGE)
73     M4ERROR ((warning_status, 0, _("numeric overflow detected")));
74   return value;
75 }
76
77 /* Parse STR as a double, reporting warnings.  */
78 static double
79 arg_double (const char *str)
80 {
81   char *endp;
82   double value;
83   size_t len = strlen (str);
84
85   if (!len)
86     {
87       M4ERROR ((warning_status, 0, _("empty string treated as 0")));
88       return 0.0;
89     }
90   errno = 0;
91   value = strtod (str, &endp);
92   if (endp - str - len)
93     M4ERROR ((warning_status, 0, _("non-numeric argument %s"), str));
94   else if (isspace (to_uchar (*str)))
95     M4ERROR ((warning_status, 0, _("leading whitespace ignored")));
96   else if (errno == ERANGE)
97     M4ERROR ((warning_status, 0, _("numeric overflow detected")));
98   return value;
99 }
100
101 #define ARG_INT(argc, argv) \
102         ((argc == 0) ? 0 : \
103          (--argc, argv++, arg_int (TOKEN_DATA_TEXT (argv[-1]))))
104
105 #define ARG_LONG(argc, argv) \
106         ((argc == 0) ? 0 : \
107          (--argc, argv++, arg_long (TOKEN_DATA_TEXT (argv[-1]))))
108
109 #define ARG_STR(argc, argv) \
110         ((argc == 0) ? "" : \
111          (--argc, argv++, TOKEN_DATA_TEXT (argv[-1])))
112
113 #define ARG_DOUBLE(argc, argv) \
114         ((argc == 0) ? 0 : \
115          (--argc, argv++, arg_double (TOKEN_DATA_TEXT (argv[-1]))))
116 \f
117
118 /*------------------------------------------------------------------.
119 | The main formatting function.  Output is placed on the obstack    |
120 | OBS, the first argument in ARGV is the formatting string, and the |
121 | rest is arguments for the string.  Warn rather than invoke        |
122 | unspecified behavior in the underlying printf when we do not      |
123 | recognize a format.                                               |
124 `------------------------------------------------------------------*/
125
126 void
127 expand_format (struct obstack *obs, int argc, token_data **argv)
128 {
129   const char *f;                        /* format control string */
130   const char *fmt;                      /* position within f */
131   char fstart[] = "%'+- 0#*.*hhd";      /* current format spec */
132   char *p;                              /* position within fstart */
133   unsigned char c;                      /* a simple character */
134
135   /* Flags.  */
136   char flags;                           /* flags to use in fstart */
137   enum {
138     THOUSANDS   = 0x01, /* ' */
139     PLUS        = 0x02, /* + */
140     MINUS       = 0x04, /* - */
141     SPACE       = 0x08, /*   */
142     ZERO        = 0x10, /* 0 */
143     ALT         = 0x20, /* # */
144     DONE        = 0x40  /* no more flags */
145   };
146
147   /* Precision specifiers.  */
148   int width;                    /* minimum field width */
149   int prec;                     /* precision */
150   char lflag;                   /* long flag */
151
152   /* Specifiers we are willing to accept.  ok['x'] implies %x is ok.
153      Various modifiers reduce the set, in order to avoid undefined
154      behavior in printf.  */
155   char ok[128];
156
157   /* Buffer and stuff.  */
158   char *str;                    /* malloc'd buffer of formatted text */
159   enum {CHAR, INT, LONG, DOUBLE, STR} datatype;
160
161   f = fmt = ARG_STR (argc, argv);
162   memset (ok, 0, sizeof ok);
163   while (1)
164     {
165       const char *percent = strchr (fmt, '%');
166       if (!percent)
167         {
168           obstack_grow (obs, fmt, strlen (fmt));
169           return;
170         }
171       obstack_grow (obs, fmt, percent - fmt);
172       fmt = percent + 1;
173
174       if (*fmt == '%')
175         {
176           obstack_1grow (obs, '%');
177           fmt++;
178           continue;
179         }
180
181       p = fstart + 1; /* % */
182       lflag = 0;
183       ok['a'] = ok['A'] = ok['c'] = ok['d'] = ok['e'] = ok['E']
184         = ok['f'] = ok['F'] = ok['g'] = ok['G'] = ok['i'] = ok['o']
185         = ok['s'] = ok['u'] = ok['x'] = ok['X'] = 1;
186
187       /* Parse flags.  */
188       flags = 0;
189       do
190         {
191           switch (*fmt)
192             {
193             case '\'': /* thousands separator */
194               ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E']
195                 = ok['o'] = ok['s'] = ok['x'] = ok['X'] = 0;
196               flags |= THOUSANDS;
197               break;
198
199             case '+': /* mandatory sign */
200               ok['c'] = ok['o'] = ok['s'] = ok['u'] = ok['x'] = ok['X'] = 0;
201               flags |= PLUS;
202               break;
203
204             case ' ': /* space instead of positive sign */
205               ok['c'] = ok['o'] = ok['s'] = ok['u'] = ok['x'] = ok['X'] = 0;
206               flags |= SPACE;
207               break;
208
209             case '0': /* zero padding */
210               ok['c'] = ok['s'] = 0;
211               flags |= ZERO;
212               break;
213
214             case '#': /* alternate output */
215               ok['c'] = ok['d'] = ok['i'] = ok['s'] = ok['u'] = 0;
216               flags |= ALT;
217               break;
218
219             case '-': /* left justification */
220               flags |= MINUS;
221               break;
222
223             default:
224               flags |= DONE;
225               break;
226             }
227         }
228       while (!(flags & DONE) && fmt++);
229       if (flags & THOUSANDS)
230         *p++ = '\'';
231       if (flags & PLUS)
232         *p++ = '+';
233       if (flags & MINUS)
234         *p++ = '-';
235       if (flags & SPACE)
236         *p++ = ' ';
237       if (flags & ZERO)
238         *p++ = '0';
239       if (flags & ALT)
240         *p++ = '#';
241
242       /* Minimum field width; an explicit 0 is the same as not giving
243          the width.  */
244       width = 0;
245       *p++ = '*';
246       if (*fmt == '*')
247         {
248           width = ARG_INT (argc, argv);
249           fmt++;
250         }
251       else
252         while (isdigit (to_uchar (*fmt)))
253           {
254             width = 10 * width + *fmt - '0';
255             fmt++;
256           }
257
258       /* Maximum precision; an explicit negative precision is the same
259          as not giving the precision.  A lone '.' is a precision of 0.  */
260       prec = -1;
261       *p++ = '.';
262       *p++ = '*';
263       if (*fmt == '.')
264         {
265           ok['c'] = 0;
266           if (*(++fmt) == '*')
267             {
268               prec = ARG_INT (argc, argv);
269               ++fmt;
270             }
271           else
272             {
273               prec = 0;
274               while (isdigit (to_uchar (*fmt)))
275                 {
276                   prec = 10 * prec + *fmt - '0';
277                   fmt++;
278                 }
279             }
280         }
281
282       /* Length modifiers.  We don't yet recognize ll, j, t, or z.  */
283       if (*fmt == 'l')
284         {
285           *p++ = 'l';
286           lflag = 1;
287           fmt++;
288           ok['c'] = ok['s'] = 0;
289         }
290       else if (*fmt == 'h')
291         {
292           *p++ = 'h';
293           fmt++;
294           if (*fmt == 'h')
295             {
296               *p++ = 'h';
297               fmt++;
298             }
299           ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E'] = ok['f'] = ok['F']
300             = ok['g'] = ok['G'] = ok['s'] = 0;
301         }
302
303       c = *fmt++;
304       if (sizeof ok <= c || !ok[c])
305         {
306           M4ERROR ((warning_status, 0,
307                     "Warning: unrecognized specifier in `%s'", f));
308           if (c == '\0')
309             fmt--;
310           continue;
311         }
312
313       /* Specifiers.  We don't yet recognize C, S, n, or p.  */
314       switch (c)
315         {
316         case 'c':
317           datatype = CHAR;
318           p -= 2; /* %.*c is undefined, so undo the '.*'.  */
319           break;
320
321         case 's':
322           datatype = STR;
323           break;
324
325         case 'd':
326         case 'i':
327         case 'o':
328         case 'x':
329         case 'X':
330         case 'u':
331           datatype = lflag ? LONG : INT;
332           break;
333
334         case 'a':
335         case 'A':
336         case 'e':
337         case 'E':
338         case 'f':
339         case 'F':
340         case 'g':
341         case 'G':
342           datatype = DOUBLE;
343           break;
344
345         default:
346           abort ();
347         }
348       *p++ = c;
349       *p = '\0';
350
351       switch (datatype)
352         {
353         case CHAR:
354           str = xasprintf (fstart, width, ARG_INT(argc, argv));
355           break;
356
357         case INT:
358           str = xasprintf (fstart, width, prec, ARG_INT(argc, argv));
359           break;
360
361         case LONG:
362           str = xasprintf (fstart, width, prec, ARG_LONG(argc, argv));
363           break;
364
365         case DOUBLE:
366           str = xasprintf (fstart, width, prec, ARG_DOUBLE(argc, argv));
367           break;
368
369         case STR:
370           str = xasprintf (fstart, width, prec, ARG_STR(argc, argv));
371           break;
372
373         default:
374           abort();
375         }
376
377       /* NULL was returned on failure, such as invalid format string.  For
378          now, just silently ignore that bad specifier.  */
379       if (str == NULL)
380         continue;
381
382       obstack_grow (obs, str, strlen (str));
383       free (str);
384     }
385 }