More whitespace cleanup.
[m4.git] / modules / format.c
blobb772290a1d9b26133472165acdc717d1bf36a30b
1 /* GNU m4 -- A simple macro processor
2 Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2001, 2006, 2007,
3 2008, 2009, 2010 Free Software 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 (c > sizeof ok || !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 continue;
343 fmt++;
344 f_len--;
346 /* Specifiers. We don't yet recognize C, S, n, or p. */
347 switch (c)
349 case 'c':
350 datatype = CHAR;
351 p -= 2; /* %.*c is undefined, so undo the '.*'. */
352 break;
354 case 's':
355 datatype = STR;
356 break;
358 case 'd':
359 case 'i':
360 case 'o':
361 case 'x':
362 case 'X':
363 case 'u':
364 datatype = lflag ? LONG : INT;
365 break;
367 case 'a':
368 case 'A':
369 case 'e':
370 case 'E':
371 case 'f':
372 case 'F':
373 case 'g':
374 case 'G':
375 datatype = DOUBLE;
376 break;
378 default:
379 abort ();
381 *p++ = c;
382 *p = '\0';
384 switch (datatype)
386 case CHAR:
387 result = obstack_printf (obs, fstart, width,
388 ARG_INT (i, argc, argv));
389 break;
391 case INT:
392 result = obstack_printf (obs, fstart, width, prec,
393 ARG_INT (i, argc, argv));
394 break;
396 case LONG:
397 result = obstack_printf (obs, fstart, width, prec,
398 ARG_LONG (i, argc, argv));
399 break;
401 case DOUBLE:
402 result = obstack_printf (obs, fstart, width, prec,
403 ARG_DOUBLE (i, argc, argv));
404 break;
406 case STR:
407 result = obstack_printf (obs, fstart, width, prec,
408 ARG_STR (i, argc, argv));
409 break;
411 default:
412 abort ();
414 /* Since obstack_printf can only fail with EILSEQ or EINVAL, but
415 we constructed fstart, the result should not be negative. */
416 assert (0 <= result);
418 if (valid_format)
419 m4_bad_argc (context, argc, me, i, i, true);