Back to index

tetex-bin  3.0
macro.c
Go to the documentation of this file.
00001 /* macro.c -- user-defined macros for Texinfo.
00002    $Id: macro.c,v 1.6 2004/04/11 17:56:47 karl Exp $
00003 
00004    Copyright (C) 1998, 1999, 2002, 2003 Free Software Foundation, Inc.
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2, or (at your option)
00009    any later version.
00010 
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014    GNU General Public License for more details.
00015 
00016    You should have received a copy of the GNU General Public License
00017    along with this program; if not, write to the Free Software Foundation,
00018    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
00019 
00020 #include "system.h"
00021 #include "cmds.h"
00022 #include "files.h"
00023 #include "macro.h"
00024 #include "makeinfo.h"
00025 #include "insertion.h"
00026 
00027 /* If non-NULL, this is an output stream to write the full macro expansion
00028    of the input text to.  The result is another texinfo file, but
00029    missing @include, @infoinclude, @macro, and macro invocations.  Instead,
00030    all of the text is placed within the file. */
00031 FILE *macro_expansion_output_stream = NULL;
00032 
00033 /* Output file for -E.  */
00034 char *macro_expansion_filename;
00035 
00036 /* Nonzero means a macro string is in execution, as opposed to a file. */
00037 int me_executing_string = 0;
00038 
00039 /* Nonzero means we want only to expand macros and
00040    leave everything else intact.  */
00041 int only_macro_expansion = 0;
00042 
00043 static ITEXT **itext_info = NULL;
00044 static int itext_size = 0;
00045 
00046 /* Return the arglist on the current line.  This can behave in two different
00047    ways, depending on the variable BRACES_REQUIRED_FOR_MACRO_ARGS. */
00048 int braces_required_for_macro_args = 0;
00049 
00050 /* Array of macros and definitions. */
00051 MACRO_DEF **macro_list = NULL;
00052 
00053 int macro_list_len = 0;         /* Number of elements. */
00054 int macro_list_size = 0;        /* Number of slots in total. */
00055 
00056 /* Return the length of the array in ARRAY. */
00057 int
00058 array_len (char **array)
00059 {
00060   int i = 0;
00061 
00062   if (array)
00063     for (i = 0; array[i]; i++);
00064 
00065   return i;
00066 }
00067 
00068 void
00069 free_array (char **array)
00070 {
00071   if (array)
00072     {
00073       int i;
00074       for (i = 0; array[i]; i++)
00075         free (array[i]);
00076 
00077       free (array);
00078     }
00079 }
00080 
00081 /* Return the macro definition of NAME or NULL if NAME is not defined. */
00082 MACRO_DEF *
00083 find_macro (char *name)
00084 {
00085   int i;
00086   MACRO_DEF *def;
00087 
00088   def = NULL;
00089   for (i = 0; macro_list && (def = macro_list[i]); i++)
00090     {
00091       if ((!def->inhibited) && (strcmp (def->name, name) == 0))
00092         break;
00093     }
00094   return def;
00095 }
00096 
00097 /* Add the macro NAME with ARGLIST and BODY to the list of defined macros.
00098    SOURCE_FILE is the name of the file where this definition can be found,
00099    and SOURCE_LINENO is the line number within that file.  If a macro already
00100    exists with NAME, then a warning is produced, and that previous
00101    definition is overwritten. */
00102 static void
00103 add_macro (char *name, char **arglist, char *body, char *source_file,
00104     int source_lineno, int flags)
00105 {
00106   MACRO_DEF *def;
00107 
00108   def = find_macro (name);
00109 
00110   if (!def)
00111     {
00112       if (macro_list_len + 2 >= macro_list_size)
00113         macro_list = xrealloc
00114           (macro_list, ((macro_list_size += 10) * sizeof (MACRO_DEF *)));
00115 
00116       macro_list[macro_list_len] = xmalloc (sizeof (MACRO_DEF));
00117       macro_list[macro_list_len + 1] = NULL;
00118 
00119       def = macro_list[macro_list_len];
00120       macro_list_len += 1;
00121       def->name = name;
00122     }
00123   else
00124     {
00125       char *temp_filename = input_filename;
00126       int temp_line = line_number;
00127 
00128       warning (_("macro `%s' previously defined"), name);
00129 
00130       input_filename = def->source_file;
00131       line_number = def->source_lineno;
00132       warning (_("here is the previous definition of `%s'"), name);
00133 
00134       input_filename = temp_filename;
00135       line_number = temp_line;
00136 
00137       if (def->arglist)
00138         {
00139           int i;
00140 
00141           for (i = 0; def->arglist[i]; i++)
00142             free (def->arglist[i]);
00143 
00144           free (def->arglist);
00145         }
00146       free (def->source_file);
00147       free (def->body);
00148     }
00149 
00150   def->source_file = xstrdup (source_file);
00151   def->source_lineno = source_lineno;
00152   def->body = body;
00153   def->arglist = arglist;
00154   def->inhibited = 0;
00155   def->flags = flags;
00156 }
00157 
00158 
00159 char **
00160 get_brace_args (int quote_single)
00161 {
00162   char **arglist, *word;
00163   int arglist_index, arglist_size;
00164   int character, escape_seen, start;
00165   int depth = 1;
00166 
00167   /* There is an arglist in braces here, so gather the args inside of it. */
00168   skip_whitespace_and_newlines ();
00169   input_text_offset++;
00170   arglist = NULL;
00171   arglist_index = arglist_size = 0;
00172 
00173  get_arg:
00174   skip_whitespace_and_newlines ();
00175   start = input_text_offset;
00176   escape_seen = 0;
00177 
00178   while ((character = curchar ()))
00179     {
00180       if (character == '\\')
00181         {
00182           input_text_offset += 2;
00183           escape_seen = 1;
00184         }
00185       else if (character == '{')
00186         {
00187           depth++;
00188           input_text_offset++;
00189         }
00190       else if ((character == ',' && !quote_single) ||
00191                ((character == '}') && depth == 1))
00192         {
00193           int len = input_text_offset - start;
00194 
00195           if (len || (character != '}'))
00196             {
00197               word = xmalloc (1 + len);
00198               memcpy (word, input_text + start, len);
00199               word[len] = 0;
00200 
00201               /* Clean up escaped characters. */
00202               if (escape_seen)
00203                 {
00204                   int i;
00205                   for (i = 0; word[i]; i++)
00206                     if (word[i] == '\\')
00207                       memmove (word + i, word + i + 1,
00208                                1 + strlen (word + i + 1));
00209                 }
00210 
00211               if (arglist_index + 2 >= arglist_size)
00212                 arglist = xrealloc
00213                   (arglist, (arglist_size += 10) * sizeof (char *));
00214 
00215               arglist[arglist_index++] = word;
00216               arglist[arglist_index] = NULL;
00217             }
00218 
00219           input_text_offset++;
00220           if (character == '}')
00221             break;
00222           else
00223             goto get_arg;
00224         }
00225       else if (character == '}')
00226         {
00227           depth--;
00228           input_text_offset++;
00229         }
00230       else
00231         {
00232           input_text_offset++;
00233           if (character == '\n') line_number++;
00234         }
00235     }
00236   return arglist;
00237 }
00238 
00239 static char **
00240 get_macro_args (MACRO_DEF *def)
00241 {
00242   int i;
00243   char *word;
00244 
00245   /* Quickly check to see if this macro has been invoked with any arguments.
00246      If not, then don't skip any of the following whitespace. */
00247   for (i = input_text_offset; i < input_text_length; i++)
00248     if (!cr_or_whitespace (input_text[i]))
00249       break;
00250 
00251   if (input_text[i] != '{')
00252     {
00253       if (braces_required_for_macro_args)
00254         {
00255           return NULL;
00256         }
00257       else
00258         {
00259           /* Braces are not required to fill out the macro arguments.  If
00260              this macro takes one argument, it is considered to be the
00261              remainder of the line, sans whitespace. */
00262           if (def->arglist && def->arglist[0] && !def->arglist[1])
00263             {
00264               char **arglist;
00265 
00266               get_rest_of_line (0, &word);
00267               if (input_text[input_text_offset - 1] == '\n')
00268                 {
00269                   input_text_offset--;
00270                   line_number--;
00271                 }
00272               /* canon_white (word); */
00273               arglist = xmalloc (2 * sizeof (char *));
00274               arglist[0] = word;
00275               arglist[1] = NULL;
00276               return arglist;
00277             }
00278           else
00279             {
00280               /* The macro either took no arguments, or took more than
00281                  one argument.  In that case, it must be invoked with
00282                  arguments surrounded by braces. */
00283               return NULL;
00284             }
00285         }
00286     }
00287   return get_brace_args (def->flags & ME_QUOTE_ARG);
00288 }
00289 
00290 /* Substitute actual parameters for named parameters in body.
00291    The named parameters which appear in BODY must by surrounded
00292    reverse slashes, as in \foo\. */
00293 static char *
00294 apply (char **named, char **actuals, char *body)
00295 {
00296   int i;
00297   int new_body_index, new_body_size;
00298   char *new_body, *text;
00299   int length_of_actuals;
00300 
00301   length_of_actuals = array_len (actuals);
00302   new_body_size = strlen (body);
00303   new_body = xmalloc (1 + new_body_size);
00304 
00305   /* Copy chars from BODY into NEW_BODY. */
00306   i = 0;
00307   new_body_index = 0;
00308 
00309   while (body[i])
00310     { /* Anything but a \ is easy.  */
00311       if (body[i] != '\\')
00312         new_body[new_body_index++] = body[i++];
00313       else
00314         { /* Snarf parameter name, check against named parameters. */
00315           char *param;
00316           int param_start, len;
00317 
00318           param_start = ++i;
00319           while (body[i] && body[i] != '\\')
00320             i++;
00321 
00322           len = i - param_start;
00323           param = xmalloc (1 + len);
00324           memcpy (param, body + param_start, len);
00325           param[len] = 0;
00326 
00327           if (body[i]) /* move past \ */
00328             i++;
00329 
00330           if (len == 0)
00331             { /* \\ always means \, even if macro has no args.  */
00332               len++;
00333               text = xmalloc (1 + len);
00334               sprintf (text, "\\%s", param);
00335             }
00336           else
00337             {
00338               int which;
00339               
00340               /* Check against named parameters. */
00341               for (which = 0; named && named[which]; which++)
00342                 if (STREQ (named[which], param))
00343                   break;
00344 
00345               if (named && named[which])
00346                 {
00347                   text = which < length_of_actuals ? actuals[which] : NULL;
00348                   if (!text)
00349                     text = "";
00350                   len = strlen (text);
00351                   text = xstrdup (text);  /* so we can free it */
00352                 }
00353               else
00354                 { /* not a parameter, so it's an error.  */
00355                   warning (_("\\ in macro expansion followed by `%s' instead of parameter name"),
00356                              param); 
00357                   len++;
00358                   text = xmalloc (1 + len);
00359                   sprintf (text, "\\%s", param);
00360                 }
00361             }
00362 
00363           if (strlen (param) + 2 < len)
00364             {
00365               new_body_size += len + 1;
00366               new_body = xrealloc (new_body, new_body_size);
00367             }
00368 
00369           free (param);
00370 
00371           strcpy (new_body + new_body_index, text);
00372           new_body_index += len;
00373 
00374           free (text);
00375         }
00376     }
00377 
00378   new_body[new_body_index] = 0;
00379   return new_body;
00380 }
00381 
00382 /* Expand macro passed in DEF, a pointer to a MACRO_DEF, and
00383    return its expansion as a string.  */
00384 char *
00385 expand_macro (MACRO_DEF *def)
00386 {
00387   char **arglist;
00388   int num_args;
00389   char *execution_string = NULL;
00390   int start_line = line_number;
00391 
00392   /* Find out how many arguments this macro definition takes. */
00393   num_args = array_len (def->arglist);
00394 
00395   /* Gather the arguments present on the line if there are any. */
00396   arglist = get_macro_args (def);
00397 
00398   if (num_args < array_len (arglist))
00399     {
00400       free_array (arglist);
00401       line_error (_("Macro `%s' called on line %d with too many args"),
00402                   def->name, start_line);
00403       return execution_string;
00404     }
00405 
00406   if (def->body)
00407     execution_string = apply (def->arglist, arglist, def->body);
00408 
00409   free_array (arglist);
00410   return execution_string;
00411 }
00412 
00413 /* Execute the macro passed in DEF, a pointer to a MACRO_DEF.  */
00414 void
00415 execute_macro (MACRO_DEF *def)
00416 {
00417   char *execution_string;
00418   int start_line = line_number, end_line;
00419 
00420   if (macro_expansion_output_stream && !executing_string && !me_inhibit_expansion)
00421     me_append_before_this_command ();
00422 
00423   execution_string = expand_macro (def);
00424   if (!execution_string)
00425     return;
00426 
00427   if (def->body)
00428     {
00429       /* Reset the line number to where the macro arguments began.
00430          This makes line numbers reported in error messages correct in
00431          case the macro arguments span several lines and the expanded
00432          arguments invoke other commands.  */
00433       end_line = line_number;
00434       line_number = start_line;
00435 
00436       if (macro_expansion_output_stream
00437           && !executing_string && !me_inhibit_expansion)
00438         {
00439           remember_itext (input_text, input_text_offset);
00440           me_execute_string (execution_string);
00441         }
00442       else
00443         execute_string ("%s", execution_string);
00444 
00445       free (execution_string);
00446       line_number = end_line;
00447     }
00448 }
00449 
00450 
00451 /* Read and remember the definition of a macro.  If RECURSIVE is set,
00452    set the ME_RECURSE flag.  MACTYPE is either "macro" or "rmacro", and
00453    tells us what the matching @end should be.  */
00454 static void
00455 define_macro (char *mactype, int recursive)
00456 {
00457   int i, start;
00458   char *name, *line;
00459   char *last_end = NULL;
00460   char *body = NULL;
00461   char **arglist = NULL;
00462   int body_size = 0, body_index = 0;
00463   int depth = 1;
00464   int flags = 0;
00465   int defining_line = line_number;
00466 
00467   if (macro_expansion_output_stream && !executing_string)
00468     me_append_before_this_command ();
00469 
00470   skip_whitespace ();
00471 
00472   /* Get the name of the macro.  This is the set of characters which are
00473      not whitespace and are not `{' immediately following the @macro. */
00474   start = input_text_offset;
00475   {
00476     int len;
00477 
00478     for (i = start; i < input_text_length && input_text[i] != '{'
00479                     && !cr_or_whitespace (input_text[i]);
00480          i++) ;
00481 
00482     len = i - start;
00483     name = xmalloc (1 + len);
00484     memcpy (name, input_text + start, len);
00485     name[len] = 0;
00486     input_text_offset = i;
00487   }
00488 
00489   skip_whitespace ();
00490 
00491   /* It is not required that the definition of a macro includes an arglist.
00492      If not, don't try to get the named parameters, just use a null list. */
00493   if (curchar () == '{')
00494     {
00495       int character;
00496       int arglist_index = 0, arglist_size = 0;
00497       int gathering_words = 1;
00498       char *word = NULL;
00499 
00500       /* Read the words inside of the braces which determine the arglist.
00501          These words will be replaced within the body of the macro at
00502          execution time. */
00503 
00504       input_text_offset++;
00505       skip_whitespace_and_newlines ();
00506 
00507       while (gathering_words)
00508         {
00509           int len;
00510 
00511           for (i = input_text_offset;
00512                (character = input_text[i]);
00513                i++)
00514             {
00515               switch (character)
00516                 {
00517                 case '\n':
00518                   line_number++;
00519                 case ' ':
00520                 case '\t':
00521                 case ',':
00522                 case '}':
00523                   /* Found the end of the current arglist word.  Save it. */
00524                   len = i - input_text_offset;
00525                   word = xmalloc (1 + len);
00526                   memcpy (word, input_text + input_text_offset, len);
00527                   word[len] = 0;
00528                   input_text_offset = i;
00529 
00530                   /* Advance to the comma or close-brace that signified
00531                      the end of the argument. */
00532                   while ((character = curchar ())
00533                          && character != ','
00534                          && character != '}')
00535                     {
00536                       input_text_offset++;
00537                       if (character == '\n')
00538                         line_number++;
00539                     }
00540 
00541                   /* Add the word to our list of words. */
00542                   if (arglist_index + 2 >= arglist_size)
00543                     {
00544                       arglist_size += 10;
00545                       arglist = xrealloc (arglist,
00546                                           arglist_size * sizeof (char *));
00547                     }
00548 
00549                   arglist[arglist_index++] = word;
00550                   arglist[arglist_index] = NULL;
00551                   break;
00552                 }
00553 
00554               if (character == '}')
00555                 {
00556                   input_text_offset++;
00557                   gathering_words = 0;
00558                   break;
00559                 }
00560 
00561               if (character == ',')
00562                 {
00563                   input_text_offset++;
00564                   skip_whitespace_and_newlines ();
00565                   i = input_text_offset - 1;
00566                 }
00567             }
00568         }
00569       
00570       /* If we have exactly one argument, do @quote-arg implicitly.  Not
00571          only does this match TeX's behavior (which can't feasibly be
00572          changed), but it's a good idea.  */
00573       if (arglist_index == 1)
00574         flags |= ME_QUOTE_ARG;
00575     }
00576 
00577   /* Read the text carefully until we find an "@end macro" which
00578      matches this one.  The text in between is the body of the macro. */
00579   skip_whitespace_and_newlines ();
00580 
00581   while (depth)
00582     {
00583       if ((input_text_offset + 9) > input_text_length)
00584         {
00585           file_line_error (input_filename, defining_line,
00586                         _("%cend macro not found"), COMMAND_PREFIX);
00587           return;
00588         }
00589 
00590       get_rest_of_line (0, &line);
00591 
00592       /* Handle commands only meaningful within a macro. */
00593       if ((*line == COMMAND_PREFIX) && (depth == 1) &&
00594           (strncmp (line + 1, "allow-recursion", 15) == 0) &&
00595           (line[16] == 0 || whitespace (line[16])))
00596         {
00597           for (i = 16; whitespace (line[i]); i++);
00598           strcpy (line, line + i);
00599           flags |= ME_RECURSE;
00600           if (!*line)
00601             {
00602               free (line);
00603               continue;
00604             }
00605         }
00606 
00607       if ((*line == COMMAND_PREFIX) && (depth == 1) &&
00608           (strncmp (line + 1, "quote-arg", 9) == 0) &&
00609           (line[10] == 0 || whitespace (line[10])))
00610         {
00611           for (i = 10; whitespace (line[i]); i++);
00612           strcpy (line, line + i);
00613 
00614           if (arglist && arglist[0] && !arglist[1])
00615             {
00616               flags |= ME_QUOTE_ARG;
00617               if (!*line)
00618                 {
00619                   free (line);
00620                   continue;
00621                 }
00622             }
00623           else
00624            line_error (_("@quote-arg only useful for single-argument macros"));
00625         }
00626 
00627       if (*line == COMMAND_PREFIX
00628           && (strncmp (line + 1, "macro ", 6) == 0
00629               || strncmp (line + 1, "rmacro ", 7) == 0))
00630         depth++;
00631 
00632       /* Incorrect implementation of nesting -- just check that the last
00633          @end matches what we started with.  Since nested macros don't
00634          work in TeX anyway, this isn't worth the trouble to get right.  */
00635       if (*line == COMMAND_PREFIX && strncmp (line + 1, "end macro", 9) == 0)
00636         {
00637           depth--;
00638           last_end = "macro";
00639         }
00640       if (*line == COMMAND_PREFIX && strncmp (line + 1, "end rmacro", 10) == 0)
00641         {
00642           depth--;
00643           last_end = "rmacro";
00644         }
00645 
00646       if (depth)
00647         {
00648           if ((body_index + strlen (line) + 3) >= body_size)
00649             body = xrealloc (body, body_size += 3 + strlen (line));
00650           strcpy (body + body_index, line);
00651           body_index += strlen (line);
00652           body[body_index++] = '\n';
00653           body[body_index] = 0;
00654         }
00655       free (line);
00656     }
00657 
00658   /* Check that @end matched the macro command.  */
00659   if (!STREQ (last_end, mactype))
00660     warning (_("mismatched @end %s with @%s"), last_end, mactype);
00661 
00662   /* If it was an empty macro like
00663      @macro foo
00664      @end macro
00665      create an empty body.  (Otherwise, the macro is not expanded.)  */
00666   if (!body)
00667     {
00668       body = (char *)malloc(1);
00669       *body = 0;
00670     }
00671 
00672   /* We now have the name, the arglist, and the body.  However, BODY
00673      includes the final newline which preceded the `@end macro' text.
00674      Delete it. */
00675   if (body && strlen (body))
00676     body[strlen (body) - 1] = 0;
00677 
00678   if (recursive)
00679     flags |= ME_RECURSE;
00680     
00681   add_macro (name, arglist, body, input_filename, defining_line, flags);
00682 
00683   if (macro_expansion_output_stream && !executing_string)
00684     {
00685       /* Remember text for future expansions.  */
00686       remember_itext (input_text, input_text_offset);
00687 
00688       /* Bizarrely, output the @macro itself.  This is so texinfo.tex
00689          will have a chance to read it when texi2dvi calls makeinfo -E.
00690          The problem is that we don't really expand macros in all
00691          contexts; a @table's @item is one.  And a fix is not obvious to
00692          me, since it appears virtually identical to any other internal
00693          expansion.  Just setting a variable in cm_item caused other
00694          strange expansion problems.  */
00695       write_region_to_macro_output ("@", 0, 1);
00696       write_region_to_macro_output (mactype, 0, strlen (mactype));
00697       write_region_to_macro_output (" ", 0, 1);
00698       write_region_to_macro_output (input_text, start, input_text_offset);
00699     }
00700 }
00701 
00702 void 
00703 cm_macro (void)
00704 {
00705   define_macro ("macro", 0);
00706 }
00707 
00708 void 
00709 cm_rmacro (void)
00710 {
00711   define_macro ("rmacro", 1);
00712 }
00713 
00714 /* Delete the macro with name NAME.  The macro is deleted from the list,
00715    but it is also returned.  If there was no macro defined, NULL is
00716    returned. */
00717 
00718 static MACRO_DEF *
00719 delete_macro (char *name)
00720 {
00721   int i;
00722   MACRO_DEF *def;
00723 
00724   def = NULL;
00725 
00726   for (i = 0; macro_list && (def = macro_list[i]); i++)
00727     if (strcmp (def->name, name) == 0)
00728       {
00729         memmove (macro_list + i, macro_list + i + 1,
00730                ((macro_list_len + 1) - i) * sizeof (MACRO_DEF *));
00731         macro_list_len--;
00732         break;
00733       }
00734   return def;
00735 }
00736 
00737 void
00738 cm_unmacro (void)
00739 {
00740   int i;
00741   char *line, *name;
00742   MACRO_DEF *def;
00743 
00744   if (macro_expansion_output_stream && !executing_string)
00745     me_append_before_this_command ();
00746 
00747   get_rest_of_line (0, &line);
00748 
00749   for (i = 0; line[i] && !whitespace (line[i]); i++);
00750   name = xmalloc (i + 1);
00751   memcpy (name, line, i);
00752   name[i] = 0;
00753 
00754   def = delete_macro (name);
00755 
00756   if (def)
00757     {
00758       free (def->source_file);
00759       free (def->name);
00760       free (def->body);
00761 
00762       if (def->arglist)
00763         {
00764           int i;
00765 
00766           for (i = 0; def->arglist[i]; i++)
00767             free (def->arglist[i]);
00768 
00769           free (def->arglist);
00770         }
00771 
00772       free (def);
00773     }
00774 
00775   free (line);
00776   free (name);
00777 
00778   if (macro_expansion_output_stream && !executing_string)
00779     remember_itext (input_text, input_text_offset);
00780 }
00781 
00782 /* How to output sections of the input file verbatim. */
00783 
00784 /* Set the value of POINTER's offset to OFFSET. */
00785 ITEXT *
00786 remember_itext (char *pointer, int offset)
00787 {
00788   int i;
00789   ITEXT *itext = NULL;
00790 
00791   /* If we have no info, initialize a blank list. */
00792   if (!itext_info)
00793     {
00794       itext_info = xmalloc ((itext_size = 10) * sizeof (ITEXT *));
00795       for (i = 0; i < itext_size; i++)
00796         itext_info[i] = NULL;
00797     }
00798 
00799   /* If the pointer is already present in the list, then set the offset. */
00800   for (i = 0; i < itext_size; i++)
00801     if ((itext_info[i]) &&
00802         (itext_info[i]->pointer == pointer))
00803       {
00804         itext = itext_info[i];
00805         itext_info[i]->offset = offset;
00806         break;
00807       }
00808 
00809   if (i == itext_size)
00810     {
00811       /* Find a blank slot (or create a new one), and remember the
00812          pointer and offset. */
00813       for (i = 0; i < itext_size; i++)
00814         if (itext_info[i] == NULL)
00815           break;
00816 
00817       /* If not found, then add some slots. */
00818       if (i == itext_size)
00819         {
00820           int j;
00821 
00822           itext_info = xrealloc
00823             (itext_info, (itext_size += 10) * sizeof (ITEXT *));
00824 
00825           for (j = i; j < itext_size; j++)
00826             itext_info[j] = NULL;
00827         }
00828 
00829       /* Now add the pointer and the offset. */
00830       itext_info[i] = xmalloc (sizeof (ITEXT));
00831       itext_info[i]->pointer = pointer;
00832       itext_info[i]->offset = offset;
00833       itext = itext_info[i];
00834     }
00835   return itext;
00836 }
00837 
00838 /* Forget the input text associated with POINTER. */
00839 void
00840 forget_itext (char *pointer)
00841 {
00842   int i;
00843 
00844   for (i = 0; i < itext_size; i++)
00845     if (itext_info[i] && (itext_info[i]->pointer == pointer))
00846       {
00847         free (itext_info[i]);
00848         itext_info[i] = NULL;
00849         break;
00850       }
00851 }
00852 
00853 /* Append the text which appeared in input_text from the last offset to
00854    the character just before the command that we are currently executing. */
00855 void
00856 me_append_before_this_command (void)
00857 {
00858   int i;
00859 
00860   for (i = input_text_offset; i && (input_text[i] != COMMAND_PREFIX); i--)
00861     ;
00862   maybe_write_itext (input_text, i);
00863 }
00864 
00865 /* Similar to execute_string, but only takes a single string argument,
00866    and remembers the input text location, etc. */
00867 void
00868 me_execute_string (char *execution_string)
00869 {
00870   int saved_escape_html = escape_html;
00871   int saved_in_paragraph = in_paragraph;
00872   escape_html = me_executing_string == 0;
00873   in_paragraph = 0;
00874   
00875   pushfile ();
00876   input_text_offset = 0;
00877   /* The following xstrdup is so we can relocate input_text at will.  */
00878   input_text = xstrdup (execution_string);
00879   input_filename = xstrdup (input_filename);
00880   input_text_length = strlen (execution_string);
00881 
00882   remember_itext (input_text, 0);
00883 
00884   me_executing_string++;
00885   reader_loop ();
00886   free (input_text);
00887   free (input_filename);
00888   popfile ();
00889   me_executing_string--;
00890 
00891   in_paragraph = saved_in_paragraph;
00892   escape_html = saved_escape_html;
00893 }
00894 
00895 /* A wrapper around me_execute_string which saves and restores
00896    variables important for output generation.  This is called
00897    when we need to produce macro-expanded output for input which
00898    leaves no traces in the Info output.  */
00899 void
00900 me_execute_string_keep_state (char *execution_string, char *append_string)
00901 {
00902   int op_orig, opcol_orig, popen_orig;
00903   int fill_orig, newline_orig, indent_orig, meta_pos_orig;
00904 
00905   remember_itext (input_text, input_text_offset);
00906   op_orig = output_paragraph_offset;
00907   meta_pos_orig = meta_char_pos;
00908   opcol_orig = output_column;
00909   popen_orig = paragraph_is_open;
00910   fill_orig = filling_enabled;
00911   newline_orig = last_char_was_newline;
00912   filling_enabled = 0;
00913   indent_orig = no_indent;
00914   no_indent = 1;
00915   me_execute_string (execution_string);
00916   if (append_string)
00917     write_region_to_macro_output (append_string, 0, strlen (append_string));
00918   output_paragraph_offset = op_orig;
00919   meta_char_pos = meta_pos_orig;
00920   output_column = opcol_orig;
00921   paragraph_is_open = popen_orig;
00922   filling_enabled = fill_orig;
00923   last_char_was_newline = newline_orig;
00924   no_indent = indent_orig;
00925 }
00926 
00927 /* Append the text which appears in input_text from the last offset to
00928    the current OFFSET. */
00929 void
00930 append_to_expansion_output (int offset)
00931 {
00932   int i;
00933   ITEXT *itext = NULL;
00934 
00935   for (i = 0; i < itext_size; i++)
00936     if (itext_info[i] && itext_info[i]->pointer == input_text)
00937       {
00938         itext = itext_info[i];
00939         break;
00940       }
00941 
00942   if (!itext)
00943     return;
00944 
00945   if (offset > itext->offset)
00946     {
00947       write_region_to_macro_output (input_text, itext->offset, offset);
00948       remember_itext (input_text, offset);
00949     }
00950 }
00951 
00952 /* Only write this input text iff it appears in our itext list. */
00953 void
00954 maybe_write_itext (char *pointer, int offset)
00955 {
00956   int i;
00957   ITEXT *itext = NULL;
00958 
00959   for (i = 0; i < itext_size; i++)
00960     if (itext_info[i] && (itext_info[i]->pointer == pointer))
00961       {
00962         itext = itext_info[i];
00963         break;
00964       }
00965 
00966   if (itext && (itext->offset < offset))
00967     {
00968       write_region_to_macro_output (itext->pointer, itext->offset, offset);
00969       remember_itext (pointer, offset);
00970     }
00971 }
00972 
00973 void
00974 write_region_to_macro_output (char *string, int start, int end)
00975 {
00976   if (macro_expansion_output_stream)
00977     fwrite (string + start, 1, end - start, macro_expansion_output_stream);
00978 }
00979 
00980 /* Aliases. */
00981 
00982 typedef struct alias_struct
00983 {
00984   char *alias;
00985   char *mapto;
00986   struct alias_struct *next;
00987 } alias_type;
00988 
00989 static alias_type *aliases; 
00990 
00991 /* @alias aname = cmdname */
00992 
00993 void
00994 cm_alias (void)
00995 {
00996   alias_type *a = xmalloc (sizeof (alias_type));
00997 
00998   skip_whitespace ();
00999   get_until_in_line (0, "=", &(a->alias));
01000   canon_white (a->alias);
01001 
01002   discard_until ("=");
01003   skip_whitespace ();
01004   get_until_in_line (0, " ", &(a->mapto));
01005 
01006   a->next = aliases;
01007   aliases = a;
01008 }
01009 
01010 /* Perform an alias expansion.  Called from read_command.  */
01011 char *
01012 alias_expand (char *tok)
01013 {
01014   alias_type *findit = aliases;
01015 
01016   while (findit)
01017     if (strcmp (findit->alias, tok) == 0)
01018       {
01019        free (tok);
01020        return alias_expand (xstrdup (findit->mapto));
01021       }
01022     else
01023       findit = findit->next;
01024 
01025   return tok;
01026 }
01027 
01028 /* definfoenclose implementation.  */
01029 
01030 /* This structure is used to track enclosure macros.  When an enclosure
01031    macro is recognized, a pointer to the enclosure block corresponding 
01032    to its name is saved in the brace element for its argument. */
01033 typedef struct enclose_struct
01034 {
01035   char *enclose;
01036   char *before;
01037   char *after;
01038   struct enclose_struct *next;
01039 } enclosure_type;
01040 
01041 static enclosure_type *enclosures; 
01042 
01043 typedef struct enclosure_stack_struct
01044 {
01045     enclosure_type *current;
01046     struct enclosure_stack_struct *next;
01047 } enclosure_stack_type;
01048 
01049 static enclosure_stack_type *enclosure_stack;
01050 
01051 /* @definfoenclose */
01052 void
01053 cm_definfoenclose (void)
01054 {
01055   enclosure_type *e = xmalloc (sizeof (enclosure_type));
01056 
01057   skip_whitespace ();
01058   get_until_in_line (1, ",", &(e->enclose));
01059   discard_until (",");
01060   get_until_in_line (0, ",", &(e->before));
01061   discard_until (",");
01062   get_until_in_line (0, "\n", &(e->after));
01063 
01064   e->next = enclosures;
01065   enclosures = e;
01066 }
01067 
01068 /* If TOK is an enclosure command, push it on the enclosure stack and
01069    return 1.  Else return 0.  */
01070 
01071 int
01072 enclosure_command (char *tok)
01073 {
01074   enclosure_type *findit = enclosures;
01075 
01076   while (findit)
01077     if (strcmp (findit->enclose, tok) == 0)
01078       {
01079         enclosure_stack_type *new = xmalloc (sizeof (enclosure_stack_type));
01080         new->current = findit;
01081         new->next = enclosure_stack;
01082         enclosure_stack = new;
01083 
01084         return 1;
01085       }
01086     else
01087       findit = findit->next;
01088 
01089   return 0;
01090 }
01091 
01092 /* actually perform the enclosure expansion */
01093 void
01094 enclosure_expand (int arg, int start, int end)
01095 {
01096   if (arg == START)
01097     add_word (enclosure_stack->current->before);
01098   else
01099     {
01100       enclosure_stack_type *temp;
01101 
01102       add_word (enclosure_stack->current->after);
01103 
01104       temp = enclosure_stack;
01105       enclosure_stack = enclosure_stack->next;
01106       free (temp);
01107     }
01108 }