macro.c revision 56160
1/* macro.c -- user-defined macros for Texinfo.
2   $Id: macro.c,v 1.10 1999/08/17 21:06:35 karl Exp $
3
4   Copyright (C) 1998, 99 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          int temp_line = line_number;
594          line_number = defining_line;
595          line_error (_("%cend macro not found"), COMMAND_PREFIX);
596          line_number = temp_line;
597          return;
598        }
599
600      get_rest_of_line (0, &line);
601
602      /* Handle commands only meaningful within a macro. */
603      if ((*line == COMMAND_PREFIX) && (depth == 1) &&
604          (strncmp (line + 1, "allow-recursion", 15) == 0) &&
605          (line[16] == 0 || whitespace (line[16])))
606        {
607          for (i = 16; whitespace (line[i]); i++);
608          strcpy (line, line + i);
609          flags |= ME_RECURSE;
610          if (!*line)
611            {
612              free (line);
613              continue;
614            }
615        }
616
617      if ((*line == COMMAND_PREFIX) && (depth == 1) &&
618          (strncmp (line + 1, "quote-arg", 9) == 0) &&
619          (line[10] == 0 || whitespace (line[10])))
620        {
621          for (i = 10; whitespace (line[i]); i++);
622          strcpy (line, line + i);
623
624          if (arglist && arglist[0] && !arglist[1])
625            {
626              flags |= ME_QUOTE_ARG;
627              if (!*line)
628                {
629                  free (line);
630                  continue;
631                }
632            }
633          else
634           line_error (_("@quote-arg only useful for single-argument macros"));
635        }
636
637      if (*line == COMMAND_PREFIX
638          && (strncmp (line + 1, "macro ", 6) == 0
639              || strncmp (line + 1, "rmacro ", 7) == 0))
640        depth++;
641
642      /* Incorrect implementation of nesting -- just check that the last
643         @end matches what we started with.  Since nested macros don't
644         work in TeX anyway, this isn't worth the trouble to get right.  */
645      if (*line == COMMAND_PREFIX && strncmp (line + 1, "end macro", 9) == 0)
646        {
647          depth--;
648          last_end = "macro";
649        }
650      if (*line == COMMAND_PREFIX && strncmp (line + 1, "end rmacro", 9) == 0)
651        {
652          depth--;
653          last_end = "rmacro";
654        }
655
656      if (depth)
657        {
658          if ((body_index + strlen (line) + 3) >= body_size)
659            body = xrealloc (body, body_size += 3 + strlen (line));
660          strcpy (body + body_index, line);
661          body_index += strlen (line);
662          body[body_index++] = '\n';
663          body[body_index] = 0;
664        }
665      free (line);
666    }
667
668  /* Check that @end matched the macro command.  */
669  if (!STREQ (last_end, mactype))
670    warning (_("mismatched @end %s with @%s"), last_end, mactype);
671
672  /* If it was an empty macro like
673     @macro foo
674     @end macro
675     create an empty body.  (Otherwise, the macro is not expanded.)  */
676  if (!body)
677    {
678      body = (char *)malloc(1);
679      *body = 0;
680    }
681
682  /* We now have the name, the arglist, and the body.  However, BODY
683     includes the final newline which preceded the `@end macro' text.
684     Delete it. */
685  if (body && strlen (body))
686    body[strlen (body) - 1] = 0;
687
688  if (recursive)
689    flags |= ME_RECURSE;
690
691  add_macro (name, arglist, body, input_filename, defining_line, flags);
692
693  if (macro_expansion_output_stream && !executing_string)
694    remember_itext (input_text, input_text_offset);
695}
696
697void
698cm_macro ()
699{
700  define_macro ("macro", 0);
701}
702
703void
704cm_rmacro ()
705{
706  define_macro ("rmacro", 1);
707}
708
709/* Delete the macro with name NAME.  The macro is deleted from the list,
710   but it is also returned.  If there was no macro defined, NULL is
711   returned. */
712
713static MACRO_DEF *
714delete_macro (name)
715     char *name;
716{
717  int i;
718  MACRO_DEF *def;
719
720  def = NULL;
721
722  for (i = 0; macro_list && (def = macro_list[i]); i++)
723    if (strcmp (def->name, name) == 0)
724      {
725        memmove (macro_list + i, macro_list + i + 1,
726               ((macro_list_len + 1) - i) * sizeof (MACRO_DEF *));
727        macro_list_len--;
728        break;
729      }
730  return def;
731}
732
733void
734cm_unmacro ()
735{
736  int i;
737  char *line, *name;
738  MACRO_DEF *def;
739
740  if (macro_expansion_output_stream && !executing_string)
741    me_append_before_this_command ();
742
743  get_rest_of_line (0, &line);
744
745  for (i = 0; line[i] && !whitespace (line[i]); i++);
746  name = xmalloc (i + 1);
747  memcpy (name, line, i);
748  name[i] = 0;
749
750  def = delete_macro (name);
751
752  if (def)
753    {
754      free (def->source_file);
755      free (def->name);
756      free (def->body);
757
758      if (def->arglist)
759        {
760          int i;
761
762          for (i = 0; def->arglist[i]; i++)
763            free (def->arglist[i]);
764
765          free (def->arglist);
766        }
767
768      free (def);
769    }
770
771  free (line);
772  free (name);
773
774  if (macro_expansion_output_stream && !executing_string)
775    remember_itext (input_text, input_text_offset);
776}
777
778/* How to output sections of the input file verbatim. */
779
780/* Set the value of POINTER's offset to OFFSET. */
781ITEXT *
782remember_itext (pointer, offset)
783     char *pointer;
784     int offset;
785{
786  int i;
787  ITEXT *itext = NULL;
788
789  /* If we have no info, initialize a blank list. */
790  if (!itext_info)
791    {
792      itext_info = xmalloc ((itext_size = 10) * sizeof (ITEXT *));
793      for (i = 0; i < itext_size; i++)
794        itext_info[i] = NULL;
795    }
796
797  /* If the pointer is already present in the list, then set the offset. */
798  for (i = 0; i < itext_size; i++)
799    if ((itext_info[i]) &&
800        (itext_info[i]->pointer == pointer))
801      {
802        itext = itext_info[i];
803        itext_info[i]->offset = offset;
804        break;
805      }
806
807  if (i == itext_size)
808    {
809      /* Find a blank slot (or create a new one), and remember the
810         pointer and offset. */
811      for (i = 0; i < itext_size; i++)
812        if (itext_info[i] == NULL)
813          break;
814
815      /* If not found, then add some slots. */
816      if (i == itext_size)
817        {
818          int j;
819
820          itext_info = xrealloc
821            (itext_info, (itext_size += 10) * sizeof (ITEXT *));
822
823          for (j = i; j < itext_size; j++)
824            itext_info[j] = NULL;
825        }
826
827      /* Now add the pointer and the offset. */
828      itext_info[i] = xmalloc (sizeof (ITEXT));
829      itext_info[i]->pointer = pointer;
830      itext_info[i]->offset = offset;
831      itext = itext_info[i];
832    }
833  return itext;
834}
835
836/* Forget the input text associated with POINTER. */
837void
838forget_itext (pointer)
839     char *pointer;
840{
841  int i;
842
843  for (i = 0; i < itext_size; i++)
844    if (itext_info[i] && (itext_info[i]->pointer == pointer))
845      {
846        free (itext_info[i]);
847        itext_info[i] = NULL;
848        break;
849      }
850}
851
852/* Append the text which appeared in input_text from the last offset to
853   the character just before the command that we are currently executing. */
854void
855me_append_before_this_command ()
856{
857  int i;
858
859  for (i = input_text_offset; i && (input_text[i] != COMMAND_PREFIX); i--)
860    ;
861  maybe_write_itext (input_text, i);
862}
863
864/* Similar to execute_string, but only takes a single string argument,
865   and remembers the input text location, etc. */
866void
867me_execute_string (execution_string)
868     char *execution_string;
869{
870  int saved_escape_html = escape_html;
871  int saved_in_paragraph = in_paragraph;
872  escape_html = me_executing_string == 0;
873  in_paragraph = 0;
874
875  pushfile ();
876  input_text_offset = 0;
877  /* The following xstrdup is so we can relocate input_text at will.  */
878  input_text = xstrdup (execution_string);
879  input_filename = xstrdup (input_filename);
880  input_text_length = strlen (execution_string);
881
882  remember_itext (input_text, 0);
883
884  me_executing_string++;
885  reader_loop ();
886  free (input_text);
887  free (input_filename);
888  popfile ();
889  me_executing_string--;
890
891  in_paragraph = saved_in_paragraph;
892  escape_html = saved_escape_html;
893}
894
895/* A wrapper around me_execute_string which saves and restores
896   variables important for output generation.  This is called
897   when we need to produce macro-expanded output for input which
898   leaves no traces in the Info output.  */
899void
900me_execute_string_keep_state (execution_string, append_string)
901     char *execution_string, *append_string;
902{
903  int op_orig, opcol_orig, popen_orig;
904  int fill_orig, newline_orig, indent_orig, meta_pos_orig;
905
906  remember_itext (input_text, input_text_offset);
907  op_orig = output_paragraph_offset;
908  meta_pos_orig = meta_char_pos;
909  opcol_orig = output_column;
910  popen_orig = paragraph_is_open;
911  fill_orig = filling_enabled;
912  newline_orig = last_char_was_newline;
913  filling_enabled = 0;
914  indent_orig = no_indent;
915  no_indent = 1;
916  me_execute_string (execution_string);
917  if (append_string)
918    write_region_to_macro_output (append_string, 0, strlen (append_string));
919  output_paragraph_offset = op_orig;
920  meta_char_pos = meta_pos_orig;
921  output_column = opcol_orig;
922  paragraph_is_open = popen_orig;
923  filling_enabled = fill_orig;
924  last_char_was_newline = newline_orig;
925  no_indent = indent_orig;
926}
927
928/* Append the text which appears in input_text from the last offset to
929   the current OFFSET. */
930void
931append_to_expansion_output (offset)
932     int offset;
933{
934  int i;
935  ITEXT *itext = NULL;
936
937  for (i = 0; i < itext_size; i++)
938    if (itext_info[i] && itext_info[i]->pointer == input_text)
939      {
940        itext = itext_info[i];
941        break;
942      }
943
944  if (!itext)
945    return;
946
947  if (offset > itext->offset)
948    {
949      write_region_to_macro_output (input_text, itext->offset, offset);
950      remember_itext (input_text, offset);
951    }
952}
953
954/* Only write this input text iff it appears in our itext list. */
955void
956maybe_write_itext (pointer, offset)
957     char *pointer;
958     int offset;
959{
960  int i;
961  ITEXT *itext = NULL;
962
963  for (i = 0; i < itext_size; i++)
964    if (itext_info[i] && (itext_info[i]->pointer == pointer))
965      {
966        itext = itext_info[i];
967        break;
968      }
969
970  if (itext && (itext->offset < offset))
971    {
972      write_region_to_macro_output (itext->pointer, itext->offset, offset);
973      remember_itext (pointer, offset);
974    }
975}
976
977void
978write_region_to_macro_output (string, start, end)
979     char *string;
980     int start, end;
981{
982  if (macro_expansion_output_stream)
983    fwrite (string + start, 1, end - start, macro_expansion_output_stream);
984}
985
986/* Aliases. */
987
988typedef struct alias_struct
989{
990  char *alias;
991  char *mapto;
992  struct alias_struct *next;
993} alias_type;
994
995static alias_type *aliases;
996
997/* @alias */
998void
999cm_alias ()
1000{
1001  alias_type *a = xmalloc (sizeof (alias_type));
1002
1003  skip_whitespace ();
1004  get_until_in_line (1, "=", &(a->alias));
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