Never let printf failures go undetected.
[m4.git] / src / format.c
blob0cc283ad0ce8b0b52ccc0a438bf479a1ab7ee0d9
1 /* GNU m4 -- A simple macro processor
3 Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2006, 2007
4 Free Software Foundation, Inc.
6 This file is part of GNU M4.
8 GNU M4 is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 GNU M4 is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 /* printf like formatting for m4. */
24 #include "m4.h"
25 #include "xvasprintf.h"
27 /* Simple varargs substitute. We assume int and unsigned int are the
28 same size; likewise for long and unsigned long. */
30 #define ARG_INT(argc, argv) \
31 ((argc == 0) ? 0 : \
32 (--argc, argv++, atoi (TOKEN_DATA_TEXT (argv[-1]))))
34 #define ARG_LONG(argc, argv) \
35 ((argc == 0) ? 0 : \
36 (--argc, argv++, atol (TOKEN_DATA_TEXT (argv[-1]))))
38 #define ARG_STR(argc, argv) \
39 ((argc == 0) ? "" : \
40 (--argc, argv++, TOKEN_DATA_TEXT (argv[-1])))
42 #define ARG_DOUBLE(argc, argv) \
43 ((argc == 0) ? 0 : \
44 (--argc, argv++, atof (TOKEN_DATA_TEXT (argv[-1]))))
47 /*------------------------------------------------------------------.
48 | The main formatting function. Output is placed on the obstack |
49 | OBS, the first argument in ARGV is the formatting string, and the |
50 | rest is arguments for the string. Warn rather than invoke |
51 | unspecified behavior in the underlying printf when we do not |
52 | recognize a format. |
53 `------------------------------------------------------------------*/
55 void
56 format (struct obstack *obs, int argc, token_data **argv)
58 const char *f; /* format control string */
59 const char *fmt; /* position within f */
60 char fstart[] = "%'+- 0#*.*hhd"; /* current format spec */
61 char *p; /* position within fstart */
62 unsigned char c; /* a simple character */
64 /* Flags. */
65 char flags; /* flags to use in fstart */
66 enum {
67 THOUSANDS = 0x01, /* ' */
68 PLUS = 0x02, /* + */
69 MINUS = 0x04, /* - */
70 SPACE = 0x08, /* */
71 ZERO = 0x10, /* 0 */
72 ALT = 0x20, /* # */
73 DONE = 0x40 /* no more flags */
76 /* Precision specifiers. */
77 int width; /* minimum field width */
78 int prec; /* precision */
79 char lflag; /* long flag */
81 /* Specifiers we are willing to accept. ok['x'] implies %x is ok.
82 Various modifiers reduce the set, in order to avoid undefined
83 behavior in printf. */
84 char ok[128];
86 /* Buffer and stuff. */
87 char *str; /* malloc'd buffer of formatted text */
88 enum {CHAR, INT, LONG, DOUBLE, STR} datatype;
90 f = fmt = ARG_STR (argc, argv);
91 memset (ok, 0, sizeof ok);
92 for (;;)
94 while ((c = *fmt++) != '%')
96 if (c == '\0')
97 return;
98 obstack_1grow (obs, c);
101 if (*fmt == '%')
103 obstack_1grow (obs, '%');
104 fmt++;
105 continue;
108 p = fstart + 1; /* % */
109 lflag = 0;
110 ok['a'] = ok['A'] = ok['c'] = ok['d'] = ok['e'] = ok['E']
111 = ok['f'] = ok['F'] = ok['g'] = ok['G'] = ok['i'] = ok['o']
112 = ok['s'] = ok['u'] = ok['x'] = ok['X'] = 1;
114 /* Parse flags. */
115 flags = 0;
118 switch (*fmt)
120 case '\'': /* thousands separator */
121 ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E']
122 = ok['o'] = ok['s'] = ok['x'] = ok['X'] = 0;
123 flags |= THOUSANDS;
124 break;
126 case '+': /* mandatory sign */
127 ok['c'] = ok['o'] = ok['s'] = ok['u'] = ok['x'] = ok['X'] = 0;
128 flags |= PLUS;
129 break;
131 case ' ': /* space instead of positive sign */
132 ok['c'] = ok['o'] = ok['s'] = ok['u'] = ok['x'] = ok['X'] = 0;
133 flags |= SPACE;
134 break;
136 case '0': /* zero padding */
137 ok['c'] = ok['s'] = 0;
138 flags |= ZERO;
139 break;
141 case '#': /* alternate output */
142 ok['c'] = ok['d'] = ok['i'] = ok['s'] = ok['u'] = 0;
143 flags |= ALT;
144 break;
146 case '-': /* left justification */
147 flags |= MINUS;
148 break;
150 default:
151 flags |= DONE;
152 break;
155 while (!(flags & DONE) && fmt++);
156 if (flags & THOUSANDS)
157 *p++ = '\'';
158 if (flags & PLUS)
159 *p++ = '+';
160 if (flags & MINUS)
161 *p++ = '-';
162 if (flags & SPACE)
163 *p++ = ' ';
164 if (flags & ZERO)
165 *p++ = '0';
166 if (flags & ALT)
167 *p++ = '#';
169 /* Minimum field width; an explicit 0 is the same as not giving
170 the width. */
171 width = 0;
172 *p++ = '*';
173 if (*fmt == '*')
175 width = ARG_INT (argc, argv);
176 fmt++;
178 else
179 while (isdigit (to_uchar (*fmt)))
181 width = 10 * width + *fmt - '0';
182 fmt++;
185 /* Maximum precision; an explicit negative precision is the same
186 as not giving the precision. A lone '.' is a precision of 0. */
187 prec = -1;
188 *p++ = '.';
189 *p++ = '*';
190 if (*fmt == '.')
192 ok['c'] = 0;
193 if (*(++fmt) == '*')
195 prec = ARG_INT (argc, argv);
196 ++fmt;
198 else
200 prec = 0;
201 while (isdigit (to_uchar (*fmt)))
203 prec = 10 * prec + *fmt - '0';
204 fmt++;
209 /* Length modifiers. We don't yet recognize ll, j, t, or z. */
210 if (*fmt == 'l')
212 *p++ = 'l';
213 lflag = 1;
214 fmt++;
215 ok['c'] = ok['s'] = 0;
217 else if (*fmt == 'h')
219 *p++ = 'h';
220 fmt++;
221 if (*fmt == 'h')
223 *p++ = 'h';
224 fmt++;
226 ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E'] = ok['f'] = ok['F']
227 = ok['g'] = ok['G'] = ok['s'] = 0;
230 c = *fmt++;
231 if (c > sizeof ok || !ok[c])
233 M4ERROR ((warning_status, 0,
234 "Warning: unrecognized specifier in `%s'", f));
235 if (c == '\0')
236 fmt--;
237 continue;
240 /* Specifiers. We don't yet recognize C, S, n, or p. */
241 switch (c)
243 case 'c':
244 datatype = CHAR;
245 p -= 2; /* %.*c is undefined, so undo the '.*'. */
246 break;
248 case 's':
249 datatype = STR;
250 break;
252 case 'd':
253 case 'i':
254 case 'o':
255 case 'x':
256 case 'X':
257 case 'u':
258 datatype = lflag ? LONG : INT;
259 break;
261 case 'a':
262 case 'A':
263 case 'e':
264 case 'E':
265 case 'f':
266 case 'F':
267 case 'g':
268 case 'G':
269 datatype = DOUBLE;
270 break;
272 default:
273 abort ();
275 *p++ = c;
276 *p = '\0';
278 switch (datatype)
280 case CHAR:
281 str = xasprintf (fstart, width, ARG_INT(argc, argv));
282 break;
284 case INT:
285 str = xasprintf (fstart, width, prec, ARG_INT(argc, argv));
286 break;
288 case LONG:
289 str = xasprintf (fstart, width, prec, ARG_LONG(argc, argv));
290 break;
292 case DOUBLE:
293 str = xasprintf (fstart, width, prec, ARG_DOUBLE(argc, argv));
294 break;
296 case STR:
297 str = xasprintf (fstart, width, prec, ARG_STR(argc, argv));
298 break;
300 default:
301 abort();
304 /* NULL was returned on failure, such as invalid format string.
305 Issue a warning, then proceed. */
306 if (str == NULL)
308 M4ERROR ((warning_status, 0,
309 "Warning: unable to format output for `%s'", f));
310 continue;
313 obstack_grow (obs, str, strlen (str));
314 free (str);