home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-387-Vol-3of3.iso
/
m
/
m4v05as.zip
/
BUILTIN.C
next >
Wrap
C/C++ Source or Header
|
1992-02-22
|
32KB
|
1,352 lines
/*
* GNU m4 -- A simple macro processor
* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
*
* This program 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 1, or (at your option)
* any later version.
*
* This program 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* MS-DOS port (c) 1990 by Thorsten Ohl, ohl@gnu.ai.mit.edu
* This port is also distributed under the terms of the
* GNU General Public License as published by the
* Free Software Foundation.
*
* Please note that this file is not identical to the
* original GNU release, you should have received this
* code as patch to the official release.
*
* $Header: e:/gnu/m4/RCS/builtin.c 0.5.1.0 90/09/28 18:34:53 tho Exp $
*/
/*
* Code for all builtin macros, initialisation of symbol table, and
* expansion of user defined macros.
*/
#include "m4.h"
#define ARG(i) (argc > (i) ? TOKEN_DATA_TEXT(argv[i]) : "")
/*
* Initialisation of builtin and predefined macros. The table
* "builtin_tab" is both used for initialisation, and by the "builtin"
* builtin.
*/
#ifdef MSDOS
#include <process.h>
#include <errno.h>
#include <io.h>
#define M4_ARGS \
struct obstack *obs, int argc, struct token_data **argv
builtin *find_builtin_by_name(char *name);
builtin *find_builtin_by_addr(builtin_func *func);
static char *ntoa (eval_t value, int radix);
static enum boolean bad_argc (char *name, int argc, int min, int max);
static int dumpdef_cmp (struct symbol **s1, struct symbol **s2);
static void define_builtin (char *name, struct builtin *bp,\
enum symbol_lookup mode);
static void define_macro (int argc, struct token_data **argv,\
enum symbol_lookup mode);
static void dump_args (struct obstack *obs, int argc,\
struct token_data **argv, char *sep,\
enum boolean quoted);
static void dump_symbol (struct symbol *sym, struct dump_symbol_data *data);
static void include (int argc, struct token_data **argv, enum boolean silent);
static void m4_builtin (M4_ARGS);
static void m4_changecom (M4_ARGS);
static void m4_changequote (M4_ARGS);
static void m4_define (M4_ARGS);
static void m4_defn (M4_ARGS);
static void m4_divert (M4_ARGS);
static void m4_divnum (M4_ARGS);
static void m4_dnl (M4_ARGS);
static void m4_dumpdef (M4_ARGS);
static void m4_errprint (M4_ARGS);
static void m4_eval (M4_ARGS);
static void m4_ifdef (M4_ARGS);
static void m4_ifelse (M4_ARGS);
static void m4_include (M4_ARGS);
static void m4_index (M4_ARGS);
static void m4_len (M4_ARGS);
static void m4_m4exit (M4_ARGS);
static void m4_m4wrap (M4_ARGS);
static void m4_maketemp (M4_ARGS);
static void m4_popdef (M4_ARGS);
static void m4_pushdef (M4_ARGS);
static void m4_shift (M4_ARGS);
static void m4_sinclude (M4_ARGS);
static void m4_substr (M4_ARGS);
static void m4_syscmd (M4_ARGS);
static void m4_sysval (M4_ARGS);
static void m4_traceoff (M4_ARGS);
static void m4_traceon (M4_ARGS);
static void m4_translit (M4_ARGS);
static void m4_undefine (M4_ARGS);
static void m4_undivert (M4_ARGS);
static void set_trace (struct symbol *sym, char *data);
static void shipout_int (struct obstack *obs, int val);
#else /* not MSDOS */
static void m4_builtin();
static void m4_changecom();
static void m4_changequote();
static void m4_define();
static void m4_defn();
static void m4_divert();
static void m4_divnum();
static void m4_dnl();
static void m4_dumpdef();
static void m4_errprint();
static void m4_eval();
static void m4_ifdef();
static void m4_ifelse();
static void m4_include();
static void m4_index();
static void m4_len();
static void m4_m4exit();
static void m4_m4wrap();
static void m4_maketemp();
static void m4_popdef();
static void m4_pushdef();
static void m4_shift();
static void m4_sinclude();
static void m4_substr();
static void m4_syscmd();
static void m4_sysval();
static void m4_traceoff();
static void m4_traceon();
static void m4_translit();
static void m4_undefine();
static void m4_undivert();
#endif /* not MSDOS */
static builtin
builtin_tab[] = {
/* name gnu macro-args function */
{ "builtin", true, false, m4_builtin },
{ "changecom", false, false, m4_changecom },
{ "changequote", false, false, m4_changequote },
{ "define", false, true, m4_define },
{ "defn", false, false, m4_defn },
{ "divert", false, false, m4_divert },
{ "divnum", false, false, m4_divnum },
{ "dnl", false, false, m4_dnl },
{ "dumpdef", false, false, m4_dumpdef },
{ "errprint", false, false, m4_errprint },
{ "eval", false, false, m4_eval },
{ "ifdef", false, false, m4_ifdef },
{ "ifelse", false, false, m4_ifelse },
{ "include", false, false, m4_include },
{ "index", false, false, m4_index },
{ "len", false, false, m4_len },
{ "m4exit", false, false, m4_m4exit },
{ "m4wrap", false, false, m4_m4wrap },
{ "maketemp", false, false, m4_maketemp },
{ "popdef", false, false, m4_popdef },
{ "pushdef", false, true, m4_pushdef },
{ "shift", false, false, m4_shift },
{ "sinclude", false, false, m4_sinclude },
{ "substr" , false, false, m4_substr },
{ "syscmd", false, false, m4_syscmd },
{ "sysval", false, false, m4_sysval },
{ "traceoff", false, false, m4_traceoff },
{ "traceon", false, false, m4_traceon },
{ "translit", false, false, m4_translit },
{ "undefine", false, false, m4_undefine },
{ "undivert", false, false, m4_undivert },
{ 0, false, false, 0 },
};
static predefined
predefined_tab[] = {
{ "unix", false, "" },
{ "gnu", true, "" },
{ "incr", false, "eval(`($1)+1')" },
{ "decr", false, "eval(`($1)-1')" },
{ 0, false, 0 },
};
/* The number of the currently active diversion */
static int current_diversion;
/*
* Find the builtin, which lives on ADDR
*/
builtin *
find_builtin_by_addr(func)
builtin_func *func;
{
builtin *bp;
for (bp = &builtin_tab[0]; bp->name != nil; bp++)
if (bp->func == func)
return bp;
return nil;
}
/*
* Find the builtin, which has NAME
*/
builtin *
find_builtin_by_name(name)
char *name;
{
builtin *bp;
for (bp = &builtin_tab[0]; bp->name != nil; bp++)
if (strcmp(bp->name, name) == 0)
return bp;
return nil;
}
/*
* Install a builtin macro with name NAME, bound to the C function given
* in BP.
*/
static void
define_builtin(name, bp, mode)
char *name;
builtin *bp;
symbol_lookup mode;
{
symbol *sym;
sym = lookup_symbol(name, mode);
SYMBOL_TYPE(sym) = TOKEN_FUNC;
SYMBOL_MACRO_ARGS(sym) = bp->groks_macro_args;
SYMBOL_FUNC(sym) = bp->func;
}
/*
* Define a predefined or user-defined macro, with name NAME, and
* expansion TEXT. MODE destinguishes between the "define" and the
* "pushdef" case. It is also used from main().
*/
void
define_user_macro(name, text, mode)
char *name;
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);
}
/*
* Initialise all builtin and predefined macros.
*/
void
builtin_init()
{
builtin *bp;
predefined *pp;
for (bp = &builtin_tab[0]; bp->name != nil; bp++) {
if (!(no_gnu_extensions && bp->gnu_extension))
define_builtin(bp->name, bp, SYMBOL_INSERT);
}
for (pp = &predefined_tab[0]; pp->name != nil; pp++) {
if (!(no_gnu_extensions && pp->gnu_extension))
define_user_macro(pp->name, pp->func, SYMBOL_INSERT);
}
current_diversion = 0;
}
/*
* Give friendly warnings, if a builtin macro is passed an inappropriate
* number of arguments. NAME is macro name for messages, ARGC is actual
* number of arguments, MIN is minimum number of acceptable arguments,
* negative if inapplicable, MAX is maximum number, negative if
* inapplicable.
*/
static boolean
bad_argc(name, argc, min, max)
char *name;
int argc;
int min;
int max;
{
if (min > 0 && argc < min) {
warning("too few arguments to %s", name);
return true;
} else if (max > 0 && argc > max)
warning("excess arguments to %s ignored", name);
return false;
}
/*
* Format an int VAL, and stuff it into an obstack OBS.
*/
static void
shipout_int(obs, val)
struct obstack *obs;
int val;
{
char buf[512];
sprintf(buf, "%d", val);
obstack_grow(obs, buf, strlen(buf));
}
/*
* Print all arguments to a macro to obstack OBS, separated by SEP, and
* quoted by the current quotes, if QUOTED is true.
*/
static void
dump_args(obs, argc, argv, sep, quoted)
struct obstack *obs;
int argc;
token_data **argv;
char *sep;
boolean quoted;
{
int i;
int len = strlen(sep);
for (i = 1; i < argc; i++) {
if (i > 1)
obstack_grow(obs, sep, len);
if (quoted)
obstack_1grow(obs, lquote);
obstack_grow(obs, TOKEN_DATA_TEXT(argv[i]), strlen(TOKEN_DATA_TEXT(argv[i])));
if (quoted)
obstack_1grow(obs, rquote);
}
}
/*
* The rest of this file is code for builtins and expansion of user
* defined macros. All the functions for builtins have a prototype as:
*
* void m4_MACRONAME(struct obstack *obs, int argc, char *argv[]);
*
* The function are expected to leave their expansion on the obstack
* OBS, as an unfinished object. ARGV is a table of ARGC pointers to
* the individual arguments to the macro. Please note that in general
* argv[argc] != nil.
*/
/*
* The first section are macros for definining, undefining, examining,
* changing, ... other macros.
*/
static void
define_macro(argc, argv, mode)
int argc;
token_data **argv;
symbol_lookup mode;
{
builtin *bp;
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 3, 3))
return;
if (TOKEN_DATA_TYPE(argv[1]) != TOKEN_TEXT)
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 == nil)
return;
else
define_builtin(ARG(1), bp, mode);
break;
default:
internal_error("Bad token data type in define_macro()");
break;
}
return;
}
static void
m4_define(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
define_macro(argc, argv, SYMBOL_INSERT);
}
static void
m4_undefine(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 2, 2))
return;
lookup_symbol(ARG(1), SYMBOL_DELETE);
}
static void
m4_pushdef(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
define_macro(argc, argv, SYMBOL_PUSHDEF);
}
static void
m4_popdef(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 2, 2))
return;
lookup_symbol(ARG(1), SYMBOL_POPDEF);
}
static void
m4_ifdef(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
symbol *s;
char *result;
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 3, 4))
return;
s = lookup_symbol(ARG(1), SYMBOL_LOOKUP);
if (s != nil)
result = ARG(2);
else if (argc == 4)
result = ARG(3);
else
result = nil;
if (result != nil)
obstack_grow(obs, result, strlen(result));
}
static void
m4_ifelse(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
char *result;
char *name = TOKEN_DATA_TEXT(argv[0]);
--argc, ++argv;
result = nil;
while (result == nil) {
bad_argc(name, argc, 3, -1);
if (strcmp(ARG(0), ARG(1)) == 0) {
result = ARG(2);
} else {
switch (argc) {
case 2:
result = "";
break;
case 3:
case 4:
result = ARG(3);
break;
default:
argc -= 3;
argv += 3;
break;
}
}
}
obstack_grow(obs, result, strlen(result));
}
/*
* The function dump_symbol() is for use by "dumpdef". It builds up a
* table of all defined, un-shadowed, symbols. The structure
* dump_symbol_data is used to pass the information needed from call to
* call to dump_symbol.
*/
struct dump_symbol_data {
struct obstack *obs; /* obstack for table */
symbol **base; /* base of table */
int size; /* size of table */
};
static void
dump_symbol(sym, data)
symbol *sym;
struct dump_symbol_data *data;
{
if (!sym->shadowed) {
obstack_blank(data->obs, sizeof(symbol*));
data->base = (symbol **)obstack_base(data->obs);
#if defined(_MSC_VER) && (_MSC_VER == 600)
/* Work around an optimizer bug. */
{ int tmp = data->size++; data->base[tmp] = sym; }
#else
data->base[data->size++] = sym;
#endif
}
}
/*
* qsort comparison routine, for sorting the table made in m4_dumpdef().
*/
static int
dumpdef_cmp(s1, s2)
symbol **s1, **s2;
{
return strcmp(SYMBOL_NAME(*s1), SYMBOL_NAME(*s2));
}
/*
* Implementation of "dumpdef" itself. It builds up a table of pointers
* to symbols, sorts it and prints the sorted table.
*/
static void
m4_dumpdef(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
symbol *s;
int i;
struct dump_symbol_data data;
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 != nil)
#ifdef MSDOS
dump_symbol(s, &data);
#else /* not MSDOS */
dump_symbol(s, (char *)&data);
#endif /* not MSDOS */
else
error("Undefined name %s", TOKEN_DATA_TEXT(argv[i]));
}
}
qsort((char*)data.base, data.size, sizeof(symbol*), dumpdef_cmp);
for ( ; data.size > 0; --data.size, data.base++) {
fprintf(stderr, "%s:\t", SYMBOL_NAME(data.base[0]));
switch(SYMBOL_TYPE(data.base[0])) {
case TOKEN_TEXT:
fprintf(stderr, "`%s'\n", SYMBOL_TEXT(data.base[0]));
break;
case TOKEN_FUNC:
bp = find_builtin_by_addr(SYMBOL_FUNC(data.base[0]));
if (bp == nil)
internal_error("builtin not found in builtin table!");
fprintf(stderr, "<%s>\n", bp->name);
break;
default:
internal_error("Bad token data type in m4_dumpdef()");
break;
}
}
#ifdef MSDOS /* but it's a genuine bug. */
/* Or is the intention to finalize the object?
(cf. m4_errprint (), which could also free the object) */
obstack_free (obs, obstack_base (obs));
#endif
}
/*
* This is non-standard. "Builtin" allows calls to builtin macros, even
* if thier definition has been overridden or shadowed. It is thus
* possible to redefine builtins, and still access their original
* definition.
*
* This macro is not available in compatibility mode.
*/
static void
m4_builtin(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
struct builtin *bp;
char *name = ARG(1);
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 2, -1))
return;
bp = find_builtin_by_name(name);
if (bp == nil)
error("Undefined name %s", name);
else
(*bp->func)(obs, argc-1, argv+1);
}
/*
* The macro "defn" returns the quoted definition of its first argument.
*/
static void
m4_defn(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
symbol *s;
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 2, 2))
return;
s = lookup_symbol(ARG(1), SYMBOL_LOOKUP);
if (s == nil)
return;
switch (SYMBOL_TYPE(s)) {
case TOKEN_TEXT:
obstack_1grow(obs, lquote);
obstack_grow(obs, SYMBOL_TEXT(s), strlen(SYMBOL_TEXT(s)));
obstack_1grow(obs, rquote);
break;
case TOKEN_FUNC:
push_macro(SYMBOL_FUNC(s));
break;
default:
internal_error("Bad symbol type in m4_defn()");
break;
}
}
/*
* This section contains macros to handle the builtins "syscmd" and
* "sysval".
*/
/* Exit code from last "syscmd" command. */
static int sysval;
static void
m4_syscmd(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
#ifdef MSDOS
/* The system function from the MSC runtime lib doesn't return
the exit code, so we have to hack one ourselves. */
char *sysargv[4];
char *p = NULL;
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 2, 2))
return;
/* Find $SHELL or $COMSPEC, assuming the former is a UNIX style
shell (preferred) and the latter is a DOS style command
processor. */
sysargv[0] = getenv ("SHELL");
if (sysargv[0])
sysargv[1] = "-c";
else
{
sysargv[0] = getenv ("COMSPEC");
if (sysargv[0])
sysargv[1] = "/c";
else
{
errno = ENOENT;
sysval = (-1) << 8;
return;
}
}
/* Let the shell execute COMMAND. */
sysargv[2] = ARG(1);
sysargv[3] = NULL;
/* Precompensate the ">> 8" from m4_sysval () */
sysval = spawnvpe (P_WAIT, sysargv[0], sysargv, NULL) << 8;
#else /* not MSDOS */
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 2, 2))
return;
sysval = system(ARG(1));
#endif /* not MSDOS */
}
static void
m4_sysval(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
shipout_int(obs, sysval>>8);
}
/*
* This section contains the top level code for the "eval" builtin. The
* actual work is done in the function evaluate(), which lives in eval.c.
*/
/* digits for number to ascii conversions. */
static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
static char *
ntoa(value, radix)
register eval_t value;
int radix;
{
#ifdef MSDOS
unsigned long uvalue;
#else
unsigned int uvalue;
#endif
static char str[256];
register char *s = &str[sizeof str];
boolean negative = false;
*--s = '\0';
if (radix == 10 && value < 0) {
int tmp;
negative = true;
value = -(value+1);
#ifdef MSDOS /* shut up the compiler. */
tmp = (int) (value%radix);
#else
tmp = value%radix;
#endif
if (tmp == radix - 1) {
*--s = '0';
value = value/radix + 1;
} else {
*--s = digits[tmp+1];
value /= radix;
}
if (value == 0) {
*--s = '-';
return s;
}
}
#ifdef MSDOS
uvalue = (unsigned long)value;
#else
uvalue = (unsigned int)value;
#endif
do {
*--s = digits[uvalue%radix];
uvalue /= radix;
} while (uvalue > 0);
if (negative)
*--s = '-';
return s;
}
static void
m4_eval(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
eval_t value;
int radix = 10;
int min = 1;
char *s;
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 2, 4))
return;
if (argc >= 3 && (sscanf(ARG(2), "%d", &radix) != 1)) {
error("non-numeric second argument to eval");
return;
}
if (radix <= 1 || radix > strlen(digits)) {
error("radix in eval out of range (radix = %d)", radix);
return;
}
if (argc >= 4 && (sscanf(ARG(3), "%d", &min) != 1 || min <= 0)) {
error("non-numeric third argument 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));
}
/*
* This section contains the macros for handling diversion.
*/
/*
* Divert further output to the diversion given by ARGV[ARGC]. Out of
* range means discard further output.
*/
static void
m4_divert(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
int i = 0;
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 1, 2))
return;
if (argc == 2 && sscanf(ARG(1), "%d", &i) != 1) {
error("non-numeric argument to divert");
return;
}
make_divertion(i);
current_diversion = i;
}
/*
* Expand to the current diversion number, -1 if none.
*/
static void
m4_divnum(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 1, 1))
return;
shipout_int(obs, current_diversion);
}
/*
* Bring back the diversion given by the argument list. If none is
* specified, bring back all diversions.
*/
static void
m4_undivert(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
int i, div;
if (argc == 1) {
undivert_all();
} else {
for (i = 1; i < argc; i++) {
if (sscanf(ARG(1), "%d", &div) != 1) {
error("non-numeric argument to divert");
continue;
}
insert_divertion(div);
}
}
}
/*
* This section contains various macros, which does not fall into any
* specific group.
*/
/*
* Delete all subsequent whitespace from input. skip_whitespace() lives
* in input.c.
*/
static void
m4_dnl(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
skip_line();
}
/*
* Shift all argument one to the left, discarding the first argument.
* Each output argument is quoted iwth the current quotes.
*/
static void
m4_shift(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
dump_args(obs, argc-1, argv+1, ", ", true);
}
/*
* Change the current quotes. Currently, only single character quotes
* are supported.
*/
static void
m4_changequote(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 1, 3))
return;
lquote = (argc >= 2) ? TOKEN_DATA_TEXT(argv[1])[0] : DEF_LQUOTE;
rquote = (argc >= 3) ? TOKEN_DATA_TEXT(argv[2])[0] : DEF_RQUOTE;
}
/*
* Change the current comment delimiters. Currently, only single
* comment delimiters are supported.
*/
static void
m4_changecom(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 1, 3))
return;
bcomm = (argc >= 2) ? TOKEN_DATA_TEXT(argv[1])[0] : DEF_BCOMM;
ecomm = (argc >= 3) ? TOKEN_DATA_TEXT(argv[2])[0] : DEF_ECOMM;
}
/*
* This section contains macros for inclusion of other files. This
* differs from diversion, in that the input is scanned before being
* copied to the output.
*/
/*
* Generic include function. Include the file given by the first
* argument, if it exists. Complain about inaccesible files, only if
* SILENT is false.
*/
static void
include(argc, argv, silent)
int argc;
token_data **argv;
boolean silent;
{
FILE *fp;
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 2, 2))
return;
fp = fopen(ARG(1), "r");
if (fp == nil) {
if (!silent)
error("can't open %s: %s", ARG(1), syserr());
return;
}
push_file(fp, ARG(1));
}
/*
* Include a file, complaining in case of errors.
*/
static void
m4_include(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
include(argc, argv, false);
}
/*
* Include a file, ignoring errors.
*/
static void
m4_sinclude(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
include(argc, argv, true);
}
/*
* Use the first argument as at template for a temporary file name.
*/
static void
m4_maketemp(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 2, 2))
return;
mktemp(ARG(1));
obstack_grow(obs, ARG(1), strlen(ARG(1)));
}
/*
* Print all arguments on standard error.
*/
static void
m4_errprint(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
dump_args(obs, argc, argv, " ", false);
#ifdef MSDOS /* but it's a genuine bug. */
obstack_1grow(obs, '\0');
#endif
fprintf(stderr, "%s", obstack_finish(obs));
fflush(stderr);
}
/*
* This section contains various macros for exiting, saving input until
* EOF is seen, and tracing macro calls.
*/
/*
* Exit immediately, with exitcode specified by the first argument, 0 if
* no arguments are present.
*/
static void
m4_m4exit(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
int exit_code = 0;
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 1, 2))
return;
if (argc == 2 && sscanf(ARG(1), "%d", &exit_code) != 1) {
error("non-numeric argument to m4exit");
exit_code = 0;
}
exit(exit_code);
}
/*
* Save the argument text until EOF has been seen, allowing for user
* specified cleanup action. GNU version saves all arguments, the
* standard version only the first.
*/
static void
m4_m4wrap(obs, argc, argv)
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);
#ifdef MSDOS /* but it's a genuine bug. */
obstack_1grow(obs, '\0');
#endif
push_wrapup(obstack_finish(obs));
}
/*
* Enable tracing of all specified macros, or all, if none is specified.
* Tracing is disabled by default, when a macro is defined.
*/
/*
* Set_trace() is used by "traceon" and "traceoff" to enable and disable
* tracing of a macro. It disables tracing if DATA is nil, otherwise it
* enable tracing.
*/
static void
set_trace(sym, data)
symbol *sym;
char *data;
{
SYMBOL_TRACED(sym) = (boolean)(data != nil);
}
static void
m4_traceon(obs, argc, argv)
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 != nil)
set_trace(s, (char *)obs);
else
error("Undefined name %s", TOKEN_DATA_TEXT(argv[i]));
}
}
/*
* Disable tracing of all specified macros, or all, if none is
* specified.
*/
static void
m4_traceoff(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
symbol *s;
int i;
if (argc == 1)
hack_all_symbols(set_trace, nil);
else
for (i = 1; i < argc; i++) {
s = lookup_symbol(TOKEN_DATA_TEXT(argv[i]), SYMBOL_LOOKUP);
if (s != nil)
set_trace(s, nil);
else
error("Undefined name %s", TOKEN_DATA_TEXT(argv[i]));
}
}
/*
* This section contains some text processing macros.
*/
/*
* Expand to the length of the first argument.
*/
static void
m4_len(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 2, 2))
return;
shipout_int(obs, strlen(ARG(1)));
}
/*
* The macro expands to the first index of the second argument in the
* first argument.
*/
static void
m4_index(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
char *cp, *last;
int l1, l2, retval;
if (bad_argc(TOKEN_DATA_TEXT(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);
}
/*
* The macro "substr" extracts substrings from the first argument,
* starting from the index given by the second argument, extending for a
* length given by the third argument. If the third argument is
* missing, the substring extends to the end of the first argument.
*/
static void
m4_substr(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
int start, length, avail;
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 3, 4))
return;
length = avail = strlen(ARG(1));
if (sscanf(ARG(2), "%d", &start) != 1) {
error("non-numeric second argument to substr");
return;
}
if (argc == 4 && sscanf(ARG(3), "%d", &length) != 1) {
error("non-numeric third argument to substr");
return;
}
if (start < 0 || length <= 0 || start >= avail)
return;
if (start + length > avail)
length = avail - start;
obstack_grow(obs, ARG(1) + start, length);
}
/*
* The macro "translit" transliterates all characters in the first
* argument, which are present in the second argument, into the
* corresponding character from the third argument. If the third
* argument is shorter than the second, the extra characters in the
* second argument, are delete from the first. (pueh)
*/
static void
m4_translit(obs, argc, argv)
struct obstack *obs;
int argc;
token_data **argv;
{
register char *data, *tmp;
char *from, *to;
int tolen;
if (bad_argc(TOKEN_DATA_TEXT(argv[0]), argc, 3, 4))
return;
from = ARG(2);
to = argc == 4 ? ARG(3) : "";
tolen = strlen(to);
for (data = ARG(1); *data; data++) {
tmp = (char*)index(from, *data);
if (tmp == nil){
obstack_1grow(obs, *data);
continue;
}
if (tmp - from >= tolen)
continue;
obstack_1grow(obs, *(to + (tmp - from)));
}
}
/*
* This function handles all expansion of user defined and predefined
* macros. It is called with an obstack OBS, where the macros expansion
* will be placed, as an unfinished object. SYM points to the macro
* definition, giving the expansin text. ARGC and ARGV are the
* arguments, as usual.
*/
void
expand_user_macro(obs, sym, argc, argv)
struct obstack *obs;
symbol *sym;
int argc;
token_data **argv;
{
register 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 '#': /* number of arguments */
shipout_int(obs, argc-1);
text++;
break;
case '*': /* all arguments */
case '@': /* ... same, but quoted */
dump_args(obs, argc, argv, ", ", *text == '@');
text++;
break;
default:
obstack_1grow(obs, '$');
break;
}
}
}