1 /* GNU m4 -- A simple macro processor
3 Copyright (C) 1989-1994, 2006-2014, 2016-2017, 2020-2021 Free
4 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 <https://www.gnu.org/licenses/>.
22 /* printf like formatting for m4. */
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 /* Parse STR as an integer, reporting warnings. */
32 arg_int (const char *str)
36 size_t len = strlen (str);
40 M4ERROR ((warning_status, 0, _("empty string treated as 0")));
44 value = strtol (str, &endp, 10);
46 M4ERROR ((warning_status, 0, _("non-numeric argument %s"), str));
47 else if (c_isspace (*str))
48 M4ERROR ((warning_status, 0, _("leading whitespace ignored")));
49 else if (errno == ERANGE || (int) value != value)
50 M4ERROR ((warning_status, 0, _("numeric overflow detected")));
54 /* Parse STR as a long, reporting warnings. */
56 arg_long (const char *str)
60 size_t len = strlen (str);
64 M4ERROR ((warning_status, 0, _("empty string treated as 0")));
68 value = strtol (str, &endp, 10);
70 M4ERROR ((warning_status, 0, _("non-numeric argument %s"), str));
71 else if (c_isspace (*str))
72 M4ERROR ((warning_status, 0, _("leading whitespace ignored")));
73 else if (errno == ERANGE)
74 M4ERROR ((warning_status, 0, _("numeric overflow detected")));
78 /* Parse STR as a double, reporting warnings. */
80 arg_double (const char *str)
84 size_t len = strlen (str);
88 M4ERROR ((warning_status, 0, _("empty string treated as 0")));
92 value = strtod (str, &endp);
94 M4ERROR ((warning_status, 0, _("non-numeric argument %s"), str));
95 else if (c_isspace (*str))
96 M4ERROR ((warning_status, 0, _("leading whitespace ignored")));
97 else if (errno == ERANGE)
98 M4ERROR ((warning_status, 0, _("numeric overflow detected")));
102 #define ARG_INT(argc, argv) \
104 (--argc, argv++, arg_int (TOKEN_DATA_TEXT (argv[-1]))))
106 #define ARG_LONG(argc, argv) \
108 (--argc, argv++, arg_long (TOKEN_DATA_TEXT (argv[-1]))))
110 #define ARG_STR(argc, argv) \
111 ((argc == 0) ? "" : \
112 (--argc, argv++, TOKEN_DATA_TEXT (argv[-1])))
114 #define ARG_DOUBLE(argc, argv) \
116 (--argc, argv++, arg_double (TOKEN_DATA_TEXT (argv[-1]))))
119 /*------------------------------------------------------------------.
120 | The main formatting function. Output is placed on the obstack |
121 | OBS, the first argument in ARGV is the formatting string, and the |
122 | rest is arguments for the string. Warn rather than invoke |
123 | unspecified behavior in the underlying printf when we do not |
124 | recognize a format. |
125 `------------------------------------------------------------------*/
128 expand_format (struct obstack *obs, int argc, token_data **argv)
130 const char *f; /* format control string */
131 const char *fmt; /* position within f */
132 char fstart[] = "%'+- 0#*.*hhd"; /* current format spec */
133 char *p; /* position within fstart */
134 unsigned char c; /* a simple character */
137 char flags; /* flags to use in fstart */
139 THOUSANDS = 0x01, /* ' */
141 MINUS = 0x04, /* - */
145 DONE = 0x40 /* no more flags */
148 /* Precision specifiers. */
149 int width; /* minimum field width */
150 int prec; /* precision */
151 char lflag; /* long flag */
153 /* Specifiers we are willing to accept. ok['x'] implies %x is ok.
154 Various modifiers reduce the set, in order to avoid undefined
155 behavior in printf. */
158 /* Buffer and stuff. */
159 char *str; /* malloc'd buffer of formatted text */
160 enum {CHAR, INT, LONG, DOUBLE, STR} datatype;
162 f = fmt = ARG_STR (argc, argv);
163 memset (ok, 0, sizeof ok);
166 const char *percent = strchr (fmt, '%');
169 obstack_grow (obs, fmt, strlen (fmt));
172 obstack_grow (obs, fmt, percent - fmt);
177 obstack_1grow (obs, '%');
182 p = fstart + 1; /* % */
184 ok['a'] = ok['A'] = ok['c'] = ok['d'] = ok['e'] = ok['E']
185 = ok['f'] = ok['F'] = ok['g'] = ok['G'] = ok['i'] = ok['o']
186 = ok['s'] = ok['u'] = ok['x'] = ok['X'] = 1;
194 case '\'': /* thousands separator */
195 ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E']
196 = ok['o'] = ok['s'] = ok['x'] = ok['X'] = 0;
200 case '+': /* mandatory sign */
201 ok['c'] = ok['o'] = ok['s'] = ok['u'] = ok['x'] = ok['X'] = 0;
205 case ' ': /* space instead of positive sign */
206 ok['c'] = ok['o'] = ok['s'] = ok['u'] = ok['x'] = ok['X'] = 0;
210 case '0': /* zero padding */
211 ok['c'] = ok['s'] = 0;
215 case '#': /* alternate output */
216 ok['c'] = ok['d'] = ok['i'] = ok['s'] = ok['u'] = 0;
220 case '-': /* left justification */
229 while (!(flags & DONE) && fmt++);
230 if (flags & THOUSANDS)
243 /* Minimum field width; an explicit 0 is the same as not giving
249 width = ARG_INT (argc, argv);
253 while (c_isdigit (*fmt))
255 width = 10 * width + *fmt - '0';
259 /* Maximum precision; an explicit negative precision is the same
260 as not giving the precision. A lone '.' is a precision of 0. */
269 prec = ARG_INT (argc, argv);
275 while (c_isdigit (*fmt))
277 prec = 10 * prec + *fmt - '0';
283 /* Length modifiers. We don't yet recognize ll, j, t, or z. */
289 ok['c'] = ok['s'] = 0;
291 else if (*fmt == 'h')
300 ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E'] = ok['f'] = ok['F']
301 = ok['g'] = ok['G'] = ok['s'] = 0;
305 if (sizeof ok <= c || !ok[c])
307 M4ERROR ((warning_status, 0,
308 _("Warning: unrecognized specifier in `%s'"), f));
314 /* Specifiers. We don't yet recognize C, S, n, or p. */
319 p -= 2; /* %.*c is undefined, so undo the '.*'. */
332 datatype = lflag ? LONG : INT;
352 /* Our constructed format string in fstart is safe. */
353 #if 4 < __GNUC__ + (6 <= __GNUC_MINOR__)
354 # pragma GCC diagnostic push
355 # pragma GCC diagnostic ignored "-Wformat-nonliteral"
361 str = xasprintf (fstart, width, ARG_INT(argc, argv));
365 str = xasprintf (fstart, width, prec, ARG_INT(argc, argv));
369 str = xasprintf (fstart, width, prec, ARG_LONG(argc, argv));
373 str = xasprintf (fstart, width, prec, ARG_DOUBLE(argc, argv));
377 str = xasprintf (fstart, width, prec, ARG_STR(argc, argv));
383 #if 4 < __GNUC__ + (6 <= __GNUC_MINOR__)
384 # pragma GCC diagnostic pop
387 /* NULL was returned on failure, such as invalid format string. For
388 now, just silently ignore that bad specifier. */
392 obstack_grow (obs, str, strlen (str));