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
32 arg_int (struct m4
*context
, const m4_call_info
*me
, const char *str
,
38 /* TODO - also allow parsing `'a' or `"a' which results in the
39 numeric value of 'a', as in printf(1). */
42 m4_warn (context
, 0, me
, _("empty string treated as 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"));
57 /* Parse STR of length LEN as a long, reporting warnings on behalf of
60 arg_long (struct m4
*context
, const m4_call_info
*me
, const char *str
,
66 /* TODO - also allow parsing `'a' or `"a' which results in the
67 numeric value of 'a', as in printf(1). */
70 m4_warn (context
, 0, me
, _("empty string treated as 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"));
85 /* Check STR of length LEN for embedded NUL, reporting warnings on
88 arg_string (struct m4
*context
, const m4_call_info
*me
, const char *str
,
91 if (strlen (str
) < len
)
92 m4_warn (context
, 0, me
, _("argument %s truncated"),
93 quotearg_style_mem (locale_quoting_style
, str
, len
));
97 /* Parse STR of length LEN as a double, reporting warnings on behalf
100 arg_double (struct m4
*context
, const m4_call_info
*me
, const char *str
,
108 m4_warn (context
, 0, me
, _("empty string treated as 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"));
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
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. */
156 char flags
; /* Flags to use in fstart. */
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. */
177 /* Check that formatted text succeeded with correct type. */
179 enum {CHAR
, INT
, LONG
, DOUBLE
, STR
} datatype
;
182 f_len
= M4ARGLEN (1);
183 assert (!f
[f_len
]); /* Requiring a terminating NUL makes parsing simpler. */
184 memset (ok
, 0, sizeof ok
);
187 const char *percent
= (char *) memchr (fmt
, '%', f_len
);
190 obstack_grow (obs
, fmt
, f_len
);
193 obstack_grow (obs
, fmt
, percent
- fmt
);
194 f_len
-= percent
- fmt
+ 1;
199 obstack_1grow (obs
, '%');
205 p
= fstart
+ 1; /* % */
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;
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;
223 case '+': /* mandatory sign */
224 ok
['c'] = ok
['o'] = ok
['s'] = ok
['u'] = ok
['x'] = ok
['X'] = 0;
228 case ' ': /* space instead of positive sign */
229 ok
['c'] = ok
['o'] = ok
['s'] = ok
['u'] = ok
['x'] = ok
['X'] = 0;
233 case '0': /* zero padding */
234 ok
['c'] = ok
['s'] = 0;
238 case '#': /* alternate output */
239 ok
['c'] = ok
['d'] = ok
['i'] = ok
['s'] = ok
['u'] = 0;
243 case '-': /* left justification */
252 while (!(flags
& DONE
) && (f_len
--, fmt
++));
253 if (flags
& THOUSANDS
)
266 /* Minimum field width; an explicit 0 is the same as not giving
272 width
= ARG_INT (i
, argc
, argv
);
277 while (isdigit ((unsigned char) *fmt
))
279 width
= 10 * width
+ *fmt
- '0';
284 /* Maximum precision; an explicit negative precision is the same
285 as not giving the precision. A lone '.' is a precision of 0. */
295 prec
= ARG_INT (i
, argc
, argv
);
302 while (isdigit ((unsigned char) *fmt
))
304 prec
= 10 * prec
+ *fmt
- '0';
311 /* Length modifiers. We don't yet recognize ll, j, t, or z. */
318 ok
['c'] = ok
['s'] = 0;
320 else if (*fmt
== 'h')
331 ok
['a'] = ok
['A'] = ok
['c'] = ok
['e'] = ok
['E'] = ok
['f'] = ok
['F']
332 = ok
['g'] = ok
['G'] = ok
['s'] = 0;
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;
346 /* Specifiers. We don't yet recognize C, S, n, or p. */
351 p
-= 2; /* %.*c is undefined, so undo the '.*'. */
364 datatype
= lflag
? LONG
: INT
;
387 result
= obstack_printf (obs
, fstart
, width
,
388 ARG_INT (i
, argc
, argv
));
392 result
= obstack_printf (obs
, fstart
, width
, prec
,
393 ARG_INT (i
, argc
, argv
));
397 result
= obstack_printf (obs
, fstart
, width
, prec
,
398 ARG_LONG (i
, argc
, argv
));
402 result
= obstack_printf (obs
, fstart
, width
, prec
,
403 ARG_DOUBLE (i
, argc
, argv
));
407 result
= obstack_printf (obs
, fstart
, width
, prec
,
408 ARG_STR (i
, argc
, argv
));
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
);
419 m4_bad_argc (context
, argc
, me
, i
, i
, true);