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