/* mp4h -- A macro processor for HTML documents
Copyright 2000-2002, Denis Barbier
All rights reserved.
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 2, or (at your option)
any later version.
This program is a work based on GNU m4 version 1.4n. Below is the
original copyright.
*/
/* GNU m4 -- A simple macro processor
Copyright (C) 1989, 90, 91, 92, 93, 94 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 2, 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.
*/
#include "mp4h.h"
#include
#include
/* Enable sync output for /lib/cpp (-s). */
int sync_output = 0;
/* Debug (-d[flags]). */
int debug_level = 0;
/* Hash table size (should be a prime) (-Hsize). */
int hash_table_size = HASHMAX;
/* Max length of arguments in trace output (-lsize). */
int max_debug_argument_length = 0;
/* Suppress warnings about missing arguments. */
int suppress_warnings = 0;
/* If not zero, then value of exit status for warning diagnostics. */
int warning_status = 0;
/* Artificial limit for expansion_level in macro.c. */
int nesting_limit = 250;
/* Security level */
int safety_level = 0;
/* Document encoding */
encoding_type document_encoding = ENCODING_8BIT;
/* Flags to control how expansion is performed. */
#define DEFAULT_EXPANSION_FLAGS 3114
int exp_flags = DEFAULT_EXPANSION_FLAGS;
/* Name of frozen file to digest after initialization. */
const char *frozen_file_to_read = NULL;
/* Name of frozen file to produce near completion. */
const char *frozen_file_to_write = NULL;
/* True when -F flag is passed. */
int frozen_dump = 0;
/* The name this program was run with (needed by ../lib/error.c). */
const char *program_name = PACKAGE_NAME;
/* If nonzero, display usage information and exit. */
static int show_help = 0;
/* If nonzero, print the version on standard output and exit. */
static int show_version = 0;
struct macro_definition
{
struct macro_definition *next;
int code; /* D, U or t */
const char *macro;
};
typedef struct macro_definition macro_definition;
/*---------------------------------------------.
| Print a usage message and exit with STATUS. |
`---------------------------------------------*/
#define HELP_EXP_FLAGS(n,str) \
do \
{ \
printf(" %c %4d %s\n", \
((exp_flags & n) ? '*' : ' '), n, str); \
} \
while (0)
static void
usage (int status)
{
if (status != EXIT_SUCCESS)
fprintf (stderr, _("Try `%s --help' for more information.\n"),
PACKAGE_NAME);
else
{
printf (_("Usage: %s [OPTION]... [FILE]...\n"), PACKAGE_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, --fatal-warnings stop execution after first warning\n\
-Q, --quiet, --silent suppress some warnings for builtins\n\
-S, --safety-level=NUMBER disable hazardous functions\n"),
stdout);
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\
Parser features:\n\
-c, --caseless=NUMBER set tags (1), variables (2) or entities (4)\n\
case insensitive. Default value is 3, i.e.\n\
only entities are case sensitive\n\
-e, --encoding=NAME specify document encoding\n\
-X, --expansion=NUMBER set parser behaviour according to the bits\n\
of NUMBER, with (star marks current flags)\n\
"), stdout);
HELP_EXP_FLAGS( 1, "do not parse unknown tags");
HELP_EXP_FLAGS( 2, "unknown tags are assumed being simple");
HELP_EXP_FLAGS( 4, "trailing star in tag name do not make this tag simple");
HELP_EXP_FLAGS( 8, "an unmatched end tag closes all previous unmatched begin tags");
HELP_EXP_FLAGS( 16, "interpret backslashes as printf");
HELP_EXP_FLAGS( 32, "remove trailing slash in tag attributes");
HELP_EXP_FLAGS( 64, "do not remove trailing star in tag name");
HELP_EXP_FLAGS( 128, "do not remove leading star in tag name");
HELP_EXP_FLAGS( 256, "do not add a space before trailing slash in tag attributes");
HELP_EXP_FLAGS( 1024, "suppress warnings about bad nested tags");
HELP_EXP_FLAGS( 2048, "suppress warnings about missing trailing slash");
fputs (_("\
\n\
Limits control:\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 `ae')\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 debugging-on'ed\n\
a show actual arguments\n\
e show expansion\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\
m show results of module operations\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);
}
/*--------------------------------------.
| Decode options and launch execution. |
`--------------------------------------*/
static const struct option long_options[] =
{
{"arglength", required_argument, NULL, 'l'},
{"debug", optional_argument, NULL, 'd'},
{"error-output", required_argument, NULL, 'o'},
{"expansion", required_argument, NULL, 'X'},
{"fatal-warnings", no_argument, NULL, 'E'},
{"freeze-state", required_argument, NULL, 'F'},
{"hashsize", required_argument, NULL, 'H'},
{"include", required_argument, NULL, 'I'},
{"nesting-limit", required_argument, NULL, 'L'},
{"quiet", no_argument, NULL, 'Q'},
{"reload-state", required_argument, NULL, 'R'},
{"silent", no_argument, NULL, 'Q'},
{"synclines", no_argument, NULL, 's'},
{"safety-level", required_argument, NULL, 'S'},
{"encoding", required_argument, NULL, 'e'},
{"caseless", required_argument, NULL, 'c'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
/* These are somewhat troublesome. */
{ "define", required_argument, NULL, 'D' },
{ "undefine", required_argument, NULL, 'U' },
{ "trace", required_argument, NULL, 't' },
{ 0, 0, 0, 0 },
};
#define OPTSTRING "c:D:e:EF:H:I:L:QR:U:X:d:hl:o:sS:Ot:V"
int
main (int argc, char *const *argv)
{
macro_definition *head; /* head of deferred argument list */
macro_definition *tail;
macro_definition *new;
int optchar; /* option character */
macro_definition *defines;
FILE *fp;
char *filename;
int caseless = CASELESS_DEFAULT;
debug_init ();
include_init ();
/* First, we decode the arguments, to size up tables and stuff. */
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 'c':
caseless = atoi (optarg);
if (caseless <= 0)
caseless = CASELESS_DEFAULT;
break;
case 'D':
case 'U':
case 't':
/* Arguments that cannot be handled until later are accumulated. */
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':
if (strcasecmp(optarg, "8bit") == 0)
document_encoding = ENCODING_8BIT;
else if (strcasecmp(optarg, "utf8") == 0)
document_encoding = ENCODING_UTF8;
else
error (0, 0, _("Bad encoding: `%s'"), optarg);
break;
case 'E':
warning_status = EXIT_FAILURE;
break;
case 'F':
frozen_file_to_write = optarg;
frozen_dump = 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 'Q':
suppress_warnings = 1;
break;
case 'R':
frozen_file_to_read = optarg;
break;
case 'd':
debug_level = debug_decode (optarg);
if (debug_level < 0)
{
error (0, 0, _("Bad debug flags: `%s'"), optarg);
debug_level = 0;
}
break;
case 'X':
exp_flags = atoi (optarg);
if (exp_flags < 0)
{
error (0, 0, _("Bad expansion flags: `%s'"), optarg);
exp_flags = DEFAULT_EXPANSION_FLAGS;
}
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;
case 'S':
safety_level = atoi (optarg);
break;
case 'V':
show_version = 1;
break;
case 'h':
show_help = 1;
break;
}
if (show_version)
{
printf ("%s (%s)\n", PACKAGE_STRING, PACKAGE_DATE);
exit (EXIT_SUCCESS);
}
if (show_help)
usage (EXIT_SUCCESS);
/* Do the basic initialisations. */
input_init ();
output_init ();
include_env_init ();
symtab_init ();
break_init ();
caseless_init (caseless);
#ifdef HAVE_LOCALE_H
locale_init (LC_ALL, NULL);
#endif
#ifdef WITH_MODULES
module_init ();
#endif
pcre_init ();
if (frozen_file_to_read)
reload_frozen_state (frozen_file_to_read);
else
builtin_init ();
/* Handle deferred command line macro definitions. Must come after
initialisation of the symbol table. */
defines = head;
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';
sym = lookup_variable (defines->macro, SYMBOL_INSERT);
initialize_builtin (sym);
SYMBOL_TYPE (sym) = TOKEN_TEXT;
SYMBOL_TEXT (sym) = xstrdup (macro_value);
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:
MP4HERROR ((warning_status, 0,
"INTERNAL ERROR: Bad code in deferred arguments"));
exit (1);
}
next = defines->next;
xfree ((voidstar) defines);
defines = next;
}
/* Handle the various input files. Each file is pushed on the input,
and the input read. Wrapup text is handled separately later. */
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], &filename);
if (fp == NULL)
{
error (0, errno, argv[optind]);
MP4HERROR ((warning_status, 0,
_("%s: file skipped"), argv[optind]));
continue;
}
else
{
xfree ((voidstar) current_file);
current_file = xstrdup (filename);
push_file (fp, filename);
xfree ((voidstar) filename);
}
}
expand_input ();
}
#undef NEXTARG
/* Now handle wrapup text. */
while (pop_wrapup ())
expand_input ();
if (frozen_file_to_write)
produce_frozen_state (frozen_file_to_write);
/* Free memory */
input_deallocate ();
debug_deallocate ();
include_deallocate ();
output_deallocate ();
symtab_deallocate ();
break_deallocate ();
builtin_deallocate ();
pcre_deallocate ();
xfree ((voidstar) current_file);
exit (EXIT_SUCCESS);
}