diff options
Diffstat (limited to 'contrib/texinfo/makeinfo/macro.c')
-rw-r--r-- | contrib/texinfo/makeinfo/macro.c | 1114 |
1 files changed, 1114 insertions, 0 deletions
diff --git a/contrib/texinfo/makeinfo/macro.c b/contrib/texinfo/makeinfo/macro.c new file mode 100644 index 000000000000..8c89da257106 --- /dev/null +++ b/contrib/texinfo/makeinfo/macro.c @@ -0,0 +1,1114 @@ +/* macro.c -- user-defined macros for Texinfo. + $Id: macro.c,v 1.10 1999/08/17 21:06:35 karl Exp $ + + Copyright (C) 1998, 99 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., 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) + { + int temp_line = line_number; + line_number = defining_line; + line_error (_("%cend macro not found"), COMMAND_PREFIX); + line_number = temp_line; + 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)); + 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); + } +} |