156160Sru/* macro.c -- user-defined macros for Texinfo.
2146515Sru   $Id: macro.c,v 1.6 2004/04/11 17:56:47 karl Exp $
356160Sru
4116525Sru   Copyright (C) 1998, 1999, 2002, 2003 Free Software Foundation, Inc.
556160Sru
656160Sru   This program is free software; you can redistribute it and/or modify
756160Sru   it under the terms of the GNU General Public License as published by
856160Sru   the Free Software Foundation; either version 2, or (at your option)
956160Sru   any later version.
1056160Sru
1156160Sru   This program is distributed in the hope that it will be useful,
1256160Sru   but WITHOUT ANY WARRANTY; without even the implied warranty of
1356160Sru   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1456160Sru   GNU General Public License for more details.
1556160Sru
1656160Sru   You should have received a copy of the GNU General Public License
1756160Sru   along with this program; if not, write to the Free Software Foundation,
1856160Sru   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
1956160Sru
2056160Sru#include "system.h"
2156160Sru#include "cmds.h"
22146515Sru#include "files.h"
2356160Sru#include "macro.h"
2456160Sru#include "makeinfo.h"
2556160Sru#include "insertion.h"
2656160Sru
2756160Sru/* If non-NULL, this is an output stream to write the full macro expansion
2856160Sru   of the input text to.  The result is another texinfo file, but
2956160Sru   missing @include, @infoinclude, @macro, and macro invocations.  Instead,
3056160Sru   all of the text is placed within the file. */
3156160SruFILE *macro_expansion_output_stream = NULL;
3256160Sru
3356160Sru/* Output file for -E.  */
3456160Sruchar *macro_expansion_filename;
3556160Sru
3656160Sru/* Nonzero means a macro string is in execution, as opposed to a file. */
3756160Sruint me_executing_string = 0;
3856160Sru
3956160Sru/* Nonzero means we want only to expand macros and
4056160Sru   leave everything else intact.  */
4156160Sruint only_macro_expansion = 0;
4256160Sru
4356160Srustatic ITEXT **itext_info = NULL;
4456160Srustatic int itext_size = 0;
4556160Sru
4656160Sru/* Return the arglist on the current line.  This can behave in two different
4756160Sru   ways, depending on the variable BRACES_REQUIRED_FOR_MACRO_ARGS. */
4856160Sruint braces_required_for_macro_args = 0;
4956160Sru
5056160Sru/* Array of macros and definitions. */
5156160SruMACRO_DEF **macro_list = NULL;
5256160Sru
5356160Sruint macro_list_len = 0;         /* Number of elements. */
5456160Sruint macro_list_size = 0;        /* Number of slots in total. */
5556160Sru
5656160Sru/* Return the length of the array in ARRAY. */
5756160Sruint
58146515Sruarray_len (char **array)
5956160Sru{
6056160Sru  int i = 0;
6156160Sru
6256160Sru  if (array)
6356160Sru    for (i = 0; array[i]; i++);
6456160Sru
6556160Sru  return i;
6656160Sru}
6756160Sru
6856160Sruvoid
69146515Srufree_array (char **array)
7056160Sru{
7156160Sru  if (array)
7256160Sru    {
7356160Sru      int i;
7456160Sru      for (i = 0; array[i]; i++)
7556160Sru        free (array[i]);
7656160Sru
7756160Sru      free (array);
7856160Sru    }
7956160Sru}
8056160Sru
8156160Sru/* Return the macro definition of NAME or NULL if NAME is not defined. */
8256160SruMACRO_DEF *
83146515Srufind_macro (char *name)
8456160Sru{
8556160Sru  int i;
8656160Sru  MACRO_DEF *def;
8756160Sru
8856160Sru  def = NULL;
8956160Sru  for (i = 0; macro_list && (def = macro_list[i]); i++)
9056160Sru    {
9156160Sru      if ((!def->inhibited) && (strcmp (def->name, name) == 0))
9256160Sru        break;
9356160Sru    }
9456160Sru  return def;
9556160Sru}
9656160Sru
9756160Sru/* Add the macro NAME with ARGLIST and BODY to the list of defined macros.
9856160Sru   SOURCE_FILE is the name of the file where this definition can be found,
9956160Sru   and SOURCE_LINENO is the line number within that file.  If a macro already
10056160Sru   exists with NAME, then a warning is produced, and that previous
10156160Sru   definition is overwritten. */
102146515Srustatic void
103146515Sruadd_macro (char *name, char **arglist, char *body, char *source_file,
104146515Sru    int source_lineno, int flags)
10556160Sru{
10656160Sru  MACRO_DEF *def;
10756160Sru
10856160Sru  def = find_macro (name);
10956160Sru
11056160Sru  if (!def)
11156160Sru    {
11256160Sru      if (macro_list_len + 2 >= macro_list_size)
11356160Sru        macro_list = xrealloc
11456160Sru          (macro_list, ((macro_list_size += 10) * sizeof (MACRO_DEF *)));
11556160Sru
11656160Sru      macro_list[macro_list_len] = xmalloc (sizeof (MACRO_DEF));
11756160Sru      macro_list[macro_list_len + 1] = NULL;
11856160Sru
11956160Sru      def = macro_list[macro_list_len];
12056160Sru      macro_list_len += 1;
12156160Sru      def->name = name;
12256160Sru    }
12356160Sru  else
12456160Sru    {
12556160Sru      char *temp_filename = input_filename;
12656160Sru      int temp_line = line_number;
12756160Sru
12856160Sru      warning (_("macro `%s' previously defined"), name);
12956160Sru
13056160Sru      input_filename = def->source_file;
13156160Sru      line_number = def->source_lineno;
13256160Sru      warning (_("here is the previous definition of `%s'"), name);
13356160Sru
13456160Sru      input_filename = temp_filename;
13556160Sru      line_number = temp_line;
13656160Sru
13756160Sru      if (def->arglist)
13856160Sru        {
13956160Sru          int i;
14056160Sru
14156160Sru          for (i = 0; def->arglist[i]; i++)
14256160Sru            free (def->arglist[i]);
14356160Sru
14456160Sru          free (def->arglist);
14556160Sru        }
14656160Sru      free (def->source_file);
14756160Sru      free (def->body);
14856160Sru    }
14956160Sru
15056160Sru  def->source_file = xstrdup (source_file);
15156160Sru  def->source_lineno = source_lineno;
15256160Sru  def->body = body;
15356160Sru  def->arglist = arglist;
15456160Sru  def->inhibited = 0;
15556160Sru  def->flags = flags;
15656160Sru}
15756160Sru
15856160Sru
15956160Sruchar **
160146515Sruget_brace_args (int quote_single)
16156160Sru{
16256160Sru  char **arglist, *word;
16356160Sru  int arglist_index, arglist_size;
16456160Sru  int character, escape_seen, start;
16556160Sru  int depth = 1;
16656160Sru
16756160Sru  /* There is an arglist in braces here, so gather the args inside of it. */
16856160Sru  skip_whitespace_and_newlines ();
16956160Sru  input_text_offset++;
17056160Sru  arglist = NULL;
17156160Sru  arglist_index = arglist_size = 0;
17256160Sru
17356160Sru get_arg:
17456160Sru  skip_whitespace_and_newlines ();
17556160Sru  start = input_text_offset;
17656160Sru  escape_seen = 0;
17756160Sru
17856160Sru  while ((character = curchar ()))
17956160Sru    {
18056160Sru      if (character == '\\')
18156160Sru        {
18256160Sru          input_text_offset += 2;
18356160Sru          escape_seen = 1;
18456160Sru        }
18556160Sru      else if (character == '{')
18656160Sru        {
18756160Sru          depth++;
18856160Sru          input_text_offset++;
18956160Sru        }
19056160Sru      else if ((character == ',' && !quote_single) ||
19156160Sru               ((character == '}') && depth == 1))
19256160Sru        {
19356160Sru          int len = input_text_offset - start;
19456160Sru
19556160Sru          if (len || (character != '}'))
19656160Sru            {
19756160Sru              word = xmalloc (1 + len);
19856160Sru              memcpy (word, input_text + start, len);
19956160Sru              word[len] = 0;
20056160Sru
20156160Sru              /* Clean up escaped characters. */
20256160Sru              if (escape_seen)
20356160Sru                {
20456160Sru                  int i;
20556160Sru                  for (i = 0; word[i]; i++)
20656160Sru                    if (word[i] == '\\')
20756160Sru                      memmove (word + i, word + i + 1,
20856160Sru                               1 + strlen (word + i + 1));
20956160Sru                }
21056160Sru
21156160Sru              if (arglist_index + 2 >= arglist_size)
21256160Sru                arglist = xrealloc
21356160Sru                  (arglist, (arglist_size += 10) * sizeof (char *));
21456160Sru
21556160Sru              arglist[arglist_index++] = word;
21656160Sru              arglist[arglist_index] = NULL;
21756160Sru            }
21856160Sru
21956160Sru          input_text_offset++;
22056160Sru          if (character == '}')
22156160Sru            break;
22256160Sru          else
22356160Sru            goto get_arg;
22456160Sru        }
22556160Sru      else if (character == '}')
22656160Sru        {
22756160Sru          depth--;
22856160Sru          input_text_offset++;
22956160Sru        }
23056160Sru      else
23156160Sru        {
23256160Sru          input_text_offset++;
23356160Sru          if (character == '\n') line_number++;
23456160Sru        }
23556160Sru    }
23656160Sru  return arglist;
23756160Sru}
23856160Sru
239146515Srustatic char **
240146515Sruget_macro_args (MACRO_DEF *def)
24156160Sru{
24256160Sru  int i;
24356160Sru  char *word;
24456160Sru
24556160Sru  /* Quickly check to see if this macro has been invoked with any arguments.
24656160Sru     If not, then don't skip any of the following whitespace. */
24756160Sru  for (i = input_text_offset; i < input_text_length; i++)
24856160Sru    if (!cr_or_whitespace (input_text[i]))
24956160Sru      break;
25056160Sru
25156160Sru  if (input_text[i] != '{')
25256160Sru    {
25356160Sru      if (braces_required_for_macro_args)
25456160Sru        {
25556160Sru          return NULL;
25656160Sru        }
25756160Sru      else
25856160Sru        {
25956160Sru          /* Braces are not required to fill out the macro arguments.  If
26056160Sru             this macro takes one argument, it is considered to be the
26156160Sru             remainder of the line, sans whitespace. */
26256160Sru          if (def->arglist && def->arglist[0] && !def->arglist[1])
26356160Sru            {
26456160Sru              char **arglist;
26556160Sru
26656160Sru              get_rest_of_line (0, &word);
26756160Sru              if (input_text[input_text_offset - 1] == '\n')
26856160Sru                {
26956160Sru                  input_text_offset--;
27056160Sru                  line_number--;
27156160Sru                }
27256160Sru              /* canon_white (word); */
27356160Sru              arglist = xmalloc (2 * sizeof (char *));
27456160Sru              arglist[0] = word;
27556160Sru              arglist[1] = NULL;
27656160Sru              return arglist;
27756160Sru            }
27856160Sru          else
27956160Sru            {
28056160Sru              /* The macro either took no arguments, or took more than
28156160Sru                 one argument.  In that case, it must be invoked with
28256160Sru                 arguments surrounded by braces. */
28356160Sru              return NULL;
28456160Sru            }
28556160Sru        }
28656160Sru    }
28756160Sru  return get_brace_args (def->flags & ME_QUOTE_ARG);
28856160Sru}
28956160Sru
29056160Sru/* Substitute actual parameters for named parameters in body.
29156160Sru   The named parameters which appear in BODY must by surrounded
29256160Sru   reverse slashes, as in \foo\. */
293146515Srustatic char *
294146515Sruapply (char **named, char **actuals, char *body)
29556160Sru{
29656160Sru  int i;
29756160Sru  int new_body_index, new_body_size;
29856160Sru  char *new_body, *text;
29956160Sru  int length_of_actuals;
30056160Sru
30156160Sru  length_of_actuals = array_len (actuals);
30256160Sru  new_body_size = strlen (body);
30356160Sru  new_body = xmalloc (1 + new_body_size);
30456160Sru
30556160Sru  /* Copy chars from BODY into NEW_BODY. */
30656160Sru  i = 0;
30756160Sru  new_body_index = 0;
30856160Sru
30956160Sru  while (body[i])
31056160Sru    { /* Anything but a \ is easy.  */
31156160Sru      if (body[i] != '\\')
31256160Sru        new_body[new_body_index++] = body[i++];
31356160Sru      else
31456160Sru        { /* Snarf parameter name, check against named parameters. */
31556160Sru          char *param;
316116525Sru          int param_start, len;
31756160Sru
31856160Sru          param_start = ++i;
31956160Sru          while (body[i] && body[i] != '\\')
32056160Sru            i++;
32156160Sru
32256160Sru          len = i - param_start;
32356160Sru          param = xmalloc (1 + len);
32456160Sru          memcpy (param, body + param_start, len);
32556160Sru          param[len] = 0;
32656160Sru
32756160Sru          if (body[i]) /* move past \ */
32856160Sru            i++;
32956160Sru
330116525Sru          if (len == 0)
331116525Sru            { /* \\ always means \, even if macro has no args.  */
33256160Sru              len++;
33356160Sru              text = xmalloc (1 + len);
33456160Sru              sprintf (text, "\\%s", param);
33556160Sru            }
336116525Sru          else
337116525Sru            {
338116525Sru              int which;
339116525Sru
340116525Sru              /* Check against named parameters. */
341116525Sru              for (which = 0; named && named[which]; which++)
342116525Sru                if (STREQ (named[which], param))
343116525Sru                  break;
34456160Sru
345116525Sru              if (named && named[which])
346116525Sru                {
347116525Sru                  text = which < length_of_actuals ? actuals[which] : NULL;
348116525Sru                  if (!text)
349116525Sru                    text = "";
350116525Sru                  len = strlen (text);
351116525Sru                  text = xstrdup (text);  /* so we can free it */
352116525Sru                }
353116525Sru              else
354116525Sru                { /* not a parameter, so it's an error.  */
355116525Sru                  warning (_("\\ in macro expansion followed by `%s' instead of parameter name"),
356116525Sru                             param);
357116525Sru                  len++;
358116525Sru                  text = xmalloc (1 + len);
359116525Sru                  sprintf (text, "\\%s", param);
360116525Sru                }
361116525Sru            }
362116525Sru
36356160Sru          if (strlen (param) + 2 < len)
36456160Sru            {
36556160Sru              new_body_size += len + 1;
36656160Sru              new_body = xrealloc (new_body, new_body_size);
36756160Sru            }
36856160Sru
36956160Sru          free (param);
37056160Sru
37156160Sru          strcpy (new_body + new_body_index, text);
37256160Sru          new_body_index += len;
37356160Sru
374116525Sru          free (text);
37556160Sru        }
37656160Sru    }
37756160Sru
37856160Sru  new_body[new_body_index] = 0;
37956160Sru  return new_body;
38056160Sru}
38156160Sru
38256160Sru/* Expand macro passed in DEF, a pointer to a MACRO_DEF, and
38356160Sru   return its expansion as a string.  */
38456160Sruchar *
385146515Sruexpand_macro (MACRO_DEF *def)
38656160Sru{
38756160Sru  char **arglist;
38856160Sru  int num_args;
38956160Sru  char *execution_string = NULL;
39056160Sru  int start_line = line_number;
39156160Sru
39256160Sru  /* Find out how many arguments this macro definition takes. */
39356160Sru  num_args = array_len (def->arglist);
39456160Sru
39556160Sru  /* Gather the arguments present on the line if there are any. */
39656160Sru  arglist = get_macro_args (def);
39756160Sru
39856160Sru  if (num_args < array_len (arglist))
39956160Sru    {
40056160Sru      free_array (arglist);
40156160Sru      line_error (_("Macro `%s' called on line %d with too many args"),
40256160Sru                  def->name, start_line);
40356160Sru      return execution_string;
40456160Sru    }
40556160Sru
40656160Sru  if (def->body)
40756160Sru    execution_string = apply (def->arglist, arglist, def->body);
40856160Sru
40956160Sru  free_array (arglist);
41056160Sru  return execution_string;
41156160Sru}
41256160Sru
41356160Sru/* Execute the macro passed in DEF, a pointer to a MACRO_DEF.  */
41456160Sruvoid
415146515Sruexecute_macro (MACRO_DEF *def)
41656160Sru{
41756160Sru  char *execution_string;
41856160Sru  int start_line = line_number, end_line;
41956160Sru
42056160Sru  if (macro_expansion_output_stream && !executing_string && !me_inhibit_expansion)
42156160Sru    me_append_before_this_command ();
42256160Sru
42356160Sru  execution_string = expand_macro (def);
42456160Sru  if (!execution_string)
42556160Sru    return;
42656160Sru
42756160Sru  if (def->body)
42856160Sru    {
42956160Sru      /* Reset the line number to where the macro arguments began.
43056160Sru         This makes line numbers reported in error messages correct in
43156160Sru         case the macro arguments span several lines and the expanded
43256160Sru         arguments invoke other commands.  */
43356160Sru      end_line = line_number;
43456160Sru      line_number = start_line;
43556160Sru
436146515Sru      if (macro_expansion_output_stream
437146515Sru          && !executing_string && !me_inhibit_expansion)
43856160Sru        {
43956160Sru          remember_itext (input_text, input_text_offset);
44056160Sru          me_execute_string (execution_string);
44156160Sru        }
44256160Sru      else
44356160Sru        execute_string ("%s", execution_string);
44456160Sru
44556160Sru      free (execution_string);
44656160Sru      line_number = end_line;
44756160Sru    }
44856160Sru}
44956160Sru
45056160Sru
45156160Sru/* Read and remember the definition of a macro.  If RECURSIVE is set,
45256160Sru   set the ME_RECURSE flag.  MACTYPE is either "macro" or "rmacro", and
45356160Sru   tells us what the matching @end should be.  */
45456160Srustatic void
455146515Srudefine_macro (char *mactype, int recursive)
45656160Sru{
457146515Sru  int i, start;
458146515Sru  char *name, *line;
459146515Sru  char *last_end = NULL;
460146515Sru  char *body = NULL;
461146515Sru  char **arglist = NULL;
462146515Sru  int body_size = 0, body_index = 0;
46356160Sru  int depth = 1;
464146515Sru  int flags = 0;
46556160Sru  int defining_line = line_number;
46656160Sru
46756160Sru  if (macro_expansion_output_stream && !executing_string)
46856160Sru    me_append_before_this_command ();
46956160Sru
47056160Sru  skip_whitespace ();
47156160Sru
47256160Sru  /* Get the name of the macro.  This is the set of characters which are
47356160Sru     not whitespace and are not `{' immediately following the @macro. */
474146515Sru  start = input_text_offset;
47556160Sru  {
47656160Sru    int len;
47756160Sru
478146515Sru    for (i = start; i < input_text_length && input_text[i] != '{'
479146515Sru                    && !cr_or_whitespace (input_text[i]);
480146515Sru         i++) ;
48156160Sru
48256160Sru    len = i - start;
48356160Sru    name = xmalloc (1 + len);
48456160Sru    memcpy (name, input_text + start, len);
48556160Sru    name[len] = 0;
48656160Sru    input_text_offset = i;
48756160Sru  }
48856160Sru
48956160Sru  skip_whitespace ();
49056160Sru
49156160Sru  /* It is not required that the definition of a macro includes an arglist.
49256160Sru     If not, don't try to get the named parameters, just use a null list. */
49356160Sru  if (curchar () == '{')
49456160Sru    {
49556160Sru      int character;
49656160Sru      int arglist_index = 0, arglist_size = 0;
49756160Sru      int gathering_words = 1;
49856160Sru      char *word = NULL;
49956160Sru
50056160Sru      /* Read the words inside of the braces which determine the arglist.
50156160Sru         These words will be replaced within the body of the macro at
50256160Sru         execution time. */
50356160Sru
50456160Sru      input_text_offset++;
50556160Sru      skip_whitespace_and_newlines ();
50656160Sru
50756160Sru      while (gathering_words)
50856160Sru        {
50956160Sru          int len;
51056160Sru
51156160Sru          for (i = input_text_offset;
51256160Sru               (character = input_text[i]);
51356160Sru               i++)
51456160Sru            {
51556160Sru              switch (character)
51656160Sru                {
51756160Sru                case '\n':
51856160Sru                  line_number++;
51956160Sru                case ' ':
52056160Sru                case '\t':
52156160Sru                case ',':
52256160Sru                case '}':
52356160Sru                  /* Found the end of the current arglist word.  Save it. */
52456160Sru                  len = i - input_text_offset;
52556160Sru                  word = xmalloc (1 + len);
52656160Sru                  memcpy (word, input_text + input_text_offset, len);
52756160Sru                  word[len] = 0;
52856160Sru                  input_text_offset = i;
52956160Sru
53056160Sru                  /* Advance to the comma or close-brace that signified
53156160Sru                     the end of the argument. */
53256160Sru                  while ((character = curchar ())
53356160Sru                         && character != ','
53456160Sru                         && character != '}')
53556160Sru                    {
53656160Sru                      input_text_offset++;
53756160Sru                      if (character == '\n')
53856160Sru                        line_number++;
53956160Sru                    }
54056160Sru
54156160Sru                  /* Add the word to our list of words. */
54256160Sru                  if (arglist_index + 2 >= arglist_size)
54356160Sru                    {
54456160Sru                      arglist_size += 10;
54556160Sru                      arglist = xrealloc (arglist,
54656160Sru                                          arglist_size * sizeof (char *));
54756160Sru                    }
54856160Sru
54956160Sru                  arglist[arglist_index++] = word;
55056160Sru                  arglist[arglist_index] = NULL;
55156160Sru                  break;
55256160Sru                }
55356160Sru
55456160Sru              if (character == '}')
55556160Sru                {
55656160Sru                  input_text_offset++;
55756160Sru                  gathering_words = 0;
55856160Sru                  break;
55956160Sru                }
56056160Sru
56156160Sru              if (character == ',')
56256160Sru                {
56356160Sru                  input_text_offset++;
56456160Sru                  skip_whitespace_and_newlines ();
56556160Sru                  i = input_text_offset - 1;
56656160Sru                }
56756160Sru            }
56856160Sru        }
56956160Sru
57056160Sru      /* If we have exactly one argument, do @quote-arg implicitly.  Not
57156160Sru         only does this match TeX's behavior (which can't feasibly be
57256160Sru         changed), but it's a good idea.  */
57356160Sru      if (arglist_index == 1)
57456160Sru        flags |= ME_QUOTE_ARG;
57556160Sru    }
57656160Sru
57756160Sru  /* Read the text carefully until we find an "@end macro" which
57856160Sru     matches this one.  The text in between is the body of the macro. */
57956160Sru  skip_whitespace_and_newlines ();
58056160Sru
58156160Sru  while (depth)
58256160Sru    {
58356160Sru      if ((input_text_offset + 9) > input_text_length)
58456160Sru        {
58593139Sru          file_line_error (input_filename, defining_line,
58693139Sru			   _("%cend macro not found"), COMMAND_PREFIX);
58756160Sru          return;
58856160Sru        }
58956160Sru
59056160Sru      get_rest_of_line (0, &line);
59156160Sru
59256160Sru      /* Handle commands only meaningful within a macro. */
59356160Sru      if ((*line == COMMAND_PREFIX) && (depth == 1) &&
59456160Sru          (strncmp (line + 1, "allow-recursion", 15) == 0) &&
59556160Sru          (line[16] == 0 || whitespace (line[16])))
59656160Sru        {
59756160Sru          for (i = 16; whitespace (line[i]); i++);
59856160Sru          strcpy (line, line + i);
59956160Sru          flags |= ME_RECURSE;
60056160Sru          if (!*line)
60156160Sru            {
60256160Sru              free (line);
60356160Sru              continue;
60456160Sru            }
60556160Sru        }
60656160Sru
60756160Sru      if ((*line == COMMAND_PREFIX) && (depth == 1) &&
60856160Sru          (strncmp (line + 1, "quote-arg", 9) == 0) &&
60956160Sru          (line[10] == 0 || whitespace (line[10])))
61056160Sru        {
61156160Sru          for (i = 10; whitespace (line[i]); i++);
61256160Sru          strcpy (line, line + i);
61356160Sru
61456160Sru          if (arglist && arglist[0] && !arglist[1])
61556160Sru            {
61656160Sru              flags |= ME_QUOTE_ARG;
61756160Sru              if (!*line)
61856160Sru                {
61956160Sru                  free (line);
62056160Sru                  continue;
62156160Sru                }
62256160Sru            }
62356160Sru          else
62456160Sru           line_error (_("@quote-arg only useful for single-argument macros"));
62556160Sru        }
62656160Sru
62756160Sru      if (*line == COMMAND_PREFIX
62856160Sru          && (strncmp (line + 1, "macro ", 6) == 0
62956160Sru              || strncmp (line + 1, "rmacro ", 7) == 0))
63056160Sru        depth++;
63156160Sru
63256160Sru      /* Incorrect implementation of nesting -- just check that the last
63356160Sru         @end matches what we started with.  Since nested macros don't
63456160Sru         work in TeX anyway, this isn't worth the trouble to get right.  */
63556160Sru      if (*line == COMMAND_PREFIX && strncmp (line + 1, "end macro", 9) == 0)
63656160Sru        {
63756160Sru          depth--;
63856160Sru          last_end = "macro";
63956160Sru        }
640146515Sru      if (*line == COMMAND_PREFIX && strncmp (line + 1, "end rmacro", 10) == 0)
64156160Sru        {
64256160Sru          depth--;
64356160Sru          last_end = "rmacro";
64456160Sru        }
64556160Sru
64656160Sru      if (depth)
64756160Sru        {
64856160Sru          if ((body_index + strlen (line) + 3) >= body_size)
64956160Sru            body = xrealloc (body, body_size += 3 + strlen (line));
65056160Sru          strcpy (body + body_index, line);
65156160Sru          body_index += strlen (line);
65256160Sru          body[body_index++] = '\n';
65356160Sru          body[body_index] = 0;
65456160Sru        }
65556160Sru      free (line);
65656160Sru    }
65756160Sru
65856160Sru  /* Check that @end matched the macro command.  */
65956160Sru  if (!STREQ (last_end, mactype))
66056160Sru    warning (_("mismatched @end %s with @%s"), last_end, mactype);
66156160Sru
66256160Sru  /* If it was an empty macro like
66356160Sru     @macro foo
66456160Sru     @end macro
66556160Sru     create an empty body.  (Otherwise, the macro is not expanded.)  */
66656160Sru  if (!body)
66756160Sru    {
66856160Sru      body = (char *)malloc(1);
66956160Sru      *body = 0;
67056160Sru    }
67156160Sru
67256160Sru  /* We now have the name, the arglist, and the body.  However, BODY
67356160Sru     includes the final newline which preceded the `@end macro' text.
67456160Sru     Delete it. */
67556160Sru  if (body && strlen (body))
67656160Sru    body[strlen (body) - 1] = 0;
67756160Sru
67856160Sru  if (recursive)
67956160Sru    flags |= ME_RECURSE;
68056160Sru
68156160Sru  add_macro (name, arglist, body, input_filename, defining_line, flags);
68256160Sru
68356160Sru  if (macro_expansion_output_stream && !executing_string)
684146515Sru    {
685146515Sru      /* Remember text for future expansions.  */
686146515Sru      remember_itext (input_text, input_text_offset);
687146515Sru
688146515Sru      /* Bizarrely, output the @macro itself.  This is so texinfo.tex
689146515Sru         will have a chance to read it when texi2dvi calls makeinfo -E.
690146515Sru         The problem is that we don't really expand macros in all
691146515Sru         contexts; a @table's @item is one.  And a fix is not obvious to
692146515Sru         me, since it appears virtually identical to any other internal
693146515Sru         expansion.  Just setting a variable in cm_item caused other
694146515Sru         strange expansion problems.  */
695146515Sru      write_region_to_macro_output ("@", 0, 1);
696146515Sru      write_region_to_macro_output (mactype, 0, strlen (mactype));
697146515Sru      write_region_to_macro_output (" ", 0, 1);
698146515Sru      write_region_to_macro_output (input_text, start, input_text_offset);
699146515Sru    }
70056160Sru}
70156160Sru
70256160Sruvoid
703146515Srucm_macro (void)
70456160Sru{
70556160Sru  define_macro ("macro", 0);
70656160Sru}
70756160Sru
70856160Sruvoid
709146515Srucm_rmacro (void)
71056160Sru{
71156160Sru  define_macro ("rmacro", 1);
71256160Sru}
71356160Sru
71456160Sru/* Delete the macro with name NAME.  The macro is deleted from the list,
71556160Sru   but it is also returned.  If there was no macro defined, NULL is
71656160Sru   returned. */
71756160Sru
71856160Srustatic MACRO_DEF *
719146515Srudelete_macro (char *name)
72056160Sru{
72156160Sru  int i;
72256160Sru  MACRO_DEF *def;
72356160Sru
72456160Sru  def = NULL;
72556160Sru
72656160Sru  for (i = 0; macro_list && (def = macro_list[i]); i++)
72756160Sru    if (strcmp (def->name, name) == 0)
72856160Sru      {
72956160Sru        memmove (macro_list + i, macro_list + i + 1,
73056160Sru               ((macro_list_len + 1) - i) * sizeof (MACRO_DEF *));
73156160Sru        macro_list_len--;
73256160Sru        break;
73356160Sru      }
73456160Sru  return def;
73556160Sru}
73656160Sru
73756160Sruvoid
738146515Srucm_unmacro (void)
73956160Sru{
74056160Sru  int i;
74156160Sru  char *line, *name;
74256160Sru  MACRO_DEF *def;
74356160Sru
74456160Sru  if (macro_expansion_output_stream && !executing_string)
74556160Sru    me_append_before_this_command ();
74656160Sru
74756160Sru  get_rest_of_line (0, &line);
74856160Sru
74956160Sru  for (i = 0; line[i] && !whitespace (line[i]); i++);
75056160Sru  name = xmalloc (i + 1);
75156160Sru  memcpy (name, line, i);
75256160Sru  name[i] = 0;
75356160Sru
75456160Sru  def = delete_macro (name);
75556160Sru
75656160Sru  if (def)
75756160Sru    {
75856160Sru      free (def->source_file);
75956160Sru      free (def->name);
76056160Sru      free (def->body);
76156160Sru
76256160Sru      if (def->arglist)
76356160Sru        {
76456160Sru          int i;
76556160Sru
76656160Sru          for (i = 0; def->arglist[i]; i++)
76756160Sru            free (def->arglist[i]);
76856160Sru
76956160Sru          free (def->arglist);
77056160Sru        }
77156160Sru
77256160Sru      free (def);
77356160Sru    }
77456160Sru
77556160Sru  free (line);
77656160Sru  free (name);
77756160Sru
77856160Sru  if (macro_expansion_output_stream && !executing_string)
77956160Sru    remember_itext (input_text, input_text_offset);
78056160Sru}
78156160Sru
78256160Sru/* How to output sections of the input file verbatim. */
78356160Sru
78456160Sru/* Set the value of POINTER's offset to OFFSET. */
78556160SruITEXT *
786146515Sruremember_itext (char *pointer, int offset)
78756160Sru{
78856160Sru  int i;
78956160Sru  ITEXT *itext = NULL;
79056160Sru
79156160Sru  /* If we have no info, initialize a blank list. */
79256160Sru  if (!itext_info)
79356160Sru    {
79456160Sru      itext_info = xmalloc ((itext_size = 10) * sizeof (ITEXT *));
79556160Sru      for (i = 0; i < itext_size; i++)
79656160Sru        itext_info[i] = NULL;
79756160Sru    }
79856160Sru
79956160Sru  /* If the pointer is already present in the list, then set the offset. */
80056160Sru  for (i = 0; i < itext_size; i++)
80156160Sru    if ((itext_info[i]) &&
80256160Sru        (itext_info[i]->pointer == pointer))
80356160Sru      {
80456160Sru        itext = itext_info[i];
80556160Sru        itext_info[i]->offset = offset;
80656160Sru        break;
80756160Sru      }
80856160Sru
80956160Sru  if (i == itext_size)
81056160Sru    {
81156160Sru      /* Find a blank slot (or create a new one), and remember the
81256160Sru         pointer and offset. */
81356160Sru      for (i = 0; i < itext_size; i++)
81456160Sru        if (itext_info[i] == NULL)
81556160Sru          break;
81656160Sru
81756160Sru      /* If not found, then add some slots. */
81856160Sru      if (i == itext_size)
81956160Sru        {
82056160Sru          int j;
82156160Sru
82256160Sru          itext_info = xrealloc
82356160Sru            (itext_info, (itext_size += 10) * sizeof (ITEXT *));
82456160Sru
82556160Sru          for (j = i; j < itext_size; j++)
82656160Sru            itext_info[j] = NULL;
82756160Sru        }
82856160Sru
82956160Sru      /* Now add the pointer and the offset. */
83056160Sru      itext_info[i] = xmalloc (sizeof (ITEXT));
83156160Sru      itext_info[i]->pointer = pointer;
83256160Sru      itext_info[i]->offset = offset;
83356160Sru      itext = itext_info[i];
83456160Sru    }
83556160Sru  return itext;
83656160Sru}
83756160Sru
83856160Sru/* Forget the input text associated with POINTER. */
83956160Sruvoid
840146515Sruforget_itext (char *pointer)
84156160Sru{
84256160Sru  int i;
84356160Sru
84456160Sru  for (i = 0; i < itext_size; i++)
84556160Sru    if (itext_info[i] && (itext_info[i]->pointer == pointer))
84656160Sru      {
84756160Sru        free (itext_info[i]);
84856160Sru        itext_info[i] = NULL;
84956160Sru        break;
85056160Sru      }
85156160Sru}
85256160Sru
85356160Sru/* Append the text which appeared in input_text from the last offset to
85456160Sru   the character just before the command that we are currently executing. */
85556160Sruvoid
856146515Srume_append_before_this_command (void)
85756160Sru{
85856160Sru  int i;
85956160Sru
86056160Sru  for (i = input_text_offset; i && (input_text[i] != COMMAND_PREFIX); i--)
86156160Sru    ;
86256160Sru  maybe_write_itext (input_text, i);
86356160Sru}
86456160Sru
86556160Sru/* Similar to execute_string, but only takes a single string argument,
86656160Sru   and remembers the input text location, etc. */
86756160Sruvoid
868146515Srume_execute_string (char *execution_string)
86956160Sru{
87056160Sru  int saved_escape_html = escape_html;
87156160Sru  int saved_in_paragraph = in_paragraph;
87256160Sru  escape_html = me_executing_string == 0;
87356160Sru  in_paragraph = 0;
87456160Sru
87556160Sru  pushfile ();
87656160Sru  input_text_offset = 0;
87756160Sru  /* The following xstrdup is so we can relocate input_text at will.  */
87856160Sru  input_text = xstrdup (execution_string);
87956160Sru  input_filename = xstrdup (input_filename);
88056160Sru  input_text_length = strlen (execution_string);
88156160Sru
88256160Sru  remember_itext (input_text, 0);
88356160Sru
88456160Sru  me_executing_string++;
88556160Sru  reader_loop ();
88656160Sru  free (input_text);
88756160Sru  free (input_filename);
88856160Sru  popfile ();
88956160Sru  me_executing_string--;
89056160Sru
89156160Sru  in_paragraph = saved_in_paragraph;
89256160Sru  escape_html = saved_escape_html;
89356160Sru}
89456160Sru
89556160Sru/* A wrapper around me_execute_string which saves and restores
89656160Sru   variables important for output generation.  This is called
89756160Sru   when we need to produce macro-expanded output for input which
89856160Sru   leaves no traces in the Info output.  */
89956160Sruvoid
900146515Srume_execute_string_keep_state (char *execution_string, char *append_string)
90156160Sru{
90256160Sru  int op_orig, opcol_orig, popen_orig;
90356160Sru  int fill_orig, newline_orig, indent_orig, meta_pos_orig;
90456160Sru
90556160Sru  remember_itext (input_text, input_text_offset);
90656160Sru  op_orig = output_paragraph_offset;
90756160Sru  meta_pos_orig = meta_char_pos;
90856160Sru  opcol_orig = output_column;
90956160Sru  popen_orig = paragraph_is_open;
91056160Sru  fill_orig = filling_enabled;
91156160Sru  newline_orig = last_char_was_newline;
91256160Sru  filling_enabled = 0;
91356160Sru  indent_orig = no_indent;
91456160Sru  no_indent = 1;
91556160Sru  me_execute_string (execution_string);
91656160Sru  if (append_string)
91756160Sru    write_region_to_macro_output (append_string, 0, strlen (append_string));
91856160Sru  output_paragraph_offset = op_orig;
91956160Sru  meta_char_pos = meta_pos_orig;
92056160Sru  output_column = opcol_orig;
92156160Sru  paragraph_is_open = popen_orig;
92256160Sru  filling_enabled = fill_orig;
92356160Sru  last_char_was_newline = newline_orig;
92456160Sru  no_indent = indent_orig;
92556160Sru}
92656160Sru
92756160Sru/* Append the text which appears in input_text from the last offset to
92856160Sru   the current OFFSET. */
92956160Sruvoid
930146515Sruappend_to_expansion_output (int offset)
93156160Sru{
93256160Sru  int i;
93356160Sru  ITEXT *itext = NULL;
93456160Sru
93556160Sru  for (i = 0; i < itext_size; i++)
93656160Sru    if (itext_info[i] && itext_info[i]->pointer == input_text)
93756160Sru      {
93856160Sru        itext = itext_info[i];
93956160Sru        break;
94056160Sru      }
94156160Sru
94256160Sru  if (!itext)
94356160Sru    return;
94456160Sru
94556160Sru  if (offset > itext->offset)
94656160Sru    {
94756160Sru      write_region_to_macro_output (input_text, itext->offset, offset);
94856160Sru      remember_itext (input_text, offset);
94956160Sru    }
95056160Sru}
95156160Sru
95256160Sru/* Only write this input text iff it appears in our itext list. */
95356160Sruvoid
954146515Srumaybe_write_itext (char *pointer, int offset)
95556160Sru{
95656160Sru  int i;
95756160Sru  ITEXT *itext = NULL;
95856160Sru
95956160Sru  for (i = 0; i < itext_size; i++)
96056160Sru    if (itext_info[i] && (itext_info[i]->pointer == pointer))
96156160Sru      {
96256160Sru        itext = itext_info[i];
96356160Sru        break;
96456160Sru      }
96556160Sru
96656160Sru  if (itext && (itext->offset < offset))
96756160Sru    {
96856160Sru      write_region_to_macro_output (itext->pointer, itext->offset, offset);
96956160Sru      remember_itext (pointer, offset);
97056160Sru    }
97156160Sru}
97256160Sru
97356160Sruvoid
974146515Sruwrite_region_to_macro_output (char *string, int start, int end)
97556160Sru{
97656160Sru  if (macro_expansion_output_stream)
97756160Sru    fwrite (string + start, 1, end - start, macro_expansion_output_stream);
97856160Sru}
97956160Sru
98056160Sru/* Aliases. */
98156160Sru
98256160Srutypedef struct alias_struct
98356160Sru{
98456160Sru  char *alias;
98556160Sru  char *mapto;
98656160Sru  struct alias_struct *next;
98756160Sru} alias_type;
98856160Sru
98956160Srustatic alias_type *aliases;
99056160Sru
991146515Sru/* @alias aname = cmdname */
992146515Sru
99356160Sruvoid
994146515Srucm_alias (void)
99556160Sru{
99656160Sru  alias_type *a = xmalloc (sizeof (alias_type));
99756160Sru
99856160Sru  skip_whitespace ();
999146515Sru  get_until_in_line (0, "=", &(a->alias));
100093139Sru  canon_white (a->alias);
100193139Sru
100256160Sru  discard_until ("=");
100356160Sru  skip_whitespace ();
100456160Sru  get_until_in_line (0, " ", &(a->mapto));
100556160Sru
100656160Sru  a->next = aliases;
100756160Sru  aliases = a;
100856160Sru}
100956160Sru
101056160Sru/* Perform an alias expansion.  Called from read_command.  */
101156160Sruchar *
1012146515Srualias_expand (char *tok)
101356160Sru{
101456160Sru  alias_type *findit = aliases;
101556160Sru
101656160Sru  while (findit)
101756160Sru    if (strcmp (findit->alias, tok) == 0)
101856160Sru      {
101956160Sru	free (tok);
102056160Sru	return alias_expand (xstrdup (findit->mapto));
102156160Sru      }
102256160Sru    else
102356160Sru      findit = findit->next;
102456160Sru
102556160Sru  return tok;
102656160Sru}
102756160Sru
102856160Sru/* definfoenclose implementation.  */
102956160Sru
103056160Sru/* This structure is used to track enclosure macros.  When an enclosure
103156160Sru   macro is recognized, a pointer to the enclosure block corresponding
103256160Sru   to its name is saved in the brace element for its argument. */
103356160Srutypedef struct enclose_struct
103456160Sru{
103556160Sru  char *enclose;
103656160Sru  char *before;
103756160Sru  char *after;
103856160Sru  struct enclose_struct *next;
103956160Sru} enclosure_type;
104056160Sru
104156160Srustatic enclosure_type *enclosures;
104256160Sru
104356160Srutypedef struct enclosure_stack_struct
104456160Sru{
104556160Sru    enclosure_type *current;
104656160Sru    struct enclosure_stack_struct *next;
104756160Sru} enclosure_stack_type;
104856160Sru
104956160Srustatic enclosure_stack_type *enclosure_stack;
105056160Sru
105156160Sru/* @definfoenclose */
105256160Sruvoid
1053146515Srucm_definfoenclose (void)
105456160Sru{
105556160Sru  enclosure_type *e = xmalloc (sizeof (enclosure_type));
105656160Sru
105756160Sru  skip_whitespace ();
105856160Sru  get_until_in_line (1, ",", &(e->enclose));
105956160Sru  discard_until (",");
106056160Sru  get_until_in_line (0, ",", &(e->before));
106156160Sru  discard_until (",");
106256160Sru  get_until_in_line (0, "\n", &(e->after));
106356160Sru
106456160Sru  e->next = enclosures;
106556160Sru  enclosures = e;
106656160Sru}
106756160Sru
106856160Sru/* If TOK is an enclosure command, push it on the enclosure stack and
106956160Sru   return 1.  Else return 0.  */
107056160Sru
107156160Sruint
1072146515Sruenclosure_command (char *tok)
107356160Sru{
107456160Sru  enclosure_type *findit = enclosures;
107556160Sru
107656160Sru  while (findit)
107756160Sru    if (strcmp (findit->enclose, tok) == 0)
107856160Sru      {
107956160Sru        enclosure_stack_type *new = xmalloc (sizeof (enclosure_stack_type));
108056160Sru        new->current = findit;
108156160Sru        new->next = enclosure_stack;
108256160Sru        enclosure_stack = new;
108356160Sru
108456160Sru        return 1;
108556160Sru      }
108656160Sru    else
108756160Sru      findit = findit->next;
108856160Sru
108956160Sru  return 0;
109056160Sru}
109156160Sru
109256160Sru/* actually perform the enclosure expansion */
109356160Sruvoid
1094146515Sruenclosure_expand (int arg, int start, int end)
109556160Sru{
109656160Sru  if (arg == START)
109756160Sru    add_word (enclosure_stack->current->before);
109856160Sru  else
109956160Sru    {
110056160Sru      enclosure_stack_type *temp;
110156160Sru
110256160Sru      add_word (enclosure_stack->current->after);
110356160Sru
110456160Sru      temp = enclosure_stack;
110556160Sru      enclosure_stack = enclosure_stack->next;
110656160Sru      free (temp);
110756160Sru    }
110856160Sru}
1109