Home | History | Annotate | Line # | Download | only in makeinfo
      1 /*	$NetBSD: macro.c,v 1.4 2025/12/31 22:18:50 oster Exp $	*/
      2 
      3 /* macro.c -- user-defined macros for Texinfo.
      4    Id: macro.c,v 1.6 2004/04/11 17:56:47 karl Exp
      5 
      6    Copyright (C) 1998, 1999, 2002, 2003 Free Software Foundation, Inc.
      7 
      8    This program is free software; you can redistribute it and/or modify
      9    it under the terms of the GNU General Public License as published by
     10    the Free Software Foundation; either version 2, or (at your option)
     11    any later version.
     12 
     13    This program is distributed in the hope that it will be useful,
     14    but WITHOUT ANY WARRANTY; without even the implied warranty of
     15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16    GNU General Public License for more details.
     17 
     18    You should have received a copy of the GNU General Public License
     19    along with this program; if not, write to the Free Software Foundation,
     20    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
     21 
     22 #include "system.h"
     23 #include "cmds.h"
     24 #include "files.h"
     25 #include "macro.h"
     26 #include "makeinfo.h"
     27 #include "insertion.h"
     28 
     29 /* If non-NULL, this is an output stream to write the full macro expansion
     30    of the input text to.  The result is another texinfo file, but
     31    missing @include, @infoinclude, @macro, and macro invocations.  Instead,
     32    all of the text is placed within the file. */
     33 FILE *macro_expansion_output_stream = NULL;
     34 
     35 /* Output file for -E.  */
     36 char *macro_expansion_filename;
     37 
     38 /* Nonzero means a macro string is in execution, as opposed to a file. */
     39 int me_executing_string = 0;
     40 
     41 /* Nonzero means we want only to expand macros and
     42    leave everything else intact.  */
     43 int only_macro_expansion = 0;
     44 
     45 static ITEXT **itext_info = NULL;
     46 static int itext_size = 0;
     47 
     48 /* Return the arglist on the current line.  This can behave in two different
     49    ways, depending on the variable BRACES_REQUIRED_FOR_MACRO_ARGS. */
     50 int braces_required_for_macro_args = 0;
     51 
     52 /* Array of macros and definitions. */
     53 MACRO_DEF **macro_list = NULL;
     54 
     55 int macro_list_len = 0;         /* Number of elements. */
     56 int macro_list_size = 0;        /* Number of slots in total. */
     57 
     58 /* Return the length of the array in ARRAY. */
     60 int
     61 array_len (char **array)
     62 {
     63   int i = 0;
     64 
     65   if (array)
     66     for (i = 0; array[i]; i++);
     67 
     68   return i;
     69 }
     70 
     71 void
     72 free_array (char **array)
     73 {
     74   if (array)
     75     {
     76       int i;
     77       for (i = 0; array[i]; i++)
     78         free (array[i]);
     79 
     80       free (array);
     81     }
     82 }
     83 
     84 /* Return the macro definition of NAME or NULL if NAME is not defined. */
     86 MACRO_DEF *
     87 find_macro (char *name)
     88 {
     89   int i;
     90   MACRO_DEF *def;
     91 
     92   def = NULL;
     93   for (i = 0; macro_list && (def = macro_list[i]); i++)
     94     {
     95       if ((!def->inhibited) && (strcmp (def->name, name) == 0))
     96         break;
     97     }
     98   return def;
     99 }
    100 
    101 /* Add the macro NAME with ARGLIST and BODY to the list of defined macros.
    102    SOURCE_FILE is the name of the file where this definition can be found,
    103    and SOURCE_LINENO is the line number within that file.  If a macro already
    104    exists with NAME, then a warning is produced, and that previous
    105    definition is overwritten. */
    106 static void
    107 add_macro (char *name, char **arglist, char *body, char *source_file,
    108     int source_lineno, int flags)
    109 {
    110   MACRO_DEF *def;
    111 
    112   def = find_macro (name);
    113 
    114   if (!def)
    115     {
    116       if (macro_list_len + 2 >= macro_list_size)
    117         macro_list = xrealloc
    118           (macro_list, ((macro_list_size += 10) * sizeof (MACRO_DEF *)));
    119 
    120       macro_list[macro_list_len] = xmalloc (sizeof (MACRO_DEF));
    121       macro_list[macro_list_len + 1] = NULL;
    122 
    123       def = macro_list[macro_list_len];
    124       macro_list_len += 1;
    125       def->name = name;
    126     }
    127   else
    128     {
    129       char *temp_filename = input_filename;
    130       int temp_line = line_number;
    131 
    132       warning (_("macro `%s' previously defined"), name);
    133 
    134       input_filename = def->source_file;
    135       line_number = def->source_lineno;
    136       warning (_("here is the previous definition of `%s'"), name);
    137 
    138       input_filename = temp_filename;
    139       line_number = temp_line;
    140 
    141       if (def->arglist)
    142         {
    143           int i;
    144 
    145           for (i = 0; def->arglist[i]; i++)
    146             free (def->arglist[i]);
    147 
    148           free (def->arglist);
    149         }
    150       free (def->source_file);
    151       free (def->body);
    152     }
    153 
    154   def->source_file = xstrdup (source_file);
    155   def->source_lineno = source_lineno;
    156   def->body = body;
    157   def->arglist = arglist;
    158   def->inhibited = 0;
    159   def->flags = flags;
    160 }
    161 
    162 
    163 char **
    164 get_brace_args (int quote_single)
    165 {
    166   char **arglist, *word;
    167   int arglist_index, arglist_size;
    168   int character, escape_seen, start;
    169   int depth = 1;
    170 
    171   /* There is an arglist in braces here, so gather the args inside of it. */
    172   skip_whitespace_and_newlines ();
    173   input_text_offset++;
    174   arglist = NULL;
    175   arglist_index = arglist_size = 0;
    176 
    177  get_arg:
    178   skip_whitespace_and_newlines ();
    179   start = input_text_offset;
    180   escape_seen = 0;
    181 
    182   while ((character = curchar ()))
    183     {
    184       if (character == '\\')
    185         {
    186           input_text_offset += 2;
    187           escape_seen = 1;
    188         }
    189       else if (character == '{')
    190         {
    191           depth++;
    192           input_text_offset++;
    193         }
    194       else if ((character == ',' && !quote_single) ||
    195                ((character == '}') && depth == 1))
    196         {
    197           int len = input_text_offset - start;
    198 
    199           if (len || (character != '}'))
    200             {
    201               word = xmalloc (1 + len);
    202               memcpy (word, input_text + start, len);
    203               word[len] = 0;
    204 
    205               /* Clean up escaped characters. */
    206               if (escape_seen)
    207                 {
    208                   int i;
    209                   for (i = 0; word[i]; i++)
    210                     if (word[i] == '\\')
    211                       memmove (word + i, word + i + 1,
    212                                1 + strlen (word + i + 1));
    213                 }
    214 
    215               if (arglist_index + 2 >= arglist_size)
    216                 arglist = xrealloc
    217                   (arglist, (arglist_size += 10) * sizeof (char *));
    218 
    219               arglist[arglist_index++] = word;
    220               arglist[arglist_index] = NULL;
    221             }
    222 
    223           input_text_offset++;
    224           if (character == '}')
    225             break;
    226           else
    227             goto get_arg;
    228         }
    229       else if (character == '}')
    230         {
    231           depth--;
    232           input_text_offset++;
    233         }
    234       else
    235         {
    236           input_text_offset++;
    237           if (character == '\n') line_number++;
    238         }
    239     }
    240   return arglist;
    241 }
    242 
    243 static char **
    244 get_macro_args (MACRO_DEF *def)
    245 {
    246   int i;
    247   char *word;
    248 
    249   /* Quickly check to see if this macro has been invoked with any arguments.
    250      If not, then don't skip any of the following whitespace. */
    251   for (i = input_text_offset; i < input_text_length; i++)
    252     if (!cr_or_whitespace (input_text[i]))
    253       break;
    254 
    255   if (input_text[i] != '{')
    256     {
    257       if (braces_required_for_macro_args)
    258         {
    259           return NULL;
    260         }
    261       else
    262         {
    263           /* Braces are not required to fill out the macro arguments.  If
    264              this macro takes one argument, it is considered to be the
    265              remainder of the line, sans whitespace. */
    266           if (def->arglist && def->arglist[0] && !def->arglist[1])
    267             {
    268               char **arglist;
    269 
    270               get_rest_of_line (0, &word);
    271               if (input_text[input_text_offset - 1] == '\n')
    272                 {
    273                   input_text_offset--;
    274                   line_number--;
    275                 }
    276               /* canon_white (word); */
    277               arglist = xmalloc (2 * sizeof (char *));
    278               arglist[0] = word;
    279               arglist[1] = NULL;
    280               return arglist;
    281             }
    282           else
    283             {
    284               /* The macro either took no arguments, or took more than
    285                  one argument.  In that case, it must be invoked with
    286                  arguments surrounded by braces. */
    287               return NULL;
    288             }
    289         }
    290     }
    291   return get_brace_args (def->flags & ME_QUOTE_ARG);
    292 }
    293 
    294 /* Substitute actual parameters for named parameters in body.
    295    The named parameters which appear in BODY must by surrounded
    296    reverse slashes, as in \foo\. */
    297 static char *
    298 apply (char **named, char **actuals, char *body)
    299 {
    300   int i;
    301   int new_body_index, new_body_size;
    302   char *new_body, *text;
    303   int length_of_actuals;
    304 
    305   length_of_actuals = array_len (actuals);
    306   new_body_size = strlen (body);
    307   new_body = xmalloc (1 + new_body_size);
    308 
    309   /* Copy chars from BODY into NEW_BODY. */
    310   i = 0;
    311   new_body_index = 0;
    312 
    313   while (body[i])
    314     { /* Anything but a \ is easy.  */
    315       if (body[i] != '\\')
    316         new_body[new_body_index++] = body[i++];
    317       else
    318         { /* Snarf parameter name, check against named parameters. */
    319           char *param;
    320           int param_start, len;
    321 
    322           param_start = ++i;
    323           while (body[i] && body[i] != '\\')
    324             i++;
    325 
    326           len = i - param_start;
    327           param = xmalloc (1 + len);
    328           memcpy (param, body + param_start, len);
    329           param[len] = 0;
    330 
    331           if (body[i]) /* move past \ */
    332             i++;
    333 
    334           if (len == 0)
    335             { /* \\ always means \, even if macro has no args.  */
    336               len++;
    337               text = xmalloc (1 + len);
    338               sprintf (text, "\\%s", param);
    339             }
    340           else
    341             {
    342               int which;
    343 
    344               /* Check against named parameters. */
    345               for (which = 0; named && named[which]; which++)
    346                 if (STREQ (named[which], param))
    347                   break;
    348 
    349               if (named && named[which])
    350                 {
    351                   text = which < length_of_actuals ? actuals[which] : NULL;
    352                   if (!text)
    353                     text = "";
    354                   len = strlen (text);
    355                   text = xstrdup (text);  /* so we can free it */
    356                 }
    357               else
    358                 { /* not a parameter, so it's an error.  */
    359                   warning (_("\\ in macro expansion followed by `%s' instead of parameter name"),
    360                              param);
    361                   len++;
    362                   text = xmalloc (1 + len);
    363                   sprintf (text, "\\%s", param);
    364                 }
    365             }
    366 
    367           if (strlen (param) + 2 < len)
    368             {
    369               new_body_size += len + 1;
    370               new_body = xrealloc (new_body, new_body_size);
    371             }
    372 
    373           free (param);
    374 
    375           strcpy (new_body + new_body_index, text);
    376           new_body_index += len;
    377 
    378           free (text);
    379         }
    380     }
    381 
    382   new_body[new_body_index] = 0;
    383   return new_body;
    384 }
    385 
    386 /* Expand macro passed in DEF, a pointer to a MACRO_DEF, and
    387    return its expansion as a string.  */
    388 char *
    389 expand_macro (MACRO_DEF *def)
    390 {
    391   char **arglist;
    392   int num_args;
    393   char *execution_string = NULL;
    394   int start_line = line_number;
    395 
    396   /* Find out how many arguments this macro definition takes. */
    397   num_args = array_len (def->arglist);
    398 
    399   /* Gather the arguments present on the line if there are any. */
    400   arglist = get_macro_args (def);
    401 
    402   if (num_args < array_len (arglist))
    403     {
    404       free_array (arglist);
    405       line_error (_("Macro `%s' called on line %d with too many args"),
    406                   def->name, start_line);
    407       return execution_string;
    408     }
    409 
    410   if (def->body)
    411     execution_string = apply (def->arglist, arglist, def->body);
    412 
    413   free_array (arglist);
    414   return execution_string;
    415 }
    416 
    417 /* Execute the macro passed in DEF, a pointer to a MACRO_DEF.  */
    418 void
    419 execute_macro (MACRO_DEF *def)
    420 {
    421   char *execution_string;
    422   int start_line = line_number, end_line;
    423 
    424   if (macro_expansion_output_stream && !executing_string && !me_inhibit_expansion)
    425     me_append_before_this_command ();
    426 
    427   execution_string = expand_macro (def);
    428   if (!execution_string)
    429     return;
    430 
    431   if (def->body)
    432     {
    433       /* Reset the line number to where the macro arguments began.
    434          This makes line numbers reported in error messages correct in
    435          case the macro arguments span several lines and the expanded
    436          arguments invoke other commands.  */
    437       end_line = line_number;
    438       line_number = start_line;
    439 
    440       if (macro_expansion_output_stream
    441           && !executing_string && !me_inhibit_expansion)
    442         {
    443           remember_itext (input_text, input_text_offset);
    444           me_execute_string (execution_string);
    445         }
    446       else
    447         execute_string ("%s", execution_string);
    448 
    449       free (execution_string);
    450       line_number = end_line;
    451     }
    452 }
    453 
    454 
    455 /* Read and remember the definition of a macro.  If RECURSIVE is set,
    457    set the ME_RECURSE flag.  MACTYPE is either "macro" or "rmacro", and
    458    tells us what the matching @end should be.  */
    459 static void
    460 define_macro (char *mactype, int recursive)
    461 {
    462   int i, start;
    463   char *name, *line;
    464   char *last_end = NULL;
    465   char *body = NULL;
    466   char **arglist = NULL;
    467   int body_size = 0, body_index = 0;
    468   int depth = 1;
    469   int flags = 0;
    470   int defining_line = line_number;
    471 
    472   if (macro_expansion_output_stream && !executing_string)
    473     me_append_before_this_command ();
    474 
    475   skip_whitespace ();
    476 
    477   /* Get the name of the macro.  This is the set of characters which are
    478      not whitespace and are not `{' immediately following the @macro. */
    479   start = input_text_offset;
    480   {
    481     int len;
    482 
    483     for (i = start; i < input_text_length && input_text[i] != '{'
    484                     && !cr_or_whitespace (input_text[i]);
    485          i++) ;
    486 
    487     len = i - start;
    488     name = xmalloc (1 + len);
    489     memcpy (name, input_text + start, len);
    490     name[len] = 0;
    491     input_text_offset = i;
    492   }
    493 
    494   skip_whitespace ();
    495 
    496   /* It is not required that the definition of a macro includes an arglist.
    497      If not, don't try to get the named parameters, just use a null list. */
    498   if (curchar () == '{')
    499     {
    500       int character;
    501       int arglist_index = 0, arglist_size = 0;
    502       int gathering_words = 1;
    503       char *word = NULL;
    504 
    505       /* Read the words inside of the braces which determine the arglist.
    506          These words will be replaced within the body of the macro at
    507          execution time. */
    508 
    509       input_text_offset++;
    510       skip_whitespace_and_newlines ();
    511 
    512       while (gathering_words)
    513         {
    514           int len;
    515 
    516           for (i = input_text_offset;
    517                (character = input_text[i]);
    518                i++)
    519             {
    520               switch (character)
    521                 {
    522                 case '\n':
    523                   line_number++;
    524                 case ' ':
    525                 case '\t':
    526                 case ',':
    527                 case '}':
    528                   /* Found the end of the current arglist word.  Save it. */
    529                   len = i - input_text_offset;
    530                   word = xmalloc (1 + len);
    531                   memcpy (word, input_text + input_text_offset, len);
    532                   word[len] = 0;
    533                   input_text_offset = i;
    534 
    535                   /* Advance to the comma or close-brace that signified
    536                      the end of the argument. */
    537                   while ((character = curchar ())
    538                          && character != ','
    539                          && character != '}')
    540                     {
    541                       input_text_offset++;
    542                       if (character == '\n')
    543                         line_number++;
    544                     }
    545 
    546                   /* Add the word to our list of words. */
    547                   if (arglist_index + 2 >= arglist_size)
    548                     {
    549                       arglist_size += 10;
    550                       arglist = xrealloc (arglist,
    551                                           arglist_size * sizeof (char *));
    552                     }
    553 
    554                   arglist[arglist_index++] = word;
    555                   arglist[arglist_index] = NULL;
    556                   break;
    557                 }
    558 
    559               if (character == '}')
    560                 {
    561                   input_text_offset++;
    562                   gathering_words = 0;
    563                   break;
    564                 }
    565 
    566               if (character == ',')
    567                 {
    568                   input_text_offset++;
    569                   skip_whitespace_and_newlines ();
    570                   i = input_text_offset - 1;
    571                 }
    572             }
    573         }
    574 
    575       /* If we have exactly one argument, do @quote-arg implicitly.  Not
    576          only does this match TeX's behavior (which can't feasibly be
    577          changed), but it's a good idea.  */
    578       if (arglist_index == 1)
    579         flags |= ME_QUOTE_ARG;
    580     }
    581 
    582   /* Read the text carefully until we find an "@end macro" which
    583      matches this one.  The text in between is the body of the macro. */
    584   skip_whitespace_and_newlines ();
    585 
    586   while (depth)
    587     {
    588       if ((input_text_offset + 9) > input_text_length)
    589         {
    590           file_line_error (input_filename, defining_line,
    591 			   _("%cend macro not found"), COMMAND_PREFIX);
    592           return;
    593         }
    594 
    595       get_rest_of_line (0, &line);
    596 
    597       /* Handle commands only meaningful within a macro. */
    598       if ((*line == COMMAND_PREFIX) && (depth == 1) &&
    599           (strncmp (line + 1, "allow-recursion", 15) == 0) &&
    600           (line[16] == 0 || whitespace (line[16])))
    601         {
    602           for (i = 16; whitespace (line[i]); i++);
    603           strcpy (line, line + i);
    604           flags |= ME_RECURSE;
    605           if (!*line)
    606             {
    607               free (line);
    608               continue;
    609             }
    610         }
    611 
    612       if ((*line == COMMAND_PREFIX) && (depth == 1) &&
    613           (strncmp (line + 1, "quote-arg", 9) == 0) &&
    614           (line[10] == 0 || whitespace (line[10])))
    615         {
    616           for (i = 10; whitespace (line[i]); i++);
    617           strcpy (line, line + i);
    618 
    619           if (arglist && arglist[0] && !arglist[1])
    620             {
    621               flags |= ME_QUOTE_ARG;
    622               if (!*line)
    623                 {
    624                   free (line);
    625                   continue;
    626                 }
    627             }
    628           else
    629            line_error (_("@quote-arg only useful for single-argument macros"));
    630         }
    631 
    632       if (*line == COMMAND_PREFIX
    633           && (strncmp (line + 1, "macro ", 6) == 0
    634               || strncmp (line + 1, "rmacro ", 7) == 0))
    635         depth++;
    636 
    637       /* Incorrect implementation of nesting -- just check that the last
    638          @end matches what we started with.  Since nested macros don't
    639          work in TeX anyway, this isn't worth the trouble to get right.  */
    640       if (*line == COMMAND_PREFIX && strncmp (line + 1, "end macro", 9) == 0)
    641         {
    642           depth--;
    643           last_end = "macro";
    644         }
    645       if (*line == COMMAND_PREFIX && strncmp (line + 1, "end rmacro", 10) == 0)
    646         {
    647           depth--;
    648           last_end = "rmacro";
    649         }
    650 
    651       if (depth)
    652         {
    653           if ((body_index + strlen (line) + 3) >= body_size)
    654             body = xrealloc (body, body_size += 3 + strlen (line));
    655           strcpy (body + body_index, line);
    656           body_index += strlen (line);
    657           body[body_index++] = '\n';
    658           body[body_index] = 0;
    659         }
    660       free (line);
    661     }
    662 
    663   /* Check that @end matched the macro command.  */
    664   if (!STREQ (last_end, mactype))
    665     warning (_("mismatched @end %s with @%s"), last_end, mactype);
    666 
    667   /* If it was an empty macro like
    668      @macro foo
    669      @end macro
    670      create an empty body.  (Otherwise, the macro is not expanded.)  */
    671   if (!body)
    672     {
    673       body = (char *)malloc(1);
    674       *body = 0;
    675     }
    676 
    677   /* We now have the name, the arglist, and the body.  However, BODY
    678      includes the final newline which preceded the `@end macro' text.
    679      Delete it. */
    680   if (body && strlen (body))
    681     body[strlen (body) - 1] = 0;
    682 
    683   if (recursive)
    684     flags |= ME_RECURSE;
    685 
    686   add_macro (name, arglist, body, input_filename, defining_line, flags);
    687 
    688   if (macro_expansion_output_stream && !executing_string)
    689     {
    690       /* Remember text for future expansions.  */
    691       remember_itext (input_text, input_text_offset);
    692 
    693       /* Bizarrely, output the @macro itself.  This is so texinfo.tex
    694          will have a chance to read it when texi2dvi calls makeinfo -E.
    695          The problem is that we don't really expand macros in all
    696          contexts; a @table's @item is one.  And a fix is not obvious to
    697          me, since it appears virtually identical to any other internal
    698          expansion.  Just setting a variable in cm_item caused other
    699          strange expansion problems.  */
    700       write_region_to_macro_output ("@", 0, 1);
    701       write_region_to_macro_output (mactype, 0, strlen (mactype));
    702       write_region_to_macro_output (" ", 0, 1);
    703       write_region_to_macro_output (input_text, start, input_text_offset);
    704     }
    705 }
    706 
    707 void
    708 cm_macro (int arg, int arg2, int arg3)
    709 {
    710   define_macro ("macro", 0);
    711 }
    712 
    713 void
    714 cm_rmacro (int arg, int arg2, int arg3)
    715 {
    716   define_macro ("rmacro", 1);
    717 }
    718 
    719 /* Delete the macro with name NAME.  The macro is deleted from the list,
    721    but it is also returned.  If there was no macro defined, NULL is
    722    returned. */
    723 
    724 static MACRO_DEF *
    725 delete_macro (char *name)
    726 {
    727   int i;
    728   MACRO_DEF *def;
    729 
    730   def = NULL;
    731 
    732   for (i = 0; macro_list && (def = macro_list[i]); i++)
    733     if (strcmp (def->name, name) == 0)
    734       {
    735         memmove (macro_list + i, macro_list + i + 1,
    736                ((macro_list_len + 1) - i) * sizeof (MACRO_DEF *));
    737         macro_list_len--;
    738         break;
    739       }
    740   return def;
    741 }
    742 
    743 void
    744 cm_unmacro (int arg, int arg2, int arg3)
    745 {
    746   int i;
    747   char *line, *name;
    748   MACRO_DEF *def;
    749 
    750   if (macro_expansion_output_stream && !executing_string)
    751     me_append_before_this_command ();
    752 
    753   get_rest_of_line (0, &line);
    754 
    755   for (i = 0; line[i] && !whitespace (line[i]); i++);
    756   name = xmalloc (i + 1);
    757   memcpy (name, line, i);
    758   name[i] = 0;
    759 
    760   def = delete_macro (name);
    761 
    762   if (def)
    763     {
    764       free (def->source_file);
    765       free (def->name);
    766       free (def->body);
    767 
    768       if (def->arglist)
    769         {
    770           int i;
    771 
    772           for (i = 0; def->arglist[i]; i++)
    773             free (def->arglist[i]);
    774 
    775           free (def->arglist);
    776         }
    777 
    778       free (def);
    779     }
    780 
    781   free (line);
    782   free (name);
    783 
    784   if (macro_expansion_output_stream && !executing_string)
    785     remember_itext (input_text, input_text_offset);
    786 }
    787 
    788 /* How to output sections of the input file verbatim. */
    790 
    791 /* Set the value of POINTER's offset to OFFSET. */
    792 ITEXT *
    793 remember_itext (char *pointer, int offset)
    794 {
    795   int i;
    796   ITEXT *itext = NULL;
    797 
    798   /* If we have no info, initialize a blank list. */
    799   if (!itext_info)
    800     {
    801       itext_info = xmalloc ((itext_size = 10) * sizeof (ITEXT *));
    802       for (i = 0; i < itext_size; i++)
    803         itext_info[i] = NULL;
    804     }
    805 
    806   /* If the pointer is already present in the list, then set the offset. */
    807   for (i = 0; i < itext_size; i++)
    808     if ((itext_info[i]) &&
    809         (itext_info[i]->pointer == pointer))
    810       {
    811         itext = itext_info[i];
    812         itext_info[i]->offset = offset;
    813         break;
    814       }
    815 
    816   if (i == itext_size)
    817     {
    818       /* Find a blank slot (or create a new one), and remember the
    819          pointer and offset. */
    820       for (i = 0; i < itext_size; i++)
    821         if (itext_info[i] == NULL)
    822           break;
    823 
    824       /* If not found, then add some slots. */
    825       if (i == itext_size)
    826         {
    827           int j;
    828 
    829           itext_info = xrealloc
    830             (itext_info, (itext_size += 10) * sizeof (ITEXT *));
    831 
    832           for (j = i; j < itext_size; j++)
    833             itext_info[j] = NULL;
    834         }
    835 
    836       /* Now add the pointer and the offset. */
    837       itext_info[i] = xmalloc (sizeof (ITEXT));
    838       itext_info[i]->pointer = pointer;
    839       itext_info[i]->offset = offset;
    840       itext = itext_info[i];
    841     }
    842   return itext;
    843 }
    844 
    845 /* Forget the input text associated with POINTER. */
    846 void
    847 forget_itext (char *pointer)
    848 {
    849   int i;
    850 
    851   for (i = 0; i < itext_size; i++)
    852     if (itext_info[i] && (itext_info[i]->pointer == pointer))
    853       {
    854         free (itext_info[i]);
    855         itext_info[i] = NULL;
    856         break;
    857       }
    858 }
    859 
    860 /* Append the text which appeared in input_text from the last offset to
    861    the character just before the command that we are currently executing. */
    862 void
    863 me_append_before_this_command (void)
    864 {
    865   int i;
    866 
    867   for (i = input_text_offset; i && (input_text[i] != COMMAND_PREFIX); i--)
    868     ;
    869   maybe_write_itext (input_text, i);
    870 }
    871 
    872 /* Similar to execute_string, but only takes a single string argument,
    873    and remembers the input text location, etc. */
    874 void
    875 me_execute_string (char *execution_string)
    876 {
    877   int saved_escape_html = escape_html;
    878   int saved_in_paragraph = in_paragraph;
    879   escape_html = me_executing_string == 0;
    880   in_paragraph = 0;
    881 
    882   pushfile ();
    883   input_text_offset = 0;
    884   /* The following xstrdup is so we can relocate input_text at will.  */
    885   input_text = xstrdup (execution_string);
    886   input_filename = xstrdup (input_filename);
    887   input_text_length = strlen (execution_string);
    888 
    889   remember_itext (input_text, 0);
    890 
    891   me_executing_string++;
    892   reader_loop ();
    893   free (input_text);
    894   free (input_filename);
    895   popfile ();
    896   me_executing_string--;
    897 
    898   in_paragraph = saved_in_paragraph;
    899   escape_html = saved_escape_html;
    900 }
    901 
    902 /* A wrapper around me_execute_string which saves and restores
    903    variables important for output generation.  This is called
    904    when we need to produce macro-expanded output for input which
    905    leaves no traces in the Info output.  */
    906 void
    907 me_execute_string_keep_state (char *execution_string, char *append_string)
    908 {
    909   int op_orig, opcol_orig, popen_orig;
    910   int fill_orig, newline_orig, indent_orig, meta_pos_orig;
    911 
    912   remember_itext (input_text, input_text_offset);
    913   op_orig = output_paragraph_offset;
    914   meta_pos_orig = meta_char_pos;
    915   opcol_orig = output_column;
    916   popen_orig = paragraph_is_open;
    917   fill_orig = filling_enabled;
    918   newline_orig = last_char_was_newline;
    919   filling_enabled = 0;
    920   indent_orig = no_indent;
    921   no_indent = 1;
    922   me_execute_string (execution_string);
    923   if (append_string)
    924     write_region_to_macro_output (append_string, 0, strlen (append_string));
    925   output_paragraph_offset = op_orig;
    926   meta_char_pos = meta_pos_orig;
    927   output_column = opcol_orig;
    928   paragraph_is_open = popen_orig;
    929   filling_enabled = fill_orig;
    930   last_char_was_newline = newline_orig;
    931   no_indent = indent_orig;
    932 }
    933 
    934 /* Append the text which appears in input_text from the last offset to
    935    the current OFFSET. */
    936 void
    937 append_to_expansion_output (int offset)
    938 {
    939   int i;
    940   ITEXT *itext = NULL;
    941 
    942   for (i = 0; i < itext_size; i++)
    943     if (itext_info[i] && itext_info[i]->pointer == input_text)
    944       {
    945         itext = itext_info[i];
    946         break;
    947       }
    948 
    949   if (!itext)
    950     return;
    951 
    952   if (offset > itext->offset)
    953     {
    954       write_region_to_macro_output (input_text, itext->offset, offset);
    955       remember_itext (input_text, offset);
    956     }
    957 }
    958 
    959 /* Only write this input text iff it appears in our itext list. */
    960 void
    961 maybe_write_itext (char *pointer, int offset)
    962 {
    963   int i;
    964   ITEXT *itext = NULL;
    965 
    966   for (i = 0; i < itext_size; i++)
    967     if (itext_info[i] && (itext_info[i]->pointer == pointer))
    968       {
    969         itext = itext_info[i];
    970         break;
    971       }
    972 
    973   if (itext && (itext->offset < offset))
    974     {
    975       write_region_to_macro_output (itext->pointer, itext->offset, offset);
    976       remember_itext (pointer, offset);
    977     }
    978 }
    979 
    980 void
    981 write_region_to_macro_output (char *string, int start, int end)
    982 {
    983   if (macro_expansion_output_stream)
    984     fwrite (string + start, 1, end - start, macro_expansion_output_stream);
    985 }
    986 
    987 /* Aliases. */
    989 
    990 typedef struct alias_struct
    991 {
    992   char *alias;
    993   char *mapto;
    994   struct alias_struct *next;
    995 } alias_type;
    996 
    997 static alias_type *aliases;
    998 
    999 /* @alias aname = cmdname */
   1000 
   1001 void
   1002 cm_alias (int arg, int arg2, int arg3)
   1003 {
   1004   alias_type *a = xmalloc (sizeof (alias_type));
   1005 
   1006   skip_whitespace ();
   1007   get_until_in_line (0, "=", &(a->alias));
   1008   canon_white (a->alias);
   1009 
   1010   discard_until ("=");
   1011   skip_whitespace ();
   1012   get_until_in_line (0, " ", &(a->mapto));
   1013 
   1014   a->next = aliases;
   1015   aliases = a;
   1016 }
   1017 
   1018 /* Perform an alias expansion.  Called from read_command.  */
   1019 char *
   1020 alias_expand (char *tok)
   1021 {
   1022   alias_type *findit = aliases;
   1023 
   1024   while (findit)
   1025     if (strcmp (findit->alias, tok) == 0)
   1026       {
   1027 	free (tok);
   1028 	return alias_expand (xstrdup (findit->mapto));
   1029       }
   1030     else
   1031       findit = findit->next;
   1032 
   1033   return tok;
   1034 }
   1035 
   1036 /* definfoenclose implementation.  */
   1038 
   1039 /* This structure is used to track enclosure macros.  When an enclosure
   1040    macro is recognized, a pointer to the enclosure block corresponding
   1041    to its name is saved in the brace element for its argument. */
   1042 typedef struct enclose_struct
   1043 {
   1044   char *enclose;
   1045   char *before;
   1046   char *after;
   1047   struct enclose_struct *next;
   1048 } enclosure_type;
   1049 
   1050 static enclosure_type *enclosures;
   1051 
   1052 typedef struct enclosure_stack_struct
   1053 {
   1054     enclosure_type *current;
   1055     struct enclosure_stack_struct *next;
   1056 } enclosure_stack_type;
   1057 
   1058 static enclosure_stack_type *enclosure_stack;
   1059 
   1060 /* @definfoenclose */
   1061 void
   1062 cm_definfoenclose (int arg, int arg2, int arg3)
   1063 {
   1064   enclosure_type *e = xmalloc (sizeof (enclosure_type));
   1065 
   1066   skip_whitespace ();
   1067   get_until_in_line (1, ",", &(e->enclose));
   1068   discard_until (",");
   1069   get_until_in_line (0, ",", &(e->before));
   1070   discard_until (",");
   1071   get_until_in_line (0, "\n", &(e->after));
   1072 
   1073   e->next = enclosures;
   1074   enclosures = e;
   1075 }
   1076 
   1077 /* If TOK is an enclosure command, push it on the enclosure stack and
   1078    return 1.  Else return 0.  */
   1079 
   1080 int
   1081 enclosure_command (char *tok)
   1082 {
   1083   enclosure_type *findit = enclosures;
   1084 
   1085   while (findit)
   1086     if (strcmp (findit->enclose, tok) == 0)
   1087       {
   1088         enclosure_stack_type *new = xmalloc (sizeof (enclosure_stack_type));
   1089         new->current = findit;
   1090         new->next = enclosure_stack;
   1091         enclosure_stack = new;
   1092 
   1093         return 1;
   1094       }
   1095     else
   1096       findit = findit->next;
   1097 
   1098   return 0;
   1099 }
   1100 
   1101 /* actually perform the enclosure expansion */
   1102 void
   1103 enclosure_expand (int arg, int start, int end)
   1104 {
   1105   if (arg == START)
   1106     add_word (enclosure_stack->current->before);
   1107   else
   1108     {
   1109       enclosure_stack_type *temp;
   1110 
   1111       add_word (enclosure_stack->current->after);
   1112 
   1113       temp = enclosure_stack;
   1114       enclosure_stack = enclosure_stack->next;
   1115       free (temp);
   1116     }
   1117 }
   1118