#include "m4.h"
#include <getopt.h>
#include <sys/signal.h>
static void usage _((int));
static int interactive = 0;
int sync_output = 0;
int debug_level = 0;
int hash_table_size = HASHMAX;
int no_gnu_extensions = 0;
int prefix_all_builtins = 0;
int max_debug_argument_length = 0;
int suppress_warnings = 0;
int warning_status = 0;
int nesting_limit = 250;
#ifdef ENABLE_CHANGEWORD
const char *user_word_regexp = NULL;
#endif
const char *frozen_file_to_read = NULL;
const char *frozen_file_to_write = NULL;
const char *program_name;
static int show_help = 0;
static int show_version = 0;
struct macro_definition
{
struct macro_definition *next;
int code;
const char *macro;
};
typedef struct macro_definition macro_definition;
void
reference_error (void)
{
fflush (stdout);
fprintf (stderr, "%s:%d: ", current_file, current_line);
}
#ifdef USE_STACKOVF
static void
stackovf_handler (void)
{
M4ERROR ((EXIT_FAILURE, 0,
"ERROR: Stack overflow. (Infinite define recursion?)"));
}
#endif
void
xfree (voidstar p)
{
if (p != NULL)
free (p);
}
static void
usage (int status)
{
if (status != EXIT_SUCCESS)
fprintf (stderr, "Try `%s --help' for more information.\n", program_name);
else
{
printf ("Usage: %s [OPTION]... [FILE]...\n", program_name);
fputs ("\
Mandatory or optional arguments to long options are mandatory or optional\n\
for short options too.\n\
\n\
Operation modes:\n\
--help display this help and exit\n\
--version output version information and exit\n\
-e, --interactive unbuffer output, ignore interrupts\n\
-E, --fatal-warnings stop execution after first warning\n\
-Q, --quiet, --silent suppress some warnings for builtins\n\
-P, --prefix-builtins force a `m4_' prefix to all builtins\n",
stdout);
#ifdef ENABLE_CHANGEWORD
fputs ("\
-W, --word-regexp=REGEXP use REGEXP for macro name syntax\n",
stdout);
#endif
fputs ("\
\n\
Preprocessor features:\n\
-I, --include=DIRECTORY search this directory second for includes\n\
-D, --define=NAME[=VALUE] enter NAME has having VALUE, or empty\n\
-U, --undefine=NAME delete builtin NAME\n\
-s, --synclines generate `#line NO \"FILE\"' lines\n",
stdout);
fputs ("\
\n\
Limits control:\n\
-G, --traditional suppress all GNU extensions\n\
-H, --hashsize=PRIME set symbol lookup hash table size\n\
-L, --nesting-limit=NUMBER change artificial nesting limit\n",
stdout);
fputs ("\
\n\
Frozen state files:\n\
-F, --freeze-state=FILE produce a frozen state on FILE at end\n\
-R, --reload-state=FILE reload a frozen state from FILE at start\n",
stdout);
fputs ("\
\n\
Debugging:\n\
-d, --debug=[FLAGS] set debug level (no FLAGS implies `aeq')\n\
-t, --trace=NAME trace NAME when it will be defined\n\
-l, --arglength=NUM restrict macro tracing size\n\
-o, --error-output=FILE redirect debug and trace output\n",
stdout);
fputs ("\
\n\
FLAGS is any of:\n\
t trace for all macro calls, not only traceon'ed\n\
a show actual arguments\n\
e show expansion\n\
q quote values as necessary, with a or e flag\n\
c show before collect, after collect and after call\n\
x add a unique macro call id, useful with c flag\n\
f say current input file name\n\
l say current input line number\n\
p show results of path searches\n\
i show changes in input files\n\
V shorthand for all of the above flags\n",
stdout);
fputs ("\
\n\
If no FILE or if FILE is `-', standard input is read.\n",
stdout);
}
exit (status);
}
static const struct option long_options[] =
{
{"arglength", required_argument, NULL, 'l'},
{"debug", optional_argument, NULL, 'd'},
{"diversions", required_argument, NULL, 'N'},
{"error-output", required_argument, NULL, 'o'},
{"fatal-warnings", no_argument, NULL, 'E'},
{"freeze-state", required_argument, NULL, 'F'},
{"hashsize", required_argument, NULL, 'H'},
{"include", required_argument, NULL, 'I'},
{"interactive", no_argument, NULL, 'e'},
{"nesting-limit", required_argument, NULL, 'L'},
{"prefix-builtins", no_argument, NULL, 'P'},
{"quiet", no_argument, NULL, 'Q'},
{"reload-state", required_argument, NULL, 'R'},
{"silent", no_argument, NULL, 'Q'},
{"synclines", no_argument, NULL, 's'},
{"traditional", no_argument, NULL, 'G'},
{"word-regexp", required_argument, NULL, 'W'},
{"help", no_argument, &show_help, 1},
{"version", no_argument, &show_version, 1},
{ "define", required_argument, NULL, 'D' },
{ "undefine", required_argument, NULL, 'U' },
{ "trace", required_argument, NULL, 't' },
{ 0, 0, 0, 0 },
};
#ifdef ENABLE_CHANGEWORD
#define OPTSTRING "B:D:EF:GH:I:L:N:PQR:S:T:U:W:d::el:o:st:"
#else
#define OPTSTRING "B:D:EF:GH:I:L:N:PQR:S:T:U:d::el:o:st:"
#endif
int
main (int argc, char *const *argv, char *const *envp)
{
macro_definition *head;
macro_definition *tail;
macro_definition *new;
int optchar;
macro_definition *defines;
FILE *fp;
program_name = argv[0];
include_init ();
debug_init ();
#ifdef USE_STACKOVF
setup_stackovf_trap (argv, envp, stackovf_handler);
#endif
head = tail = NULL;
while (optchar = getopt_long (argc, argv, OPTSTRING, long_options, NULL),
optchar != EOF)
switch (optchar)
{
default:
usage (EXIT_FAILURE);
case 0:
break;
case 'B':
case 'N':
case 'S':
case 'T':
break;
case 'D':
case 'U':
case 't':
new = (macro_definition *) xmalloc (sizeof (macro_definition));
new->code = optchar;
new->macro = optarg;
new->next = NULL;
if (head == NULL)
head = new;
else
tail->next = new;
tail = new;
break;
case 'E':
warning_status = EXIT_FAILURE;
break;
case 'F':
frozen_file_to_write = optarg;
break;
case 'G':
no_gnu_extensions = 1;
break;
case 'H':
hash_table_size = atoi (optarg);
if (hash_table_size <= 0)
hash_table_size = HASHMAX;
break;
case 'I':
add_include_directory (optarg);
break;
case 'L':
nesting_limit = atoi (optarg);
break;
case 'P':
prefix_all_builtins = 1;
break;
case 'Q':
suppress_warnings = 1;
break;
case 'R':
frozen_file_to_read = optarg;
break;
#ifdef ENABLE_CHANGEWORD
case 'W':
user_word_regexp = optarg;
break;
#endif
case 'd':
debug_level = debug_decode (optarg);
if (debug_level < 0)
{
error (0, 0, "bad debug flags: `%s'", optarg);
debug_level = 0;
}
break;
case 'e':
interactive = 1;
break;
case 'l':
max_debug_argument_length = atoi (optarg);
if (max_debug_argument_length <= 0)
max_debug_argument_length = 0;
break;
case 'o':
if (!debug_set_output (optarg))
error (0, errno, optarg);
break;
case 's':
sync_output = 1;
break;
}
if (show_version)
{
printf ("GNU %s %s\n", PRODUCT, VERSION);
exit (EXIT_SUCCESS);
}
if (show_help)
usage (EXIT_SUCCESS);
defines = head;
input_init ();
output_init ();
symtab_init ();
include_env_init ();
if (frozen_file_to_read)
reload_frozen_state (frozen_file_to_read);
else
builtin_init ();
while (defines != NULL)
{
macro_definition *next;
char *macro_value;
symbol *sym;
switch (defines->code)
{
case 'D':
macro_value = strchr (defines->macro, '=');
if (macro_value == NULL)
macro_value = "";
else
*macro_value++ = '\0';
define_user_macro (defines->macro, macro_value, SYMBOL_INSERT);
break;
case 'U':
lookup_symbol (defines->macro, SYMBOL_DELETE);
break;
case 't':
sym = lookup_symbol (defines->macro, SYMBOL_INSERT);
SYMBOL_TRACED (sym) = TRUE;
break;
default:
M4ERROR ((warning_status, 0,
"INTERNAL ERROR: Bad code in deferred arguments"));
abort ();
}
next = defines->next;
xfree ((voidstar) defines);
defines = next;
}
if (interactive)
{
signal (SIGINT, SIG_IGN);
setbuf (stdout, (char *) NULL);
}
if (optind == argc)
{
push_file (stdin, "stdin");
expand_input ();
}
else
for (; optind < argc; optind++)
{
if (strcmp (argv[optind], "-") == 0)
push_file (stdin, "stdin");
else
{
fp = path_search (argv[optind]);
if (fp == NULL)
{
error (0, errno, argv[optind]);
continue;
}
else
push_file (fp, argv[optind]);
}
expand_input ();
}
#undef NEXTARG
while (pop_wrapup ())
expand_input ();
if (frozen_file_to_write)
produce_frozen_state (frozen_file_to_write);
else
{
make_diversion (0);
undivert_all ();
}
exit (EXIT_SUCCESS);
}