2000-01-17 10:39:58 +00:00
|
|
|
|
/* macro.c -- user-defined macros for Texinfo.
|
2003-05-02 00:48:41 +00:00
|
|
|
|
$Id: macro.c,v 1.1 2002/08/25 23:38:38 karl Exp $
|
2000-01-17 10:39:58 +00:00
|
|
|
|
|
2003-05-02 00:48:41 +00:00
|
|
|
|
Copyright (C) 1998, 1999, 2002 Free Software Foundation, Inc.
|
2000-01-17 10:39:58 +00:00
|
|
|
|
|
|
|
|
|
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
|
|
|
|
|
|
|
|
#include "system.h"
|
|
|
|
|
#include "cmds.h"
|
|
|
|
|
#include "macro.h"
|
|
|
|
|
#include "makeinfo.h"
|
|
|
|
|
#include "insertion.h"
|
|
|
|
|
|
|
|
|
|
/* If non-NULL, this is an output stream to write the full macro expansion
|
|
|
|
|
of the input text to. The result is another texinfo file, but
|
|
|
|
|
missing @include, @infoinclude, @macro, and macro invocations. Instead,
|
|
|
|
|
all of the text is placed within the file. */
|
|
|
|
|
FILE *macro_expansion_output_stream = NULL;
|
|
|
|
|
|
|
|
|
|
/* Output file for -E. */
|
|
|
|
|
char *macro_expansion_filename;
|
|
|
|
|
|
|
|
|
|
/* Nonzero means a macro string is in execution, as opposed to a file. */
|
|
|
|
|
int me_executing_string = 0;
|
|
|
|
|
|
|
|
|
|
/* Nonzero means we want only to expand macros and
|
|
|
|
|
leave everything else intact. */
|
|
|
|
|
int only_macro_expansion = 0;
|
|
|
|
|
|
|
|
|
|
static ITEXT **itext_info = NULL;
|
|
|
|
|
static int itext_size = 0;
|
|
|
|
|
|
|
|
|
|
/* Return the arglist on the current line. This can behave in two different
|
|
|
|
|
ways, depending on the variable BRACES_REQUIRED_FOR_MACRO_ARGS. */
|
|
|
|
|
int braces_required_for_macro_args = 0;
|
|
|
|
|
|
|
|
|
|
/* Array of macros and definitions. */
|
|
|
|
|
MACRO_DEF **macro_list = NULL;
|
|
|
|
|
|
|
|
|
|
int macro_list_len = 0; /* Number of elements. */
|
|
|
|
|
int macro_list_size = 0; /* Number of slots in total. */
|
|
|
|
|
|
|
|
|
|
/* Return the length of the array in ARRAY. */
|
|
|
|
|
int
|
|
|
|
|
array_len (array)
|
|
|
|
|
char **array;
|
|
|
|
|
{
|
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
|
|
if (array)
|
|
|
|
|
for (i = 0; array[i]; i++);
|
|
|
|
|
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
free_array (array)
|
|
|
|
|
char **array;
|
|
|
|
|
{
|
|
|
|
|
if (array)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
for (i = 0; array[i]; i++)
|
|
|
|
|
free (array[i]);
|
|
|
|
|
|
|
|
|
|
free (array);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the macro definition of NAME or NULL if NAME is not defined. */
|
|
|
|
|
MACRO_DEF *
|
|
|
|
|
find_macro (name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
MACRO_DEF *def;
|
|
|
|
|
|
|
|
|
|
def = NULL;
|
|
|
|
|
for (i = 0; macro_list && (def = macro_list[i]); i++)
|
|
|
|
|
{
|
|
|
|
|
if ((!def->inhibited) && (strcmp (def->name, name) == 0))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return def;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add the macro NAME with ARGLIST and BODY to the list of defined macros.
|
|
|
|
|
SOURCE_FILE is the name of the file where this definition can be found,
|
|
|
|
|
and SOURCE_LINENO is the line number within that file. If a macro already
|
|
|
|
|
exists with NAME, then a warning is produced, and that previous
|
|
|
|
|
definition is overwritten. */
|
|
|
|
|
void
|
|
|
|
|
add_macro (name, arglist, body, source_file, source_lineno, flags)
|
|
|
|
|
char *name;
|
|
|
|
|
char **arglist;
|
|
|
|
|
char *body;
|
|
|
|
|
char *source_file;
|
|
|
|
|
int source_lineno, flags;
|
|
|
|
|
{
|
|
|
|
|
MACRO_DEF *def;
|
|
|
|
|
|
|
|
|
|
def = find_macro (name);
|
|
|
|
|
|
|
|
|
|
if (!def)
|
|
|
|
|
{
|
|
|
|
|
if (macro_list_len + 2 >= macro_list_size)
|
|
|
|
|
macro_list = xrealloc
|
|
|
|
|
(macro_list, ((macro_list_size += 10) * sizeof (MACRO_DEF *)));
|
|
|
|
|
|
|
|
|
|
macro_list[macro_list_len] = xmalloc (sizeof (MACRO_DEF));
|
|
|
|
|
macro_list[macro_list_len + 1] = NULL;
|
|
|
|
|
|
|
|
|
|
def = macro_list[macro_list_len];
|
|
|
|
|
macro_list_len += 1;
|
|
|
|
|
def->name = name;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
char *temp_filename = input_filename;
|
|
|
|
|
int temp_line = line_number;
|
|
|
|
|
|
|
|
|
|
warning (_("macro `%s' previously defined"), name);
|
|
|
|
|
|
|
|
|
|
input_filename = def->source_file;
|
|
|
|
|
line_number = def->source_lineno;
|
|
|
|
|
warning (_("here is the previous definition of `%s'"), name);
|
|
|
|
|
|
|
|
|
|
input_filename = temp_filename;
|
|
|
|
|
line_number = temp_line;
|
|
|
|
|
|
|
|
|
|
if (def->arglist)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; def->arglist[i]; i++)
|
|
|
|
|
free (def->arglist[i]);
|
|
|
|
|
|
|
|
|
|
free (def->arglist);
|
|
|
|
|
}
|
|
|
|
|
free (def->source_file);
|
|
|
|
|
free (def->body);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def->source_file = xstrdup (source_file);
|
|
|
|
|
def->source_lineno = source_lineno;
|
|
|
|
|
def->body = body;
|
|
|
|
|
def->arglist = arglist;
|
|
|
|
|
def->inhibited = 0;
|
|
|
|
|
def->flags = flags;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char **
|
|
|
|
|
get_brace_args (quote_single)
|
|
|
|
|
int quote_single;
|
|
|
|
|
{
|
|
|
|
|
char **arglist, *word;
|
|
|
|
|
int arglist_index, arglist_size;
|
|
|
|
|
int character, escape_seen, start;
|
|
|
|
|
int depth = 1;
|
|
|
|
|
|
|
|
|
|
/* There is an arglist in braces here, so gather the args inside of it. */
|
|
|
|
|
skip_whitespace_and_newlines ();
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
arglist = NULL;
|
|
|
|
|
arglist_index = arglist_size = 0;
|
|
|
|
|
|
|
|
|
|
get_arg:
|
|
|
|
|
skip_whitespace_and_newlines ();
|
|
|
|
|
start = input_text_offset;
|
|
|
|
|
escape_seen = 0;
|
|
|
|
|
|
|
|
|
|
while ((character = curchar ()))
|
|
|
|
|
{
|
|
|
|
|
if (character == '\\')
|
|
|
|
|
{
|
|
|
|
|
input_text_offset += 2;
|
|
|
|
|
escape_seen = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (character == '{')
|
|
|
|
|
{
|
|
|
|
|
depth++;
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
}
|
|
|
|
|
else if ((character == ',' && !quote_single) ||
|
|
|
|
|
((character == '}') && depth == 1))
|
|
|
|
|
{
|
|
|
|
|
int len = input_text_offset - start;
|
|
|
|
|
|
|
|
|
|
if (len || (character != '}'))
|
|
|
|
|
{
|
|
|
|
|
word = xmalloc (1 + len);
|
|
|
|
|
memcpy (word, input_text + start, len);
|
|
|
|
|
word[len] = 0;
|
|
|
|
|
|
|
|
|
|
/* Clean up escaped characters. */
|
|
|
|
|
if (escape_seen)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
for (i = 0; word[i]; i++)
|
|
|
|
|
if (word[i] == '\\')
|
|
|
|
|
memmove (word + i, word + i + 1,
|
|
|
|
|
1 + strlen (word + i + 1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (arglist_index + 2 >= arglist_size)
|
|
|
|
|
arglist = xrealloc
|
|
|
|
|
(arglist, (arglist_size += 10) * sizeof (char *));
|
|
|
|
|
|
|
|
|
|
arglist[arglist_index++] = word;
|
|
|
|
|
arglist[arglist_index] = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
if (character == '}')
|
|
|
|
|
break;
|
|
|
|
|
else
|
|
|
|
|
goto get_arg;
|
|
|
|
|
}
|
|
|
|
|
else if (character == '}')
|
|
|
|
|
{
|
|
|
|
|
depth--;
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
if (character == '\n') line_number++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return arglist;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char **
|
|
|
|
|
get_macro_args (def)
|
|
|
|
|
MACRO_DEF *def;
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
char *word;
|
|
|
|
|
|
|
|
|
|
/* Quickly check to see if this macro has been invoked with any arguments.
|
|
|
|
|
If not, then don't skip any of the following whitespace. */
|
|
|
|
|
for (i = input_text_offset; i < input_text_length; i++)
|
|
|
|
|
if (!cr_or_whitespace (input_text[i]))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (input_text[i] != '{')
|
|
|
|
|
{
|
|
|
|
|
if (braces_required_for_macro_args)
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Braces are not required to fill out the macro arguments. If
|
|
|
|
|
this macro takes one argument, it is considered to be the
|
|
|
|
|
remainder of the line, sans whitespace. */
|
|
|
|
|
if (def->arglist && def->arglist[0] && !def->arglist[1])
|
|
|
|
|
{
|
|
|
|
|
char **arglist;
|
|
|
|
|
|
|
|
|
|
get_rest_of_line (0, &word);
|
|
|
|
|
if (input_text[input_text_offset - 1] == '\n')
|
|
|
|
|
{
|
|
|
|
|
input_text_offset--;
|
|
|
|
|
line_number--;
|
|
|
|
|
}
|
|
|
|
|
/* canon_white (word); */
|
|
|
|
|
arglist = xmalloc (2 * sizeof (char *));
|
|
|
|
|
arglist[0] = word;
|
|
|
|
|
arglist[1] = NULL;
|
|
|
|
|
return arglist;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* The macro either took no arguments, or took more than
|
|
|
|
|
one argument. In that case, it must be invoked with
|
|
|
|
|
arguments surrounded by braces. */
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return get_brace_args (def->flags & ME_QUOTE_ARG);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Substitute actual parameters for named parameters in body.
|
|
|
|
|
The named parameters which appear in BODY must by surrounded
|
|
|
|
|
reverse slashes, as in \foo\. */
|
|
|
|
|
char *
|
|
|
|
|
apply (named, actuals, body)
|
|
|
|
|
char **named, **actuals, *body;
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
int new_body_index, new_body_size;
|
|
|
|
|
char *new_body, *text;
|
|
|
|
|
int length_of_actuals;
|
|
|
|
|
|
|
|
|
|
length_of_actuals = array_len (actuals);
|
|
|
|
|
new_body_size = strlen (body);
|
|
|
|
|
new_body = xmalloc (1 + new_body_size);
|
|
|
|
|
|
|
|
|
|
/* Copy chars from BODY into NEW_BODY. */
|
|
|
|
|
i = 0;
|
|
|
|
|
new_body_index = 0;
|
|
|
|
|
|
|
|
|
|
while (body[i])
|
|
|
|
|
{ /* Anything but a \ is easy. */
|
|
|
|
|
if (body[i] != '\\')
|
|
|
|
|
new_body[new_body_index++] = body[i++];
|
|
|
|
|
else
|
|
|
|
|
{ /* Snarf parameter name, check against named parameters. */
|
|
|
|
|
char *param;
|
|
|
|
|
int param_start, which, len;
|
|
|
|
|
|
|
|
|
|
param_start = ++i;
|
|
|
|
|
while (body[i] && body[i] != '\\')
|
|
|
|
|
i++;
|
|
|
|
|
|
|
|
|
|
len = i - param_start;
|
|
|
|
|
param = xmalloc (1 + len);
|
|
|
|
|
memcpy (param, body + param_start, len);
|
|
|
|
|
param[len] = 0;
|
|
|
|
|
|
|
|
|
|
if (body[i]) /* move past \ */
|
|
|
|
|
i++;
|
|
|
|
|
|
|
|
|
|
/* Now check against named parameters. */
|
|
|
|
|
for (which = 0; named && named[which]; which++)
|
|
|
|
|
if (STREQ (named[which], param))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (named && named[which])
|
|
|
|
|
{
|
|
|
|
|
text = which < length_of_actuals ? actuals[which] : NULL;
|
|
|
|
|
if (!text)
|
|
|
|
|
text = "";
|
|
|
|
|
len = strlen (text);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{ /* not a parameter, either it's \\ (if len==0) or an
|
|
|
|
|
error. In either case, restore one \ at least. */
|
|
|
|
|
if (len) {
|
|
|
|
|
warning (_("\\ in macro expansion followed by `%s' instead of \\ or parameter name"),
|
|
|
|
|
param);
|
|
|
|
|
}
|
|
|
|
|
len++;
|
|
|
|
|
text = xmalloc (1 + len);
|
|
|
|
|
sprintf (text, "\\%s", param);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strlen (param) + 2 < len)
|
|
|
|
|
{
|
|
|
|
|
new_body_size += len + 1;
|
|
|
|
|
new_body = xrealloc (new_body, new_body_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free (param);
|
|
|
|
|
|
|
|
|
|
strcpy (new_body + new_body_index, text);
|
|
|
|
|
new_body_index += len;
|
|
|
|
|
|
|
|
|
|
if (!named || !named[which])
|
|
|
|
|
free (text);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
new_body[new_body_index] = 0;
|
|
|
|
|
return new_body;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Expand macro passed in DEF, a pointer to a MACRO_DEF, and
|
|
|
|
|
return its expansion as a string. */
|
|
|
|
|
char *
|
|
|
|
|
expand_macro (def)
|
|
|
|
|
MACRO_DEF *def;
|
|
|
|
|
{
|
|
|
|
|
char **arglist;
|
|
|
|
|
int num_args;
|
|
|
|
|
char *execution_string = NULL;
|
|
|
|
|
int start_line = line_number;
|
|
|
|
|
|
|
|
|
|
/* Find out how many arguments this macro definition takes. */
|
|
|
|
|
num_args = array_len (def->arglist);
|
|
|
|
|
|
|
|
|
|
/* Gather the arguments present on the line if there are any. */
|
|
|
|
|
arglist = get_macro_args (def);
|
|
|
|
|
|
|
|
|
|
if (num_args < array_len (arglist))
|
|
|
|
|
{
|
|
|
|
|
free_array (arglist);
|
|
|
|
|
line_error (_("Macro `%s' called on line %d with too many args"),
|
|
|
|
|
def->name, start_line);
|
|
|
|
|
return execution_string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (def->body)
|
|
|
|
|
execution_string = apply (def->arglist, arglist, def->body);
|
|
|
|
|
|
|
|
|
|
free_array (arglist);
|
|
|
|
|
return execution_string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Execute the macro passed in DEF, a pointer to a MACRO_DEF. */
|
|
|
|
|
void
|
|
|
|
|
execute_macro (def)
|
|
|
|
|
MACRO_DEF *def;
|
|
|
|
|
{
|
|
|
|
|
char *execution_string;
|
|
|
|
|
int start_line = line_number, end_line;
|
|
|
|
|
|
|
|
|
|
if (macro_expansion_output_stream && !executing_string && !me_inhibit_expansion)
|
|
|
|
|
me_append_before_this_command ();
|
|
|
|
|
|
|
|
|
|
execution_string = expand_macro (def);
|
|
|
|
|
if (!execution_string)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (def->body)
|
|
|
|
|
{
|
|
|
|
|
/* Reset the line number to where the macro arguments began.
|
|
|
|
|
This makes line numbers reported in error messages correct in
|
|
|
|
|
case the macro arguments span several lines and the expanded
|
|
|
|
|
arguments invoke other commands. */
|
|
|
|
|
end_line = line_number;
|
|
|
|
|
line_number = start_line;
|
|
|
|
|
|
|
|
|
|
if (macro_expansion_output_stream && !executing_string && !me_inhibit_expansion)
|
|
|
|
|
{
|
|
|
|
|
remember_itext (input_text, input_text_offset);
|
|
|
|
|
me_execute_string (execution_string);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
execute_string ("%s", execution_string);
|
|
|
|
|
|
|
|
|
|
free (execution_string);
|
|
|
|
|
line_number = end_line;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Read and remember the definition of a macro. If RECURSIVE is set,
|
|
|
|
|
set the ME_RECURSE flag. MACTYPE is either "macro" or "rmacro", and
|
|
|
|
|
tells us what the matching @end should be. */
|
|
|
|
|
static void
|
|
|
|
|
define_macro (mactype, recursive)
|
|
|
|
|
char *mactype;
|
|
|
|
|
int recursive;
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
char *name, **arglist, *body, *line, *last_end;
|
|
|
|
|
int body_size, body_index;
|
|
|
|
|
int depth = 1;
|
|
|
|
|
int defining_line = line_number;
|
|
|
|
|
int flags = 0;
|
|
|
|
|
|
|
|
|
|
arglist = NULL;
|
|
|
|
|
body = NULL;
|
|
|
|
|
body_size = 0;
|
|
|
|
|
body_index = 0;
|
|
|
|
|
|
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
|
|
|
|
me_append_before_this_command ();
|
|
|
|
|
|
|
|
|
|
skip_whitespace ();
|
|
|
|
|
|
|
|
|
|
/* Get the name of the macro. This is the set of characters which are
|
|
|
|
|
not whitespace and are not `{' immediately following the @macro. */
|
|
|
|
|
{
|
|
|
|
|
int start = input_text_offset;
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
for (i = start;
|
|
|
|
|
(i < input_text_length) &&
|
|
|
|
|
(input_text[i] != '{') &&
|
|
|
|
|
(!cr_or_whitespace (input_text[i]));
|
|
|
|
|
i++);
|
|
|
|
|
|
|
|
|
|
len = i - start;
|
|
|
|
|
name = xmalloc (1 + len);
|
|
|
|
|
memcpy (name, input_text + start, len);
|
|
|
|
|
name[len] = 0;
|
|
|
|
|
input_text_offset = i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
skip_whitespace ();
|
|
|
|
|
|
|
|
|
|
/* It is not required that the definition of a macro includes an arglist.
|
|
|
|
|
If not, don't try to get the named parameters, just use a null list. */
|
|
|
|
|
if (curchar () == '{')
|
|
|
|
|
{
|
|
|
|
|
int character;
|
|
|
|
|
int arglist_index = 0, arglist_size = 0;
|
|
|
|
|
int gathering_words = 1;
|
|
|
|
|
char *word = NULL;
|
|
|
|
|
|
|
|
|
|
/* Read the words inside of the braces which determine the arglist.
|
|
|
|
|
These words will be replaced within the body of the macro at
|
|
|
|
|
execution time. */
|
|
|
|
|
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
skip_whitespace_and_newlines ();
|
|
|
|
|
|
|
|
|
|
while (gathering_words)
|
|
|
|
|
{
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
for (i = input_text_offset;
|
|
|
|
|
(character = input_text[i]);
|
|
|
|
|
i++)
|
|
|
|
|
{
|
|
|
|
|
switch (character)
|
|
|
|
|
{
|
|
|
|
|
case '\n':
|
|
|
|
|
line_number++;
|
|
|
|
|
case ' ':
|
|
|
|
|
case '\t':
|
|
|
|
|
case ',':
|
|
|
|
|
case '}':
|
|
|
|
|
/* Found the end of the current arglist word. Save it. */
|
|
|
|
|
len = i - input_text_offset;
|
|
|
|
|
word = xmalloc (1 + len);
|
|
|
|
|
memcpy (word, input_text + input_text_offset, len);
|
|
|
|
|
word[len] = 0;
|
|
|
|
|
input_text_offset = i;
|
|
|
|
|
|
|
|
|
|
/* Advance to the comma or close-brace that signified
|
|
|
|
|
the end of the argument. */
|
|
|
|
|
while ((character = curchar ())
|
|
|
|
|
&& character != ','
|
|
|
|
|
&& character != '}')
|
|
|
|
|
{
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
if (character == '\n')
|
|
|
|
|
line_number++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add the word to our list of words. */
|
|
|
|
|
if (arglist_index + 2 >= arglist_size)
|
|
|
|
|
{
|
|
|
|
|
arglist_size += 10;
|
|
|
|
|
arglist = xrealloc (arglist,
|
|
|
|
|
arglist_size * sizeof (char *));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
arglist[arglist_index++] = word;
|
|
|
|
|
arglist[arglist_index] = NULL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (character == '}')
|
|
|
|
|
{
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
gathering_words = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (character == ',')
|
|
|
|
|
{
|
|
|
|
|
input_text_offset++;
|
|
|
|
|
skip_whitespace_and_newlines ();
|
|
|
|
|
i = input_text_offset - 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we have exactly one argument, do @quote-arg implicitly. Not
|
|
|
|
|
only does this match TeX's behavior (which can't feasibly be
|
|
|
|
|
changed), but it's a good idea. */
|
|
|
|
|
if (arglist_index == 1)
|
|
|
|
|
flags |= ME_QUOTE_ARG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read the text carefully until we find an "@end macro" which
|
|
|
|
|
matches this one. The text in between is the body of the macro. */
|
|
|
|
|
skip_whitespace_and_newlines ();
|
|
|
|
|
|
|
|
|
|
while (depth)
|
|
|
|
|
{
|
|
|
|
|
if ((input_text_offset + 9) > input_text_length)
|
|
|
|
|
{
|
2002-03-25 13:08:32 +00:00
|
|
|
|
file_line_error (input_filename, defining_line,
|
|
|
|
|
_("%cend macro not found"), COMMAND_PREFIX);
|
2000-01-17 10:39:58 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get_rest_of_line (0, &line);
|
|
|
|
|
|
|
|
|
|
/* Handle commands only meaningful within a macro. */
|
|
|
|
|
if ((*line == COMMAND_PREFIX) && (depth == 1) &&
|
|
|
|
|
(strncmp (line + 1, "allow-recursion", 15) == 0) &&
|
|
|
|
|
(line[16] == 0 || whitespace (line[16])))
|
|
|
|
|
{
|
|
|
|
|
for (i = 16; whitespace (line[i]); i++);
|
|
|
|
|
strcpy (line, line + i);
|
|
|
|
|
flags |= ME_RECURSE;
|
|
|
|
|
if (!*line)
|
|
|
|
|
{
|
|
|
|
|
free (line);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((*line == COMMAND_PREFIX) && (depth == 1) &&
|
|
|
|
|
(strncmp (line + 1, "quote-arg", 9) == 0) &&
|
|
|
|
|
(line[10] == 0 || whitespace (line[10])))
|
|
|
|
|
{
|
|
|
|
|
for (i = 10; whitespace (line[i]); i++);
|
|
|
|
|
strcpy (line, line + i);
|
|
|
|
|
|
|
|
|
|
if (arglist && arglist[0] && !arglist[1])
|
|
|
|
|
{
|
|
|
|
|
flags |= ME_QUOTE_ARG;
|
|
|
|
|
if (!*line)
|
|
|
|
|
{
|
|
|
|
|
free (line);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
line_error (_("@quote-arg only useful for single-argument macros"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (*line == COMMAND_PREFIX
|
|
|
|
|
&& (strncmp (line + 1, "macro ", 6) == 0
|
|
|
|
|
|| strncmp (line + 1, "rmacro ", 7) == 0))
|
|
|
|
|
depth++;
|
|
|
|
|
|
|
|
|
|
/* Incorrect implementation of nesting -- just check that the last
|
|
|
|
|
@end matches what we started with. Since nested macros don't
|
|
|
|
|
work in TeX anyway, this isn't worth the trouble to get right. */
|
|
|
|
|
if (*line == COMMAND_PREFIX && strncmp (line + 1, "end macro", 9) == 0)
|
|
|
|
|
{
|
|
|
|
|
depth--;
|
|
|
|
|
last_end = "macro";
|
|
|
|
|
}
|
|
|
|
|
if (*line == COMMAND_PREFIX && strncmp (line + 1, "end rmacro", 9) == 0)
|
|
|
|
|
{
|
|
|
|
|
depth--;
|
|
|
|
|
last_end = "rmacro";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (depth)
|
|
|
|
|
{
|
|
|
|
|
if ((body_index + strlen (line) + 3) >= body_size)
|
|
|
|
|
body = xrealloc (body, body_size += 3 + strlen (line));
|
|
|
|
|
strcpy (body + body_index, line);
|
|
|
|
|
body_index += strlen (line);
|
|
|
|
|
body[body_index++] = '\n';
|
|
|
|
|
body[body_index] = 0;
|
|
|
|
|
}
|
|
|
|
|
free (line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check that @end matched the macro command. */
|
|
|
|
|
if (!STREQ (last_end, mactype))
|
|
|
|
|
warning (_("mismatched @end %s with @%s"), last_end, mactype);
|
|
|
|
|
|
|
|
|
|
/* If it was an empty macro like
|
|
|
|
|
@macro foo
|
|
|
|
|
@end macro
|
|
|
|
|
create an empty body. (Otherwise, the macro is not expanded.) */
|
|
|
|
|
if (!body)
|
|
|
|
|
{
|
|
|
|
|
body = (char *)malloc(1);
|
|
|
|
|
*body = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We now have the name, the arglist, and the body. However, BODY
|
|
|
|
|
includes the final newline which preceded the `@end macro' text.
|
|
|
|
|
Delete it. */
|
|
|
|
|
if (body && strlen (body))
|
|
|
|
|
body[strlen (body) - 1] = 0;
|
|
|
|
|
|
|
|
|
|
if (recursive)
|
|
|
|
|
flags |= ME_RECURSE;
|
|
|
|
|
|
|
|
|
|
add_macro (name, arglist, body, input_filename, defining_line, flags);
|
|
|
|
|
|
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
|
|
|
|
remember_itext (input_text, input_text_offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_macro ()
|
|
|
|
|
{
|
|
|
|
|
define_macro ("macro", 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_rmacro ()
|
|
|
|
|
{
|
|
|
|
|
define_macro ("rmacro", 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Delete the macro with name NAME. The macro is deleted from the list,
|
|
|
|
|
but it is also returned. If there was no macro defined, NULL is
|
|
|
|
|
returned. */
|
|
|
|
|
|
|
|
|
|
static MACRO_DEF *
|
|
|
|
|
delete_macro (name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
MACRO_DEF *def;
|
|
|
|
|
|
|
|
|
|
def = NULL;
|
|
|
|
|
|
|
|
|
|
for (i = 0; macro_list && (def = macro_list[i]); i++)
|
|
|
|
|
if (strcmp (def->name, name) == 0)
|
|
|
|
|
{
|
|
|
|
|
memmove (macro_list + i, macro_list + i + 1,
|
|
|
|
|
((macro_list_len + 1) - i) * sizeof (MACRO_DEF *));
|
|
|
|
|
macro_list_len--;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return def;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cm_unmacro ()
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
char *line, *name;
|
|
|
|
|
MACRO_DEF *def;
|
|
|
|
|
|
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
|
|
|
|
me_append_before_this_command ();
|
|
|
|
|
|
|
|
|
|
get_rest_of_line (0, &line);
|
|
|
|
|
|
|
|
|
|
for (i = 0; line[i] && !whitespace (line[i]); i++);
|
|
|
|
|
name = xmalloc (i + 1);
|
|
|
|
|
memcpy (name, line, i);
|
|
|
|
|
name[i] = 0;
|
|
|
|
|
|
|
|
|
|
def = delete_macro (name);
|
|
|
|
|
|
|
|
|
|
if (def)
|
|
|
|
|
{
|
|
|
|
|
free (def->source_file);
|
|
|
|
|
free (def->name);
|
|
|
|
|
free (def->body);
|
|
|
|
|
|
|
|
|
|
if (def->arglist)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; def->arglist[i]; i++)
|
|
|
|
|
free (def->arglist[i]);
|
|
|
|
|
|
|
|
|
|
free (def->arglist);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free (def);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free (line);
|
|
|
|
|
free (name);
|
|
|
|
|
|
|
|
|
|
if (macro_expansion_output_stream && !executing_string)
|
|
|
|
|
remember_itext (input_text, input_text_offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* How to output sections of the input file verbatim. */
|
|
|
|
|
|
|
|
|
|
/* Set the value of POINTER's offset to OFFSET. */
|
|
|
|
|
ITEXT *
|
|
|
|
|
remember_itext (pointer, offset)
|
|
|
|
|
char *pointer;
|
|
|
|
|
int offset;
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
ITEXT *itext = NULL;
|
|
|
|
|
|
|
|
|
|
/* If we have no info, initialize a blank list. */
|
|
|
|
|
if (!itext_info)
|
|
|
|
|
{
|
|
|
|
|
itext_info = xmalloc ((itext_size = 10) * sizeof (ITEXT *));
|
|
|
|
|
for (i = 0; i < itext_size; i++)
|
|
|
|
|
itext_info[i] = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the pointer is already present in the list, then set the offset. */
|
|
|
|
|
for (i = 0; i < itext_size; i++)
|
|
|
|
|
if ((itext_info[i]) &&
|
|
|
|
|
(itext_info[i]->pointer == pointer))
|
|
|
|
|
{
|
|
|
|
|
itext = itext_info[i];
|
|
|
|
|
itext_info[i]->offset = offset;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i == itext_size)
|
|
|
|
|
{
|
|
|
|
|
/* Find a blank slot (or create a new one), and remember the
|
|
|
|
|
pointer and offset. */
|
|
|
|
|
for (i = 0; i < itext_size; i++)
|
|
|
|
|
if (itext_info[i] == NULL)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* If not found, then add some slots. */
|
|
|
|
|
if (i == itext_size)
|
|
|
|
|
{
|
|
|
|
|
int j;
|
|
|
|
|
|
|
|
|
|
itext_info = xrealloc
|
|
|
|
|
(itext_info, (itext_size += 10) * sizeof (ITEXT *));
|
|
|
|
|
|
|
|
|
|
for (j = i; j < itext_size; j++)
|
|
|
|
|
itext_info[j] = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now add the pointer and the offset. */
|
|
|
|
|
itext_info[i] = xmalloc (sizeof (ITEXT));
|
|
|
|
|
itext_info[i]->pointer = pointer;
|
|
|
|
|
itext_info[i]->offset = offset;
|
|
|
|
|
itext = itext_info[i];
|
|
|
|
|
}
|
|
|
|
|
return itext;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Forget the input text associated with POINTER. */
|
|
|
|
|
void
|
|
|
|
|
forget_itext (pointer)
|
|
|
|
|
char *pointer;
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < itext_size; i++)
|
|
|
|
|
if (itext_info[i] && (itext_info[i]->pointer == pointer))
|
|
|
|
|
{
|
|
|
|
|
free (itext_info[i]);
|
|
|
|
|
itext_info[i] = NULL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Append the text which appeared in input_text from the last offset to
|
|
|
|
|
the character just before the command that we are currently executing. */
|
|
|
|
|
void
|
|
|
|
|
me_append_before_this_command ()
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = input_text_offset; i && (input_text[i] != COMMAND_PREFIX); i--)
|
|
|
|
|
;
|
|
|
|
|
maybe_write_itext (input_text, i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Similar to execute_string, but only takes a single string argument,
|
|
|
|
|
and remembers the input text location, etc. */
|
|
|
|
|
void
|
|
|
|
|
me_execute_string (execution_string)
|
|
|
|
|
char *execution_string;
|
|
|
|
|
{
|
|
|
|
|
int saved_escape_html = escape_html;
|
|
|
|
|
int saved_in_paragraph = in_paragraph;
|
|
|
|
|
escape_html = me_executing_string == 0;
|
|
|
|
|
in_paragraph = 0;
|
|
|
|
|
|
|
|
|
|
pushfile ();
|
|
|
|
|
input_text_offset = 0;
|
|
|
|
|
/* The following xstrdup is so we can relocate input_text at will. */
|
|
|
|
|
input_text = xstrdup (execution_string);
|
|
|
|
|
input_filename = xstrdup (input_filename);
|
|
|
|
|
input_text_length = strlen (execution_string);
|
|
|
|
|
|
|
|
|
|
remember_itext (input_text, 0);
|
|
|
|
|
|
|
|
|
|
me_executing_string++;
|
|
|
|
|
reader_loop ();
|
|
|
|
|
free (input_text);
|
|
|
|
|
free (input_filename);
|
|
|
|
|
popfile ();
|
|
|
|
|
me_executing_string--;
|
|
|
|
|
|
|
|
|
|
in_paragraph = saved_in_paragraph;
|
|
|
|
|
escape_html = saved_escape_html;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* A wrapper around me_execute_string which saves and restores
|
|
|
|
|
variables important for output generation. This is called
|
|
|
|
|
when we need to produce macro-expanded output for input which
|
|
|
|
|
leaves no traces in the Info output. */
|
|
|
|
|
void
|
|
|
|
|
me_execute_string_keep_state (execution_string, append_string)
|
|
|
|
|
char *execution_string, *append_string;
|
|
|
|
|
{
|
|
|
|
|
int op_orig, opcol_orig, popen_orig;
|
|
|
|
|
int fill_orig, newline_orig, indent_orig, meta_pos_orig;
|
|
|
|
|
|
|
|
|
|
remember_itext (input_text, input_text_offset);
|
|
|
|
|
op_orig = output_paragraph_offset;
|
|
|
|
|
meta_pos_orig = meta_char_pos;
|
|
|
|
|
opcol_orig = output_column;
|
|
|
|
|
popen_orig = paragraph_is_open;
|
|
|
|
|
fill_orig = filling_enabled;
|
|
|
|
|
newline_orig = last_char_was_newline;
|
|
|
|
|
filling_enabled = 0;
|
|
|
|
|
indent_orig = no_indent;
|
|
|
|
|
no_indent = 1;
|
|
|
|
|
me_execute_string (execution_string);
|
|
|
|
|
if (append_string)
|
|
|
|
|
write_region_to_macro_output (append_string, 0, strlen (append_string));
|
|
|
|
|
output_paragraph_offset = op_orig;
|
|
|
|
|
meta_char_pos = meta_pos_orig;
|
|
|
|
|
output_column = opcol_orig;
|
|
|
|
|
paragraph_is_open = popen_orig;
|
|
|
|
|
filling_enabled = fill_orig;
|
|
|
|
|
last_char_was_newline = newline_orig;
|
|
|
|
|
no_indent = indent_orig;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Append the text which appears in input_text from the last offset to
|
|
|
|
|
the current OFFSET. */
|
|
|
|
|
void
|
|
|
|
|
append_to_expansion_output (offset)
|
|
|
|
|
int offset;
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
ITEXT *itext = NULL;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < itext_size; i++)
|
|
|
|
|
if (itext_info[i] && itext_info[i]->pointer == input_text)
|
|
|
|
|
{
|
|
|
|
|
itext = itext_info[i];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!itext)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (offset > itext->offset)
|
|
|
|
|
{
|
|
|
|
|
write_region_to_macro_output (input_text, itext->offset, offset);
|
|
|
|
|
remember_itext (input_text, offset);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Only write this input text iff it appears in our itext list. */
|
|
|
|
|
void
|
|
|
|
|
maybe_write_itext (pointer, offset)
|
|
|
|
|
char *pointer;
|
|
|
|
|
int offset;
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
ITEXT *itext = NULL;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < itext_size; i++)
|
|
|
|
|
if (itext_info[i] && (itext_info[i]->pointer == pointer))
|
|
|
|
|
{
|
|
|
|
|
itext = itext_info[i];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (itext && (itext->offset < offset))
|
|
|
|
|
{
|
|
|
|
|
write_region_to_macro_output (itext->pointer, itext->offset, offset);
|
|
|
|
|
remember_itext (pointer, offset);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
write_region_to_macro_output (string, start, end)
|
|
|
|
|
char *string;
|
|
|
|
|
int start, end;
|
|
|
|
|
{
|
|
|
|
|
if (macro_expansion_output_stream)
|
|
|
|
|
fwrite (string + start, 1, end - start, macro_expansion_output_stream);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Aliases. */
|
|
|
|
|
|
|
|
|
|
typedef struct alias_struct
|
|
|
|
|
{
|
|
|
|
|
char *alias;
|
|
|
|
|
char *mapto;
|
|
|
|
|
struct alias_struct *next;
|
|
|
|
|
} alias_type;
|
|
|
|
|
|
|
|
|
|
static alias_type *aliases;
|
|
|
|
|
|
|
|
|
|
/* @alias */
|
|
|
|
|
void
|
|
|
|
|
cm_alias ()
|
|
|
|
|
{
|
|
|
|
|
alias_type *a = xmalloc (sizeof (alias_type));
|
|
|
|
|
|
|
|
|
|
skip_whitespace ();
|
|
|
|
|
get_until_in_line (1, "=", &(a->alias));
|
2002-03-25 13:08:32 +00:00
|
|
|
|
canon_white (a->alias);
|
|
|
|
|
|
2000-01-17 10:39:58 +00:00
|
|
|
|
discard_until ("=");
|
|
|
|
|
skip_whitespace ();
|
|
|
|
|
get_until_in_line (0, " ", &(a->mapto));
|
|
|
|
|
|
|
|
|
|
a->next = aliases;
|
|
|
|
|
aliases = a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Perform an alias expansion. Called from read_command. */
|
|
|
|
|
char *
|
|
|
|
|
alias_expand (tok)
|
|
|
|
|
char *tok;
|
|
|
|
|
{
|
|
|
|
|
alias_type *findit = aliases;
|
|
|
|
|
|
|
|
|
|
while (findit)
|
|
|
|
|
if (strcmp (findit->alias, tok) == 0)
|
|
|
|
|
{
|
|
|
|
|
free (tok);
|
|
|
|
|
return alias_expand (xstrdup (findit->mapto));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
findit = findit->next;
|
|
|
|
|
|
|
|
|
|
return tok;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* definfoenclose implementation. */
|
|
|
|
|
|
|
|
|
|
/* This structure is used to track enclosure macros. When an enclosure
|
|
|
|
|
macro is recognized, a pointer to the enclosure block corresponding
|
|
|
|
|
to its name is saved in the brace element for its argument. */
|
|
|
|
|
typedef struct enclose_struct
|
|
|
|
|
{
|
|
|
|
|
char *enclose;
|
|
|
|
|
char *before;
|
|
|
|
|
char *after;
|
|
|
|
|
struct enclose_struct *next;
|
|
|
|
|
} enclosure_type;
|
|
|
|
|
|
|
|
|
|
static enclosure_type *enclosures;
|
|
|
|
|
|
|
|
|
|
typedef struct enclosure_stack_struct
|
|
|
|
|
{
|
|
|
|
|
enclosure_type *current;
|
|
|
|
|
struct enclosure_stack_struct *next;
|
|
|
|
|
} enclosure_stack_type;
|
|
|
|
|
|
|
|
|
|
static enclosure_stack_type *enclosure_stack;
|
|
|
|
|
|
|
|
|
|
/* @definfoenclose */
|
|
|
|
|
void
|
|
|
|
|
cm_definfoenclose ()
|
|
|
|
|
{
|
|
|
|
|
enclosure_type *e = xmalloc (sizeof (enclosure_type));
|
|
|
|
|
|
|
|
|
|
skip_whitespace ();
|
|
|
|
|
get_until_in_line (1, ",", &(e->enclose));
|
|
|
|
|
discard_until (",");
|
|
|
|
|
get_until_in_line (0, ",", &(e->before));
|
|
|
|
|
discard_until (",");
|
|
|
|
|
get_until_in_line (0, "\n", &(e->after));
|
|
|
|
|
|
|
|
|
|
e->next = enclosures;
|
|
|
|
|
enclosures = e;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If TOK is an enclosure command, push it on the enclosure stack and
|
|
|
|
|
return 1. Else return 0. */
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
enclosure_command (tok)
|
|
|
|
|
char *tok;
|
|
|
|
|
{
|
|
|
|
|
enclosure_type *findit = enclosures;
|
|
|
|
|
|
|
|
|
|
while (findit)
|
|
|
|
|
if (strcmp (findit->enclose, tok) == 0)
|
|
|
|
|
{
|
|
|
|
|
enclosure_stack_type *new = xmalloc (sizeof (enclosure_stack_type));
|
|
|
|
|
new->current = findit;
|
|
|
|
|
new->next = enclosure_stack;
|
|
|
|
|
enclosure_stack = new;
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
findit = findit->next;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* actually perform the enclosure expansion */
|
|
|
|
|
void
|
|
|
|
|
enclosure_expand (arg, start, end)
|
|
|
|
|
int arg, start, end;
|
|
|
|
|
{
|
|
|
|
|
if (arg == START)
|
|
|
|
|
add_word (enclosure_stack->current->before);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
enclosure_stack_type *temp;
|
|
|
|
|
|
|
|
|
|
add_word (enclosure_stack->current->after);
|
|
|
|
|
|
|
|
|
|
temp = enclosure_stack;
|
|
|
|
|
enclosure_stack = enclosure_stack->next;
|
|
|
|
|
free (temp);
|
|
|
|
|
}
|
|
|
|
|
}
|