m4/modules/format.c
Eric Blake 1dd98ea10e Avoid -Wshadow compiler warnings.
* m4/output.c (threshold_diversion_CB): s/div/diversion/.
* m4/macro.c (make_argv_ref, arg_symbol, m4_arg_symbol)
(m4_is_arg_text, m4_is_arg_func, m4_arg_text, m4_arg_empty)
(m4_arg_len, m4_arg_func, m4__arg_print, m4_make_argv_ref)
(m4_push_arg, m4__push_arg_quote): s/index/arg/.
* modules/format.c (format): Likewise.
* modules/m4.c (ifelse): Likewise.

Signed-off-by: Eric Blake <ebb9@byu.net>
2008-05-01 12:05:03 -06:00

405 lines
9.9 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* GNU m4 -- A simple macro processor
Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2001, 2006, 2007,
2008 Free Software Foundation, Inc.
This file is part of GNU M4.
GNU M4 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GNU M4 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* printf like formatting for m4. */
#include "vasnprintf.h"
/* Simple varargs substitute. We assume int and unsigned int are the
same size; likewise for long and unsigned long. We do not yet
handle long double or long long. */
/* Parse STR as an integer, reporting warnings on behalf of ME. */
static int
arg_int (struct m4 *context, const char *me, const char *str)
{
char *endp;
long value;
/* TODO - also allow parsing `'a' or `"a' which results in the
numeric value of 'a', as in printf(1). */
if (*str == '\0')
{
m4_warn (context, 0, me, _("empty string treated as 0"));
return 0;
}
errno = 0;
value = strtol (str, &endp, 10);
if (*endp != '\0')
m4_warn (context, 0, me, _("non-numeric argument `%s'"), str);
else if (isspace (to_uchar (*str)))
m4_warn (context, 0, me, _("leading whitespace ignored"));
else if (errno == ERANGE || (int) value != value)
m4_warn (context, 0, me, _("numeric overflow detected"));
return value;
}
/* Parse STR as a long, reporting warnings on behalf of ME. */
static long
arg_long (struct m4 *context, const char *me, const char *str)
{
char *endp;
long value;
/* TODO - also allow parsing `'a' or `"a' which results in the
numeric value of 'a', as in printf(1). */
if (*str == '\0')
{
m4_warn (context, 0, me, _("empty string treated as 0"));
return 0L;
}
errno = 0;
value = strtol (str, &endp, 10);
if (*endp != '\0')
m4_warn (context, 0, me, _("non-numeric argument `%s'"), str);
else if (isspace (to_uchar (*str)))
m4_warn (context, 0, me, _("leading whitespace ignored"));
else if (errno == ERANGE)
m4_warn (context, 0, me, _("numeric overflow detected"));
return value;
}
/* Parse STR as a double, reporting warnings on behalf of ME. */
static double
arg_double (struct m4 *context, const char *me, const char *str)
{
char *endp;
double value;
if (*str == '\0')
{
m4_warn (context, 0, me, _("empty string treated as 0"));
return 0.0;
}
errno = 0;
value = strtod (str, &endp);
if (*endp != '\0')
m4_warn (context, 0, me, _("non-numeric argument `%s'"), str);
else if (isspace (to_uchar (*str)))
m4_warn (context, 0, me, _("leading whitespace ignored"));
else if (errno == ERANGE)
m4_warn (context, 0, me, _("numeric overflow detected"));
return value;
}
#define ARG_INT(i, argc, argv) \
((argc <= ++i) ? 0 : arg_int (context, me, M4ARG (i)))
#define ARG_LONG(i, argc, argv) \
((argc <= ++i) ? 0L : arg_long (context, me, M4ARG (i)))
#define ARG_STR(i, argc, argv) \
((argc <= ++i) ? "" : M4ARG (i))
#define ARG_DOUBLE(i, argc, argv) \
((argc <= ++i) ? 0.0 : arg_double (context, me, M4ARG (i)))
/* The main formatting function. Output is placed on the obstack OBS,
the first argument in ARGV is the formatting string, and the rest
is arguments for the string. Warn rather than invoke unspecified
behavior in the underlying printf when we do not recognize a
format. */
static void
format (m4 *context, m4_obstack *obs, int argc, m4_macro_args *argv)
{
const char *me = M4ARG (0); /* Macro name. */
const char *f; /* Format control string. */
const char *fmt; /* Position within f. */
char fstart[] = "%'+- 0#*.*hhd"; /* Current format spec. */
char *p; /* Position within fstart. */
unsigned char c; /* A simple character. */
int i = 0; /* Index within argc used so far. */
bool valid_format = true; /* True if entire format string ok. */
/* Flags. */
char flags; /* Flags to use in fstart. */
enum {
THOUSANDS = 0x01, /* '\''. */
PLUS = 0x02, /* '+'. */
MINUS = 0x04, /* '-'. */
SPACE = 0x08, /* ' '. */
ZERO = 0x10, /* '0'. */
ALT = 0x20, /* '#'. */
DONE = 0x40 /* No more flags. */
};
/* Precision specifiers. */
int width; /* Minimum field width. */
int prec; /* Precision. */
char lflag; /* Long flag. */
/* Specifiers we are willing to accept. ok['x'] implies %x is ok.
Various modifiers reduce the set, in order to avoid undefined
behavior in printf. */
char ok[128];
/* Buffer and stuff. */
char *base; /* Current position in obs. */
size_t len; /* Length of formatted text. */
char *str; /* Malloc'd buffer of formatted text. */
enum {CHAR, INT, LONG, DOUBLE, STR} datatype;
f = fmt = ARG_STR (i, argc, argv);
memset (ok, 0, sizeof ok);
while (true)
{
while ((c = *fmt++) != '%')
{
if (c == '\0')
{
if (valid_format)
m4_bad_argc (context, argc, me, i, i, true);
return;
}
obstack_1grow (obs, c);
}
if (*fmt == '%')
{
obstack_1grow (obs, '%');
fmt++;
continue;
}
p = fstart + 1; /* % */
lflag = 0;
ok['a'] = ok['A'] = ok['c'] = ok['d'] = ok['e'] = ok['E']
= ok['f'] = ok['F'] = ok['g'] = ok['G'] = ok['i'] = ok['o']
= ok['s'] = ok['u'] = ok['x'] = ok['X'] = 1;
/* Parse flags. */
flags = 0;
do
{
switch (*fmt)
{
case '\'': /* thousands separator */
ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E']
= ok['o'] = ok['s'] = ok['x'] = ok['X'] = 0;
flags |= THOUSANDS;
break;
case '+': /* mandatory sign */
ok['c'] = ok['o'] = ok['s'] = ok['u'] = ok['x'] = ok['X'] = 0;
flags |= PLUS;
break;
case ' ': /* space instead of positive sign */
ok['c'] = ok['o'] = ok['s'] = ok['u'] = ok['x'] = ok['X'] = 0;
flags |= SPACE;
break;
case '0': /* zero padding */
ok['c'] = ok['s'] = 0;
flags |= ZERO;
break;
case '#': /* alternate output */
ok['c'] = ok['d'] = ok['i'] = ok['s'] = ok['u'] = 0;
flags |= ALT;
break;
case '-': /* left justification */
flags |= MINUS;
break;
default:
flags |= DONE;
break;
}
}
while (!(flags & DONE) && fmt++);
if (flags & THOUSANDS)
*p++ = '\'';
if (flags & PLUS)
*p++ = '+';
if (flags & MINUS)
*p++ = '-';
if (flags & SPACE)
*p++ = ' ';
if (flags & ZERO)
*p++ = '0';
if (flags & ALT)
*p++ = '#';
/* Minimum field width; an explicit 0 is the same as not giving
the width. */
width = 0;
*p++ = '*';
if (*fmt == '*')
{
width = ARG_INT (i, argc, argv);
fmt++;
}
else
while (isdigit ((unsigned char) *fmt))
{
width = 10 * width + *fmt - '0';
fmt++;
}
/* Maximum precision; an explicit negative precision is the same
as not giving the precision. A lone '.' is a precision of 0. */
prec = -1;
*p++ = '.';
*p++ = '*';
if (*fmt == '.')
{
ok['c'] = 0;
if (*(++fmt) == '*')
{
prec = ARG_INT (i, argc, argv);
++fmt;
}
else
{
prec = 0;
while (isdigit ((unsigned char) *fmt))
{
prec = 10 * prec + *fmt - '0';
fmt++;
}
}
}
/* Length modifiers. We don't yet recognize ll, j, t, or z. */
if (*fmt == 'l')
{
*p++ = 'l';
lflag = 1;
fmt++;
ok['c'] = ok['s'] = 0;
}
else if (*fmt == 'h')
{
*p++ = 'h';
fmt++;
if (*fmt == 'h')
{
*p++ = 'h';
fmt++;
}
ok['a'] = ok['A'] = ok['c'] = ok['e'] = ok['E'] = ok['f'] = ok['F']
= ok['g'] = ok['G'] = ok['s'] = 0;
}
c = *fmt++;
if (c > sizeof ok || !ok[c])
{
m4_warn (context, 0, me, _("unrecognized specifier in `%s'"), f);
valid_format = false;
if (c == '\0')
fmt--;
continue;
}
/* Specifiers. We don't yet recognize C, S, n, or p. */
switch (c)
{
case 'c':
datatype = CHAR;
p -= 2; /* %.*c is undefined, so undo the '.*'. */
break;
case 's':
datatype = STR;
break;
case 'd':
case 'i':
case 'o':
case 'x':
case 'X':
case 'u':
datatype = lflag ? LONG : INT;
break;
case 'a':
case 'A':
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
datatype = DOUBLE;
break;
default:
abort ();
}
*p++ = c;
*p = '\0';
base = obstack_next_free (obs);
len = obstack_room (obs);
switch (datatype)
{
case CHAR:
str = asnprintf (base, &len, fstart, width,
ARG_INT (i, argc, argv));
break;
case INT:
str = asnprintf (base, &len, fstart, width, prec,
ARG_INT (i, argc, argv));
break;
case LONG:
str = asnprintf (base, &len, fstart, width, prec,
ARG_LONG (i, argc, argv));
break;
case DOUBLE:
str = asnprintf (base, &len, fstart, width, prec,
ARG_DOUBLE (i, argc, argv));
break;
case STR:
str = asnprintf (base, &len, fstart, width, prec,
ARG_STR (i, argc, argv));
break;
default:
abort ();
}
if (str == NULL)
/* NULL is unexpected (EILSEQ and EINVAL are not possible
based on our construction of fstart, leaving only ENOMEM,
which should always be fatal). */
m4_error (context, EXIT_FAILURE, errno, me,
_("unable to format output for `%s'"), f);
else if (str == base)
/* The output was already computed in place, but we need to
account for its size. */
obstack_blank_fast (obs, len);
else
{
/* The output exceeded available obstack space, copy the
allocated string. */
obstack_grow (obs, str, len);
free (str);
}
}
}