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