maint: run update-copyright for 2014.
[m4/ericb.git] / modules / format.c
blob2154f4d3c33d368112d39b91bc7235de36a84d63
1 /* GNU m4 -- A simple macro processor
2 Copyright (C) 1989-1994, 2001, 2006-2010, 2013-2014 Free Software
3 Foundation, Inc.
5 This file is part of GNU M4.
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.
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.
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/>.
21 /* printf like formatting for m4. */
23 #include "vasnprintf.h"
25 /* Simple varargs substitute. We assume int and unsigned int are the
26 same size; likewise for long and unsigned long. We do not yet
27 handle long double or long long. */
29 /* Parse STR of length LEN as an integer, reporting warnings on behalf
30 of ME. */
31 static int
32 arg_int (struct m4 *context, const m4_call_info *me, const char *str,
33 size_t len)
35 char *endp;
36 long value;
38 /* TODO - also allow parsing `'a' or `"a' which results in the
39 numeric value of 'a', as in printf(1). */
40 if (!len)
42 m4_warn (context, 0, me, _("empty string treated as 0"));
43 return 0;
45 errno = 0;
46 value = strtol (str, &endp, 10);
47 if (endp - str != len)
48 m4_warn (context, 0, me, _("non-numeric argument %s"),
49 quotearg_style_mem (locale_quoting_style, str, len));
50 else if (isspace (to_uchar (*str)))
51 m4_warn (context, 0, me, _("leading whitespace ignored"));
52 else if (errno == ERANGE || (int) value != value)
53 m4_warn (context, 0, me, _("numeric overflow detected"));
54 return value;
57 /* Parse STR of length LEN as a long, reporting warnings on behalf of
58 ME. */
59 static long
60 arg_long (struct m4 *context, const m4_call_info *me, const char *str,
61 size_t len)
63 char *endp;
64 long value;
66 /* TODO - also allow parsing `'a' or `"a' which results in the
67 numeric value of 'a', as in printf(1). */
68 if (!len)
70 m4_warn (context, 0, me, _("empty string treated as 0"));
71 return 0L;
73 errno = 0;
74 value = strtol (str, &endp, 10);
75 if (endp - str != len)
76 m4_warn (context, 0, me, _("non-numeric argument %s"),
77 quotearg_style_mem (locale_quoting_style, str, len));
78 else if (isspace (to_uchar (*str)))
79 m4_warn (context, 0, me, _("leading whitespace ignored"));
80 else if (errno == ERANGE)
81 m4_warn (context, 0, me, _("numeric overflow detected"));
82 return value;
85 /* Check STR of length LEN for embedded NUL, reporting warnings on
86 behalf of ME. */
87 static const char *
88 arg_string (struct m4 *context, const m4_call_info *me, const char *str,
89 size_t len)
91 if (strlen (str) < len)
92 m4_warn (context, 0, me, _("argument %s truncated"),
93 quotearg_style_mem (locale_quoting_style, str, len));
94 return str;
97 /* Parse STR of length LEN as a double, reporting warnings on behalf
98 of ME. */
99 static double
100 arg_double (struct m4 *context, const m4_call_info *me, const char *str,
101 size_t len)
103 char *endp;
104 double value;
106 if (!len)
108 m4_warn (context, 0, me, _("empty string treated as 0"));
109 return 0.0;
111 errno = 0;
112 value = strtod (str, &endp);
113 if (endp - str != len)
114 m4_warn (context, 0, me, _("non-numeric argument %s"),
115 quotearg_style_mem (locale_quoting_style, str, len));
116 else if (isspace (to_uchar (*str)))
117 m4_warn (context, 0, me, _("leading whitespace ignored"));
118 else if (errno == ERANGE)
119 m4_warn (context, 0, me, _("numeric overflow detected"));
120 return value;
123 #define ARG_INT(i, argc, argv) \
124 ((argc <= ++i) ? 0 : arg_int (context, me, M4ARG (i), M4ARGLEN (i)))
126 #define ARG_LONG(i, argc, argv) \
127 ((argc <= ++i) ? 0L : arg_long (context, me, M4ARG (i), M4ARGLEN (i)))
129 #define ARG_STR(i, argc, argv) \
130 ((argc <= ++i) ? "" : arg_string (context, me, M4ARG (i), M4ARGLEN (i)))
132 #define ARG_DOUBLE(i, argc, argv) \
133 ((argc <= ++i) ? 0.0 : arg_double (context, me, M4ARG (i), M4ARGLEN (i)))
136 /* The main formatting function. Output is placed on the obstack OBS,
137 the first argument in ARGV is the formatting string, and the rest
138 is arguments for the string. Warn rather than invoke unspecified
139 behavior in the underlying printf when we do not recognize a
140 format. */
142 static void
143 format (m4 *context, m4_obstack *obs, int argc, m4_macro_args *argv)
145 const m4_call_info *me = m4_arg_info (argv);
146 const char *f; /* Format control string. */
147 size_t f_len; /* Length of f. */
148 const char *fmt; /* Position within f. */
149 char fstart[] = "%'+- 0#*.*hhd"; /* Current format spec. */
150 char *p; /* Position within fstart. */
151 unsigned char c; /* A simple character. */
152 int i = 1; /* Index within argc used so far. */
153 bool valid_format = true; /* True if entire format string ok. */
155 /* Flags. */
156 char flags; /* Flags to use in fstart. */
157 enum {
158 THOUSANDS = 0x01, /* '\''. */
159 PLUS = 0x02, /* '+'. */
160 MINUS = 0x04, /* '-'. */
161 SPACE = 0x08, /* ' '. */
162 ZERO = 0x10, /* '0'. */
163 ALT = 0x20, /* '#'. */
164 DONE = 0x40 /* No more flags. */
167 /* Precision specifiers. */
168 int width; /* Minimum field width. */
169 int prec; /* Precision. */
170 char lflag; /* Long flag. */
172 /* Specifiers we are willing to accept. ok['x'] implies %x is ok.
173 Various modifiers reduce the set, in order to avoid undefined
174 behavior in printf. */
175 char ok[128];
177 /* Check that formatted text succeeded with correct type. */
178 int result = 0;
179 enum {CHAR, INT, LONG, DOUBLE, STR} datatype;
181 f = fmt = M4ARG (1);
182 f_len = M4ARGLEN (1);
183 assert (!f[f_len]); /* Requiring a terminating NUL makes parsing simpler. */
184 memset (ok, 0, sizeof ok);
185 while (1)
187 const char *percent = (char *) memchr (fmt, '%', f_len);
188 if (!percent)
190 obstack_grow (obs, fmt, f_len);
191 break;
193 obstack_grow (obs, fmt, percent - fmt);
194 f_len -= percent - fmt + 1;
195 fmt = percent + 1;
197 if (*fmt == '%')
199 obstack_1grow (obs, '%');
200 fmt++;
201 f_len--;
202 continue;
205 p = fstart + 1; /* % */
206 lflag = 0;
207 ok['a'] = ok['A'] = ok['c'] = ok['d'] = ok['e'] = ok['E']
208 = ok['f'] = ok['F'] = ok['g'] = ok['G'] = ok['i'] = ok['o']
209 = ok['s'] = ok['u'] = ok['x'] = ok['X'] = 1;
211 /* Parse flags. */
212 flags = 0;
215 switch (*fmt)
217 case '\'': /* thousands separator */
218 ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E']
219 = ok['o'] = ok['s'] = ok['x'] = ok['X'] = 0;
220 flags |= THOUSANDS;
221 break;
223 case '+': /* mandatory sign */
224 ok['c'] = ok['o'] = ok['s'] = ok['u'] = ok['x'] = ok['X'] = 0;
225 flags |= PLUS;
226 break;
228 case ' ': /* space instead of positive sign */
229 ok['c'] = ok['o'] = ok['s'] = ok['u'] = ok['x'] = ok['X'] = 0;
230 flags |= SPACE;
231 break;
233 case '0': /* zero padding */
234 ok['c'] = ok['s'] = 0;
235 flags |= ZERO;
236 break;
238 case '#': /* alternate output */
239 ok['c'] = ok['d'] = ok['i'] = ok['s'] = ok['u'] = 0;
240 flags |= ALT;
241 break;
243 case '-': /* left justification */
244 flags |= MINUS;
245 break;
247 default:
248 flags |= DONE;
249 break;
252 while (!(flags & DONE) && (f_len--, fmt++));
253 if (flags & THOUSANDS)
254 *p++ = '\'';
255 if (flags & PLUS)
256 *p++ = '+';
257 if (flags & MINUS)
258 *p++ = '-';
259 if (flags & SPACE)
260 *p++ = ' ';
261 if (flags & ZERO)
262 *p++ = '0';
263 if (flags & ALT)
264 *p++ = '#';
266 /* Minimum field width; an explicit 0 is the same as not giving
267 the width. */
268 width = 0;
269 *p++ = '*';
270 if (*fmt == '*')
272 width = ARG_INT (i, argc, argv);
273 fmt++;
274 f_len--;
276 else
277 while (isdigit ((unsigned char) *fmt))
279 width = 10 * width + *fmt - '0';
280 fmt++;
281 f_len--;
284 /* Maximum precision; an explicit negative precision is the same
285 as not giving the precision. A lone '.' is a precision of 0. */
286 prec = -1;
287 *p++ = '.';
288 *p++ = '*';
289 if (*fmt == '.')
291 ok['c'] = 0;
292 f_len--;
293 if (*(++fmt) == '*')
295 prec = ARG_INT (i, argc, argv);
296 ++fmt;
297 f_len--;
299 else
301 prec = 0;
302 while (isdigit ((unsigned char) *fmt))
304 prec = 10 * prec + *fmt - '0';
305 fmt++;
306 f_len--;
311 /* Length modifiers. We don't yet recognize ll, j, t, or z. */
312 if (*fmt == 'l')
314 *p++ = 'l';
315 lflag = 1;
316 fmt++;
317 f_len--;
318 ok['c'] = ok['s'] = 0;
320 else if (*fmt == 'h')
322 *p++ = 'h';
323 fmt++;
324 f_len--;
325 if (*fmt == 'h')
327 *p++ = 'h';
328 fmt++;
329 f_len--;
331 ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E'] = ok['f'] = ok['F']
332 = ok['g'] = ok['G'] = ok['s'] = 0;
335 c = *fmt;
336 if (sizeof ok <= c || !ok[c] || !f_len)
338 m4_warn (context, 0, me, _("unrecognized specifier in %s"),
339 quotearg_style_mem (locale_quoting_style, f, M4ARGLEN (1)));
340 valid_format = false;
341 if (f_len > 0)
343 fmt++;
344 f_len--;
346 continue;
348 fmt++;
349 f_len--;
351 /* Specifiers. We don't yet recognize C, S, n, or p. */
352 switch (c)
354 case 'c':
355 datatype = CHAR;
356 p -= 2; /* %.*c is undefined, so undo the '.*'. */
357 break;
359 case 's':
360 datatype = STR;
361 break;
363 case 'd':
364 case 'i':
365 case 'o':
366 case 'x':
367 case 'X':
368 case 'u':
369 datatype = lflag ? LONG : INT;
370 break;
372 case 'a':
373 case 'A':
374 case 'e':
375 case 'E':
376 case 'f':
377 case 'F':
378 case 'g':
379 case 'G':
380 datatype = DOUBLE;
381 break;
383 default:
384 abort ();
386 *p++ = c;
387 *p = '\0';
389 switch (datatype)
391 case CHAR:
392 result = obstack_printf (obs, fstart, width,
393 ARG_INT (i, argc, argv));
394 break;
396 case INT:
397 result = obstack_printf (obs, fstart, width, prec,
398 ARG_INT (i, argc, argv));
399 break;
401 case LONG:
402 result = obstack_printf (obs, fstart, width, prec,
403 ARG_LONG (i, argc, argv));
404 break;
406 case DOUBLE:
407 result = obstack_printf (obs, fstart, width, prec,
408 ARG_DOUBLE (i, argc, argv));
409 break;
411 case STR:
412 result = obstack_printf (obs, fstart, width, prec,
413 ARG_STR (i, argc, argv));
414 break;
416 default:
417 abort ();
419 /* Since obstack_printf can only fail with EILSEQ or EINVAL, but
420 we constructed fstart, the result should not be negative. */
421 assert (0 <= result);
423 if (valid_format)
424 m4_bad_argc (context, argc, me, i, i, true);