#include "m4.h"
extern FILE *popen ();
#include "regex.h"
#define ARG(i) (argc > (i) ? TOKEN_DATA_TEXT (argv[i]) : "")
#define DECLARE(name) \
static void name _((struct obstack *, int, token_data **))
DECLARE (m4___file__);
DECLARE (m4___line__);
DECLARE (m4_builtin);
DECLARE (m4_changecom);
DECLARE (m4_changequote);
#ifdef ENABLE_CHANGEWORD
DECLARE (m4_changeword);
#endif
DECLARE (m4_debugmode);
DECLARE (m4_debugfile);
DECLARE (m4_decr);
DECLARE (m4_define);
DECLARE (m4_defn);
DECLARE (m4_divert);
DECLARE (m4_divnum);
DECLARE (m4_dnl);
DECLARE (m4_dumpdef);
DECLARE (m4_errprint);
DECLARE (m4_esyscmd);
DECLARE (m4_eval);
DECLARE (m4_format);
DECLARE (m4_ifdef);
DECLARE (m4_ifelse);
DECLARE (m4_include);
DECLARE (m4_incr);
DECLARE (m4_index);
DECLARE (m4_indir);
DECLARE (m4_len);
DECLARE (m4_m4exit);
DECLARE (m4_m4wrap);
DECLARE (m4_maketemp);
DECLARE (m4_patsubst);
DECLARE (m4_popdef);
DECLARE (m4_pushdef);
DECLARE (m4_regexp);
DECLARE (m4_shift);
DECLARE (m4_sinclude);
DECLARE (m4_substr);
DECLARE (m4_syscmd);
DECLARE (m4_sysval);
DECLARE (m4_traceoff);
DECLARE (m4_traceon);
DECLARE (m4_translit);
DECLARE (m4_undefine);
DECLARE (m4_undivert);
#undef DECLARE
static builtin
builtin_tab[] =
{
{ "__file__", TRUE, FALSE, FALSE, m4___file__ },
{ "__line__", TRUE, FALSE, FALSE, m4___line__ },
{ "builtin", TRUE, FALSE, TRUE, m4_builtin },
{ "changecom", FALSE, FALSE, FALSE, m4_changecom },
{ "changequote", FALSE, FALSE, FALSE, m4_changequote },
#ifdef ENABLE_CHANGEWORD
{ "changeword", TRUE, FALSE, FALSE, m4_changeword },
#endif
{ "debugmode", TRUE, FALSE, FALSE, m4_debugmode },
{ "debugfile", TRUE, FALSE, FALSE, m4_debugfile },
{ "decr", FALSE, FALSE, TRUE, m4_decr },
{ "define", FALSE, TRUE, TRUE, m4_define },
{ "defn", FALSE, FALSE, TRUE, m4_defn },
{ "divert", FALSE, FALSE, FALSE, m4_divert },
{ "divnum", FALSE, FALSE, FALSE, m4_divnum },
{ "dnl", FALSE, FALSE, FALSE, m4_dnl },
{ "dumpdef", FALSE, FALSE, FALSE, m4_dumpdef },
{ "errprint", FALSE, FALSE, FALSE, m4_errprint },
{ "esyscmd", TRUE, FALSE, TRUE, m4_esyscmd },
{ "eval", FALSE, FALSE, TRUE, m4_eval },
{ "format", TRUE, FALSE, FALSE, m4_format },
{ "ifdef", FALSE, FALSE, TRUE, m4_ifdef },
{ "ifelse", FALSE, FALSE, TRUE, m4_ifelse },
{ "include", FALSE, FALSE, TRUE, m4_include },
{ "incr", FALSE, FALSE, TRUE, m4_incr },
{ "index", FALSE, FALSE, TRUE, m4_index },
{ "indir", TRUE, FALSE, FALSE, m4_indir },
{ "len", FALSE, FALSE, TRUE, m4_len },
{ "m4exit", FALSE, FALSE, FALSE, m4_m4exit },
{ "m4wrap", FALSE, FALSE, FALSE, m4_m4wrap },
{ "maketemp", FALSE, FALSE, TRUE, m4_maketemp },
{ "patsubst", TRUE, FALSE, TRUE, m4_patsubst },
{ "popdef", FALSE, FALSE, TRUE, m4_popdef },
{ "pushdef", FALSE, TRUE, TRUE, m4_pushdef },
{ "regexp", TRUE, FALSE, TRUE, m4_regexp },
{ "shift", FALSE, FALSE, FALSE, m4_shift },
{ "sinclude", FALSE, FALSE, TRUE, m4_sinclude },
{ "substr", FALSE, FALSE, TRUE, m4_substr },
{ "syscmd", FALSE, FALSE, TRUE, m4_syscmd },
{ "sysval", FALSE, FALSE, FALSE, m4_sysval },
{ "traceoff", FALSE, FALSE, FALSE, m4_traceoff },
{ "traceon", FALSE, FALSE, FALSE, m4_traceon },
{ "translit", FALSE, FALSE, TRUE, m4_translit },
{ "undefine", FALSE, FALSE, TRUE, m4_undefine },
{ "undivert", FALSE, FALSE, FALSE, m4_undivert },
{ 0, FALSE, FALSE, FALSE, 0 },
};
static predefined const
predefined_tab[] =
{
{ "unix", "__unix__", "" },
{ NULL, "__gnu__", "" },
{ NULL, NULL, NULL },
};
const builtin *
find_builtin_by_addr (builtin_func *func)
{
const builtin *bp;
for (bp = &builtin_tab[0]; bp->name != NULL; bp++)
if (bp->func == func)
return bp;
return NULL;
}
const builtin *
find_builtin_by_name (const char *name)
{
const builtin *bp;
for (bp = &builtin_tab[0]; bp->name != NULL; bp++)
if (strcmp (bp->name, name) == 0)
return bp;
return NULL;
}
void
define_builtin (const char *name, const builtin *bp, symbol_lookup mode,
boolean traced)
{
symbol *sym;
sym = lookup_symbol (name, mode);
SYMBOL_TYPE (sym) = TOKEN_FUNC;
SYMBOL_MACRO_ARGS (sym) = bp->groks_macro_args;
SYMBOL_BLIND_NO_ARGS (sym) = bp->blind_if_no_args;
SYMBOL_FUNC (sym) = bp->func;
SYMBOL_TRACED (sym) = traced;
}
void
define_user_macro (const char *name, const char *text, symbol_lookup mode)
{
symbol *s;
s = lookup_symbol (name, mode);
if (SYMBOL_TYPE (s) == TOKEN_TEXT)
xfree (SYMBOL_TEXT (s));
SYMBOL_TYPE (s) = TOKEN_TEXT;
SYMBOL_TEXT (s) = xstrdup (text);
}
void
builtin_init (void)
{
const builtin *bp;
const predefined *pp;
char *string;
for (bp = &builtin_tab[0]; bp->name != NULL; bp++)
if (!no_gnu_extensions || !bp->gnu_extension)
if (prefix_all_builtins)
{
string = (char *) xmalloc (strlen (bp->name) + 4);
strcpy (string, "m4_");
strcat (string, bp->name);
define_builtin (string, bp, SYMBOL_INSERT, FALSE);
free (string);
}
else
define_builtin (bp->name, bp, SYMBOL_INSERT, FALSE);
for (pp = &predefined_tab[0]; pp->func != NULL; pp++)
if (no_gnu_extensions)
{
if (pp->unix_name != NULL)
define_user_macro (pp->unix_name, pp->func, SYMBOL_INSERT);
}
else
{
if (pp->gnu_name != NULL)
define_user_macro (pp->gnu_name, pp->func, SYMBOL_INSERT);
}
}
static boolean
bad_argc (token_data *name, int argc, int min, int max)
{
boolean isbad = FALSE;
if (min > 0 && argc < min)
{
if (!suppress_warnings)
M4ERROR ((warning_status, 0,
"Warning: Too few arguments to built-in `%s'",
TOKEN_DATA_TEXT (name)));
isbad = TRUE;
}
else if (max > 0 && argc > max && !suppress_warnings)
M4ERROR ((warning_status, 0,
"Warning: Excess arguments to built-in `%s' ignored",
TOKEN_DATA_TEXT (name)));
return isbad;
}
static boolean
numeric_arg (token_data *macro, const char *arg, int *valuep)
{
char *endp;
if (*arg == 0 || (*valuep = strtol (arg, &endp, 10), *endp != 0))
{
M4ERROR ((warning_status, 0,
"Non-numeric argument to built-in `%s'",
TOKEN_DATA_TEXT (macro)));
return FALSE;
}
return TRUE;
}
static char const digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
static const char *
ntoa (register eval_t value, int radix)
{
boolean negative;
unsigned_eval_t uvalue;
static char str[256];
register char *s = &str[sizeof str];
*--s = '\0';
if (value < 0)
{
negative = TRUE;
uvalue = (unsigned_eval_t) -value;
}
else
{
negative = FALSE;
uvalue = (unsigned_eval_t) value;
}
do
{
*--s = digits[uvalue % radix];
uvalue /= radix;
}
while (uvalue > 0);
if (negative)
*--s = '-';
return s;
}
static void
shipout_int (struct obstack *obs, int val)
{
const char *s;
s = ntoa ((eval_t) val, 10);
obstack_grow (obs, s, strlen (s));
}
static void
dump_args (struct obstack *obs, int argc, token_data **argv,
const char *sep, boolean quoted)
{
int i;
size_t len = strlen (sep);
for (i = 1; i < argc; i++)
{
if (i > 1)
obstack_grow (obs, sep, len);
if (quoted)
obstack_grow (obs, lquote.string, lquote.length);
obstack_grow (obs, TOKEN_DATA_TEXT (argv[i]),
strlen (TOKEN_DATA_TEXT (argv[i])));
if (quoted)
obstack_grow (obs, rquote.string, rquote.length);
}
}
static void
define_macro (int argc, token_data **argv, symbol_lookup mode)
{
const builtin *bp;
if (bad_argc (argv[0], argc, 2, 3))
return;
if (TOKEN_DATA_TYPE (argv[1]) != TOKEN_TEXT)
return;
if (argc == 2)
{
define_user_macro (ARG (1), "", mode);
return;
}
switch (TOKEN_DATA_TYPE (argv[2]))
{
case TOKEN_TEXT:
define_user_macro (ARG (1), ARG (2), mode);
break;
case TOKEN_FUNC:
bp = find_builtin_by_addr (TOKEN_DATA_FUNC (argv[2]));
if (bp == NULL)
return;
else
define_builtin (ARG (1), bp, mode, TOKEN_DATA_FUNC_TRACED (argv[2]));
break;
default:
M4ERROR ((warning_status, 0,
"INTERNAL ERROR: Bad token data type in define_macro ()"));
abort ();
}
return;
}
static void
m4_define (struct obstack *obs, int argc, token_data **argv)
{
define_macro (argc, argv, SYMBOL_INSERT);
}
static void
m4_undefine (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 2, 2))
return;
lookup_symbol (ARG (1), SYMBOL_DELETE);
}
static void
m4_pushdef (struct obstack *obs, int argc, token_data **argv)
{
define_macro (argc, argv, SYMBOL_PUSHDEF);
}
static void
m4_popdef (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 2, 2))
return;
lookup_symbol (ARG (1), SYMBOL_POPDEF);
}
static void
m4_ifdef (struct obstack *obs, int argc, token_data **argv)
{
symbol *s;
const char *result;
if (bad_argc (argv[0], argc, 3, 4))
return;
s = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
if (s != NULL)
result = ARG (2);
else if (argc == 4)
result = ARG (3);
else
result = NULL;
if (result != NULL)
obstack_grow (obs, result, strlen (result));
}
static void
m4_ifelse (struct obstack *obs, int argc, token_data **argv)
{
const char *result;
token_data *argv0;
if (argc == 2)
return;
if (bad_argc (argv[0], argc, 4, -1))
return;
else
bad_argc (argv[0], (argc + 2) % 3, -1, 1);
argv0 = argv[0];
argv++;
argc--;
result = NULL;
while (result == NULL)
if (strcmp (ARG (0), ARG (1)) == 0)
result = ARG (2);
else
switch (argc)
{
case 3:
return;
case 4:
case 5:
result = ARG (3);
break;
default:
argc -= 3;
argv += 3;
}
obstack_grow (obs, result, strlen (result));
}
struct dump_symbol_data
{
struct obstack *obs;
symbol **base;
int size;
};
static void
dump_symbol (symbol *sym, struct dump_symbol_data *data)
{
if (!SYMBOL_SHADOWED (sym) && SYMBOL_TYPE (sym) != TOKEN_VOID)
{
obstack_blank (data->obs, sizeof (symbol *));
data->base = (symbol **) obstack_base (data->obs);
data->base[data->size++] = sym;
}
}
static int
dumpdef_cmp (const voidstar s1, const voidstar s2)
{
return strcmp (SYMBOL_NAME (* (symbol *const *) s1),
SYMBOL_NAME (* (symbol *const *) s2));
}
static void
m4_dumpdef (struct obstack *obs, int argc, token_data **argv)
{
symbol *s;
int i;
struct dump_symbol_data data;
const builtin *bp;
data.obs = obs;
data.base = (symbol **) obstack_base (obs);
data.size = 0;
if (argc == 1)
{
hack_all_symbols (dump_symbol, (char *) &data);
}
else
{
for (i = 1; i < argc; i++)
{
s = lookup_symbol (TOKEN_DATA_TEXT (argv[i]), SYMBOL_LOOKUP);
if (s != NULL && SYMBOL_TYPE (s) != TOKEN_VOID)
dump_symbol (s, &data);
else
M4ERROR ((warning_status, 0,
"Undefined name %s", TOKEN_DATA_TEXT (argv[i])));
}
}
(void) obstack_finish (obs);
qsort ((char *) data.base, data.size, sizeof (symbol *), dumpdef_cmp);
for (; data.size > 0; --data.size, data.base++)
{
DEBUG_PRINT1 ("%s:\t", SYMBOL_NAME (data.base[0]));
switch (SYMBOL_TYPE (data.base[0]))
{
case TOKEN_TEXT:
if (debug_level & DEBUG_TRACE_QUOTE)
DEBUG_PRINT3 ("%s%s%s\n",
lquote.string, SYMBOL_TEXT (data.base[0]), rquote.string);
else
DEBUG_PRINT1 ("%s\n", SYMBOL_TEXT (data.base[0]));
break;
case TOKEN_FUNC:
bp = find_builtin_by_addr (SYMBOL_FUNC (data.base[0]));
if (bp == NULL)
{
M4ERROR ((warning_status, 0, "\
INTERNAL ERROR: Builtin not found in builtin table!"));
abort ();
}
DEBUG_PRINT1 ("<%s>\n", bp->name);
break;
default:
M4ERROR ((warning_status, 0,
"INTERNAL ERROR: Bad token data type in m4_dumpdef ()"));
abort ();
break;
}
}
}
static void
m4_builtin (struct obstack *obs, int argc, token_data **argv)
{
const builtin *bp;
const char *name = ARG (1);
if (bad_argc (argv[0], argc, 2, -1))
return;
bp = find_builtin_by_name (name);
if (bp == NULL)
M4ERROR ((warning_status, 0,
"Undefined name %s", name));
else
(*bp->func) (obs, argc - 1, argv + 1);
}
static void
m4_indir (struct obstack *obs, int argc, token_data **argv)
{
symbol *s;
const char *name = ARG (1);
if (bad_argc (argv[0], argc, 1, -1))
return;
s = lookup_symbol (name, SYMBOL_LOOKUP);
if (s == NULL)
M4ERROR ((warning_status, 0,
"Undefined macro `%s'", name));
else
call_macro (s, argc - 1, argv + 1, obs);
}
static void
m4_defn (struct obstack *obs, int argc, token_data **argv)
{
symbol *s;
if (bad_argc (argv[0], argc, 2, 2))
return;
s = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
if (s == NULL)
return;
switch (SYMBOL_TYPE (s))
{
case TOKEN_TEXT:
obstack_grow (obs, lquote.string, lquote.length);
obstack_grow (obs, SYMBOL_TEXT (s), strlen (SYMBOL_TEXT (s)));
obstack_grow (obs, rquote.string, rquote.length);
break;
case TOKEN_FUNC:
push_macro (SYMBOL_FUNC (s), SYMBOL_TRACED (s));
break;
case TOKEN_VOID:
break;
default:
M4ERROR ((warning_status, 0,
"INTERNAL ERROR: Bad symbol type in m4_defn ()"));
abort ();
}
}
static int sysval;
static void
m4_syscmd (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 2, 2))
return;
debug_flush_files ();
sysval = system (ARG (1));
}
static void
m4_esyscmd (struct obstack *obs, int argc, token_data **argv)
{
FILE *pin;
int ch;
if (bad_argc (argv[0], argc, 2, 2))
return;
debug_flush_files ();
pin = popen (ARG (1), "r");
if (pin == NULL)
{
M4ERROR ((warning_status, errno,
"Cannot open pipe to command \"%s\"", ARG (1)));
sysval = 0xff << 8;
}
else
{
while ((ch = getc (pin)) != EOF)
obstack_1grow (obs, (char) ch);
sysval = pclose (pin);
}
}
static void
m4_sysval (struct obstack *obs, int argc, token_data **argv)
{
shipout_int (obs, (sysval >> 8) & 0xff);
}
static void
m4_eval (struct obstack *obs, int argc, token_data **argv)
{
eval_t value;
int radix = 10;
int min = 1;
const char *s;
if (bad_argc (argv[0], argc, 2, 4))
return;
if (argc >= 3 && !numeric_arg (argv[0], ARG (2), &radix))
return;
if (radix <= 1 || radix > (int) strlen (digits))
{
M4ERROR ((warning_status, 0,
"Radix in eval out of range (radix = %d)", radix));
return;
}
if (argc >= 4 && !numeric_arg (argv[0], ARG (3), &min))
return;
if (min <= 0)
{
M4ERROR ((warning_status, 0,
"Negative width to eval"));
return;
}
if (evaluate (ARG (1), &value))
return;
s = ntoa (value, radix);
if (*s == '-')
{
obstack_1grow (obs, '-');
min--;
s++;
}
for (min -= strlen (s); --min >= 0;)
obstack_1grow (obs, '0');
obstack_grow (obs, s, strlen (s));
}
static void
m4_incr (struct obstack *obs, int argc, token_data **argv)
{
int value;
if (bad_argc (argv[0], argc, 2, 2))
return;
if (!numeric_arg (argv[0], ARG (1), &value))
return;
shipout_int (obs, value + 1);
}
static void
m4_decr (struct obstack *obs, int argc, token_data **argv)
{
int value;
if (bad_argc (argv[0], argc, 2, 2))
return;
if (!numeric_arg (argv[0], ARG (1), &value))
return;
shipout_int (obs, value - 1);
}
static void
m4_divert (struct obstack *obs, int argc, token_data **argv)
{
int i = 0;
if (bad_argc (argv[0], argc, 1, 2))
return;
if (argc == 2 && !numeric_arg (argv[0], ARG (1), &i))
return;
make_diversion (i);
}
static void
m4_divnum (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 1, 1))
return;
shipout_int (obs, current_diversion);
}
static void
m4_undivert (struct obstack *obs, int argc, token_data **argv)
{
int i, file;
FILE *fp;
if (argc == 1)
undivert_all ();
else
for (i = 1; i < argc; i++)
{
if (sscanf (ARG (i), "%d", &file) == 1)
insert_diversion (file);
else if (no_gnu_extensions)
M4ERROR ((warning_status, 0,
"Non-numeric argument to %s", TOKEN_DATA_TEXT (argv[0])));
else
{
fp = path_search (ARG (i));
if (fp != NULL)
{
insert_file (fp);
fclose (fp);
}
else
M4ERROR ((warning_status, errno,
"Cannot undivert %s", ARG (i)));
}
}
}
static void
m4_dnl (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 1, 1))
return;
skip_line ();
}
static void
m4_shift (struct obstack *obs, int argc, token_data **argv)
{
dump_args (obs, argc - 1, argv + 1, ",", TRUE);
}
static void
m4_changequote (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 1, 3))
return;
set_quotes ((argc >= 2) ? TOKEN_DATA_TEXT (argv[1]) : NULL,
(argc >= 3) ? TOKEN_DATA_TEXT (argv[2]) : NULL);
}
static void
m4_changecom (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 1, 3))
return;
if (argc == 1)
set_comment ("", "");
else
set_comment (TOKEN_DATA_TEXT (argv[1]),
(argc >= 3) ? TOKEN_DATA_TEXT (argv[2]) : NULL);
}
#ifdef ENABLE_CHANGEWORD
static void
m4_changeword (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 2, 2))
return;
set_word_regexp (TOKEN_DATA_TEXT (argv[1]));
}
#endif
static void
include (int argc, token_data **argv, boolean silent)
{
FILE *fp;
if (bad_argc (argv[0], argc, 2, 2))
return;
fp = path_search (ARG (1));
if (fp == NULL)
{
if (!silent)
M4ERROR ((warning_status, errno,
"Cannot open %s", ARG (1)));
return;
}
push_file (fp, ARG (1));
}
static void
m4_include (struct obstack *obs, int argc, token_data **argv)
{
include (argc, argv, FALSE);
}
static void
m4_sinclude (struct obstack *obs, int argc, token_data **argv)
{
include (argc, argv, TRUE);
}
static void
m4_maketemp (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 2, 2))
return;
mktemp (ARG (1));
obstack_grow (obs, ARG (1), strlen (ARG (1)));
}
static void
m4_errprint (struct obstack *obs, int argc, token_data **argv)
{
dump_args (obs, argc, argv, " ", FALSE);
obstack_1grow (obs, '\0');
fprintf (stderr, "%s", (char *) obstack_finish (obs));
fflush (stderr);
}
static void
m4___file__ (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 1, 1))
return;
obstack_grow (obs, lquote.string, lquote.length);
obstack_grow (obs, current_file, strlen (current_file));
obstack_grow (obs, rquote.string, rquote.length);
}
static void
m4___line__ (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 1, 1))
return;
shipout_int (obs, current_line);
}
static void
m4_m4exit (struct obstack *obs, int argc, token_data **argv)
{
int exit_code = 0;
if (bad_argc (argv[0], argc, 1, 2))
return;
if (argc == 2 && !numeric_arg (argv[0], ARG (1), &exit_code))
exit_code = 0;
exit (exit_code);
}
static void
m4_m4wrap (struct obstack *obs, int argc, token_data **argv)
{
if (no_gnu_extensions)
obstack_grow (obs, ARG (1), strlen (ARG (1)));
else
dump_args (obs, argc, argv, " ", FALSE);
obstack_1grow (obs, '\0');
push_wrapup (obstack_finish (obs));
}
static void
set_trace (symbol *sym, const char *data)
{
SYMBOL_TRACED (sym) = (boolean) (data != NULL);
}
static void
m4_traceon (struct obstack *obs, int argc, token_data **argv)
{
symbol *s;
int i;
if (argc == 1)
hack_all_symbols (set_trace, (char *) obs);
else
for (i = 1; i < argc; i++)
{
s = lookup_symbol (TOKEN_DATA_TEXT (argv[i]), SYMBOL_LOOKUP);
if (s != NULL)
set_trace (s, (char *) obs);
else
M4ERROR ((warning_status, 0,
"Undefined name %s", TOKEN_DATA_TEXT (argv[i])));
}
}
static void
m4_traceoff (struct obstack *obs, int argc, token_data **argv)
{
symbol *s;
int i;
if (argc == 1)
hack_all_symbols (set_trace, NULL);
else
for (i = 1; i < argc; i++)
{
s = lookup_symbol (TOKEN_DATA_TEXT (argv[i]), SYMBOL_LOOKUP);
if (s != NULL)
set_trace (s, NULL);
else
M4ERROR ((warning_status, 0,
"Undefined name %s", TOKEN_DATA_TEXT (argv[i])));
}
}
static void
m4_debugmode (struct obstack *obs, int argc, token_data **argv)
{
int new_debug_level;
int change_flag;
if (bad_argc (argv[0], argc, 1, 2))
return;
if (argc == 1)
debug_level = 0;
else
{
if (ARG (1)[0] == '+' || ARG (1)[0] == '-')
{
change_flag = ARG (1)[0];
new_debug_level = debug_decode (ARG (1) + 1);
}
else
{
change_flag = 0;
new_debug_level = debug_decode (ARG (1));
}
if (new_debug_level < 0)
M4ERROR ((warning_status, 0,
"Debugmode: bad debug flags: `%s'", ARG (1)));
else
{
switch (change_flag)
{
case 0:
debug_level = new_debug_level;
break;
case '+':
debug_level |= new_debug_level;
break;
case '-':
debug_level &= ~new_debug_level;
break;
}
}
}
}
static void
m4_debugfile (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 1, 2))
return;
if (argc == 1)
debug_set_output (NULL);
else if (!debug_set_output (ARG (1)))
M4ERROR ((warning_status, errno,
"Cannot set error file: %s", ARG (1)));
}
static void
m4_len (struct obstack *obs, int argc, token_data **argv)
{
if (bad_argc (argv[0], argc, 2, 2))
return;
shipout_int (obs, strlen (ARG (1)));
}
static void
m4_index (struct obstack *obs, int argc, token_data **argv)
{
const char *cp, *last;
int l1, l2, retval;
if (bad_argc (argv[0], argc, 3, 3))
return;
l1 = strlen (ARG (1));
l2 = strlen (ARG (2));
last = ARG (1) + l1 - l2;
for (cp = ARG (1); cp <= last; cp++)
{
if (strncmp (cp, ARG (2), l2) == 0)
break;
}
retval = (cp <= last) ? cp - ARG (1) : -1;
shipout_int (obs, retval);
}
static void
m4_substr (struct obstack *obs, int argc, token_data **argv)
{
int start, length, avail;
if (bad_argc (argv[0], argc, 3, 4))
return;
length = avail = strlen (ARG (1));
if (!numeric_arg (argv[0], ARG (2), &start))
return;
if (argc == 4 && !numeric_arg (argv[0], ARG (3), &length))
return;
if (start < 0 || length <= 0 || start >= avail)
return;
if (start + length > avail)
length = avail - start;
obstack_grow (obs, ARG (1) + start, length);
}
static const char *
expand_ranges (const char *s, struct obstack *obs)
{
char from;
char to;
for (from = '\0'; *s != '\0'; from = *s++)
{
if (*s == '-' && from != '\0')
{
to = *++s;
if (to == '\0')
obstack_1grow (obs, '-');
else if (from <= to)
{
while (from++ < to)
obstack_1grow (obs, from);
}
else
{
while (--from >= to)
obstack_1grow (obs, from);
}
}
else
obstack_1grow (obs, *s);
}
obstack_1grow (obs, '\0');
return obstack_finish (obs);
}
static void
m4_translit (struct obstack *obs, int argc, token_data **argv)
{
register const char *data, *tmp;
const char *from, *to;
int tolen;
if (bad_argc (argv[0], argc, 3, 4))
return;
from = ARG (2);
if (strchr (from, '-') != NULL)
{
from = expand_ranges (from, obs);
if (from == NULL)
return;
}
if (argc == 4)
{
to = ARG (3);
if (strchr (to, '-') != NULL)
{
to = expand_ranges (to, obs);
if (to == NULL)
return;
}
}
else
to = "";
tolen = strlen (to);
for (data = ARG (1); *data; data++)
{
tmp = strchr (from, *data);
if (tmp == NULL)
{
obstack_1grow (obs, *data);
}
else
{
if (tmp - from < tolen)
obstack_1grow (obs, *(to + (tmp - from)));
}
}
}
static void
m4_format (struct obstack *obs, int argc, token_data **argv)
{
format (obs, argc - 1, argv + 1);
}
static int substitute_warned = 0;
static void
substitute (struct obstack *obs, const char *victim, const char *repl,
struct re_registers *regs)
{
register unsigned int ch;
for (;;)
{
while ((ch = *repl++) != '\\')
{
if (ch == '\0')
return;
obstack_1grow (obs, ch);
}
switch ((ch = *repl++))
{
case '0':
if (!substitute_warned)
{
M4ERROR ((warning_status, 0, "\
WARNING: \\0 will disappear, use \\& instead in replacements"));
substitute_warned = 1;
}
case '&':
obstack_grow (obs, victim + regs->start[0],
regs->end[0] - regs->start[0]);
break;
case '1': case '2': case '3': case '4': case '5': case '6':
case '7': case '8': case '9':
ch -= '0';
if (regs->end[ch] > 0)
obstack_grow (obs, victim + regs->start[ch],
regs->end[ch] - regs->start[ch]);
break;
default:
obstack_1grow (obs, ch);
break;
}
}
}
static void
m4_regexp (struct obstack *obs, int argc, token_data **argv)
{
const char *victim;
const char *regexp;
const char *repl;
struct re_pattern_buffer buf;
struct re_registers regs;
const char *msg;
int startpos;
int length;
if (bad_argc (argv[0], argc, 3, 4))
return;
victim = TOKEN_DATA_TEXT (argv[1]);
regexp = TOKEN_DATA_TEXT (argv[2]);
buf.buffer = NULL;
buf.allocated = 0;
buf.fastmap = NULL;
buf.translate = NULL;
msg = re_compile_pattern (regexp, strlen (regexp), &buf);
if (msg != NULL)
{
M4ERROR ((warning_status, 0,
"Bad regular expression: `%s': %s", regexp, msg));
return;
}
length = strlen (victim);
startpos = re_search (&buf, victim, length, 0, length, ®s);
xfree (buf.buffer);
if (startpos == -2)
{
M4ERROR ((warning_status, 0,
"Error matching regular expression \"%s\"", regexp));
return;
}
if (argc == 3)
shipout_int (obs, startpos);
else if (startpos >= 0)
{
repl = TOKEN_DATA_TEXT (argv[3]);
substitute (obs, victim, repl, ®s);
}
return;
}
static void
m4_patsubst (struct obstack *obs, int argc, token_data **argv)
{
const char *victim;
const char *regexp;
struct re_pattern_buffer buf;
struct re_registers regs;
const char *msg;
int matchpos;
int offset;
int length;
if (bad_argc (argv[0], argc, 3, 4))
return;
regexp = TOKEN_DATA_TEXT (argv[2]);
buf.buffer = NULL;
buf.allocated = 0;
buf.fastmap = NULL;
buf.translate = NULL;
msg = re_compile_pattern (regexp, strlen (regexp), &buf);
if (msg != NULL)
{
M4ERROR ((warning_status, 0,
"Bad regular expression `%s': %s", regexp, msg));
if (buf.buffer != NULL)
xfree (buf.buffer);
return;
}
victim = TOKEN_DATA_TEXT (argv[1]);
length = strlen (victim);
offset = 0;
matchpos = 0;
while (offset < length)
{
matchpos = re_search (&buf, victim, length,
offset, length - offset, ®s);
if (matchpos < 0)
{
if (matchpos == -2)
M4ERROR ((warning_status, 0,
"Error matching regular expression \"%s\"", regexp));
else if (offset < length)
obstack_grow (obs, victim + offset, length - offset);
break;
}
if (matchpos > offset)
obstack_grow (obs, victim + offset, matchpos - offset);
substitute (obs, victim, ARG (3), ®s);
offset = regs.end[0];
if (regs.start[0] == regs.end[0])
obstack_1grow (obs, victim[offset++]);
}
obstack_1grow (obs, '\0');
xfree (buf.buffer);
return;
}
void
expand_user_macro (struct obstack *obs, symbol *sym,
int argc, token_data **argv)
{
register const char *text;
int i;
for (text = SYMBOL_TEXT (sym); *text != '\0';)
{
if (*text != '$')
{
obstack_1grow (obs, *text);
text++;
continue;
}
text++;
switch (*text)
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
if (no_gnu_extensions)
{
i = *text++ - '0';
}
else
{
for (i = 0; isdigit (*text); text++)
i = i*10 + (*text - '0');
}
if (i < argc)
obstack_grow (obs, TOKEN_DATA_TEXT (argv[i]),
strlen (TOKEN_DATA_TEXT (argv[i])));
break;
case '#':
shipout_int (obs, argc - 1);
text++;
break;
case '*':
case '@':
dump_args (obs, argc, argv, ",", *text == '@');
text++;
break;
default:
obstack_1grow (obs, '$');
break;
}
}
}