macro.c revision 116525
1189251Ssam/* macro.c -- user-defined macros for Texinfo.
2189251Ssam   $Id: macro.c,v 1.2 2003/06/01 23:41:23 karl Exp $
3189251Ssam
4189251Ssam   Copyright (C) 1998, 1999, 2002, 2003 Free Software Foundation, Inc.
5252726Srpaulo
6252726Srpaulo   This program is free software; you can redistribute it and/or modify
7189251Ssam   it under the terms of the GNU General Public License as published by
8189251Ssam   the Free Software Foundation; either version 2, or (at your option)
9189251Ssam   any later version.
10189251Ssam
11189251Ssam   This program is distributed in the hope that it will be useful,
12189251Ssam   but WITHOUT ANY WARRANTY; without even the implied warranty of
13214734Srpaulo   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14189251Ssam   GNU General Public License for more details.
15189251Ssam
16189251Ssam   You should have received a copy of the GNU General Public License
17189251Ssam   along with this program; if not, write to the Free Software Foundation,
18189251Ssam   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19214734Srpaulo
20189251Ssam#include "system.h"
21189251Ssam#include "cmds.h"
22189251Ssam#include "macro.h"
23189251Ssam#include "makeinfo.h"
24189251Ssam#include "insertion.h"
25189251Ssam
26189251Ssam/* If non-NULL, this is an output stream to write the full macro expansion
27189251Ssam   of the input text to.  The result is another texinfo file, but
28189251Ssam   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, 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          if (len == 0)
340            { /* \\ always means \, even if macro has no args.  */
341              len++;
342              text = xmalloc (1 + len);
343              sprintf (text, "\\%s", param);
344            }
345          else
346            {
347              int which;
348
349              /* Check against named parameters. */
350              for (which = 0; named && named[which]; which++)
351                if (STREQ (named[which], param))
352                  break;
353
354              if (named && named[which])
355                {
356                  text = which < length_of_actuals ? actuals[which] : NULL;
357                  if (!text)
358                    text = "";
359                  len = strlen (text);
360                  text = xstrdup (text);  /* so we can free it */
361                }
362              else
363                { /* not a parameter, so it's an error.  */
364                  warning (_("\\ in macro expansion followed by `%s' instead of parameter name"),
365                             param);
366                  len++;
367                  text = xmalloc (1 + len);
368                  sprintf (text, "\\%s", param);
369                }
370            }
371
372          if (strlen (param) + 2 < len)
373            {
374              new_body_size += len + 1;
375              new_body = xrealloc (new_body, new_body_size);
376            }
377
378          free (param);
379
380          strcpy (new_body + new_body_index, text);
381          new_body_index += len;
382
383          free (text);
384        }
385    }
386
387  new_body[new_body_index] = 0;
388  return new_body;
389}
390
391/* Expand macro passed in DEF, a pointer to a MACRO_DEF, and
392   return its expansion as a string.  */
393char *
394expand_macro (def)
395     MACRO_DEF *def;
396{
397  char **arglist;
398  int num_args;
399  char *execution_string = NULL;
400  int start_line = line_number;
401
402  /* Find out how many arguments this macro definition takes. */
403  num_args = array_len (def->arglist);
404
405  /* Gather the arguments present on the line if there are any. */
406  arglist = get_macro_args (def);
407
408  if (num_args < array_len (arglist))
409    {
410      free_array (arglist);
411      line_error (_("Macro `%s' called on line %d with too many args"),
412                  def->name, start_line);
413      return execution_string;
414    }
415
416  if (def->body)
417    execution_string = apply (def->arglist, arglist, def->body);
418
419  free_array (arglist);
420  return execution_string;
421}
422
423/* Execute the macro passed in DEF, a pointer to a MACRO_DEF.  */
424void
425execute_macro (def)
426     MACRO_DEF *def;
427{
428  char *execution_string;
429  int start_line = line_number, end_line;
430
431  if (macro_expansion_output_stream && !executing_string && !me_inhibit_expansion)
432    me_append_before_this_command ();
433
434  execution_string = expand_macro (def);
435  if (!execution_string)
436    return;
437
438  if (def->body)
439    {
440      /* Reset the line number to where the macro arguments began.
441         This makes line numbers reported in error messages correct in
442         case the macro arguments span several lines and the expanded
443         arguments invoke other commands.  */
444      end_line = line_number;
445      line_number = start_line;
446
447      if (macro_expansion_output_stream && !executing_string && !me_inhibit_expansion)
448        {
449          remember_itext (input_text, input_text_offset);
450          me_execute_string (execution_string);
451        }
452      else
453        execute_string ("%s", execution_string);
454
455      free (execution_string);
456      line_number = end_line;
457    }
458}
459
460
461/* Read and remember the definition of a macro.  If RECURSIVE is set,
462   set the ME_RECURSE flag.  MACTYPE is either "macro" or "rmacro", and
463   tells us what the matching @end should be.  */
464static void
465define_macro (mactype, recursive)
466     char *mactype;
467     int recursive;
468{
469  int i;
470  char *name, **arglist, *body, *line, *last_end;
471  int body_size, body_index;
472  int depth = 1;
473  int defining_line = line_number;
474  int flags = 0;
475
476  arglist = NULL;
477  body = NULL;
478  body_size = 0;
479  body_index = 0;
480
481  if (macro_expansion_output_stream && !executing_string)
482    me_append_before_this_command ();
483
484  skip_whitespace ();
485
486  /* Get the name of the macro.  This is the set of characters which are
487     not whitespace and are not `{' immediately following the @macro. */
488  {
489    int start = input_text_offset;
490    int len;
491
492    for (i = start;
493         (i < input_text_length) &&
494         (input_text[i] != '{') &&
495         (!cr_or_whitespace (input_text[i]));
496         i++);
497
498    len = i - start;
499    name = xmalloc (1 + len);
500    memcpy (name, input_text + start, len);
501    name[len] = 0;
502    input_text_offset = i;
503  }
504
505  skip_whitespace ();
506
507  /* It is not required that the definition of a macro includes an arglist.
508     If not, don't try to get the named parameters, just use a null list. */
509  if (curchar () == '{')
510    {
511      int character;
512      int arglist_index = 0, arglist_size = 0;
513      int gathering_words = 1;
514      char *word = NULL;
515
516      /* Read the words inside of the braces which determine the arglist.
517         These words will be replaced within the body of the macro at
518         execution time. */
519
520      input_text_offset++;
521      skip_whitespace_and_newlines ();
522
523      while (gathering_words)
524        {
525          int len;
526
527          for (i = input_text_offset;
528               (character = input_text[i]);
529               i++)
530            {
531              switch (character)
532                {
533                case '\n':
534                  line_number++;
535                case ' ':
536                case '\t':
537                case ',':
538                case '}':
539                  /* Found the end of the current arglist word.  Save it. */
540                  len = i - input_text_offset;
541                  word = xmalloc (1 + len);
542                  memcpy (word, input_text + input_text_offset, len);
543                  word[len] = 0;
544                  input_text_offset = i;
545
546                  /* Advance to the comma or close-brace that signified
547                     the end of the argument. */
548                  while ((character = curchar ())
549                         && character != ','
550                         && character != '}')
551                    {
552                      input_text_offset++;
553                      if (character == '\n')
554                        line_number++;
555                    }
556
557                  /* Add the word to our list of words. */
558                  if (arglist_index + 2 >= arglist_size)
559                    {
560                      arglist_size += 10;
561                      arglist = xrealloc (arglist,
562                                          arglist_size * sizeof (char *));
563                    }
564
565                  arglist[arglist_index++] = word;
566                  arglist[arglist_index] = NULL;
567                  break;
568                }
569
570              if (character == '}')
571                {
572                  input_text_offset++;
573                  gathering_words = 0;
574                  break;
575                }
576
577              if (character == ',')
578                {
579                  input_text_offset++;
580                  skip_whitespace_and_newlines ();
581                  i = input_text_offset - 1;
582                }
583            }
584        }
585
586      /* If we have exactly one argument, do @quote-arg implicitly.  Not
587         only does this match TeX's behavior (which can't feasibly be
588         changed), but it's a good idea.  */
589      if (arglist_index == 1)
590        flags |= ME_QUOTE_ARG;
591    }
592
593  /* Read the text carefully until we find an "@end macro" which
594     matches this one.  The text in between is the body of the macro. */
595  skip_whitespace_and_newlines ();
596
597  while (depth)
598    {
599      if ((input_text_offset + 9) > input_text_length)
600        {
601          file_line_error (input_filename, defining_line,
602			   _("%cend macro not found"), COMMAND_PREFIX);
603          return;
604        }
605
606      get_rest_of_line (0, &line);
607
608      /* Handle commands only meaningful within a macro. */
609      if ((*line == COMMAND_PREFIX) && (depth == 1) &&
610          (strncmp (line + 1, "allow-recursion", 15) == 0) &&
611          (line[16] == 0 || whitespace (line[16])))
612        {
613          for (i = 16; whitespace (line[i]); i++);
614          strcpy (line, line + i);
615          flags |= ME_RECURSE;
616          if (!*line)
617            {
618              free (line);
619              continue;
620            }
621        }
622
623      if ((*line == COMMAND_PREFIX) && (depth == 1) &&
624          (strncmp (line + 1, "quote-arg", 9) == 0) &&
625          (line[10] == 0 || whitespace (line[10])))
626        {
627          for (i = 10; whitespace (line[i]); i++);
628          strcpy (line, line + i);
629
630          if (arglist && arglist[0] && !arglist[1])
631            {
632              flags |= ME_QUOTE_ARG;
633              if (!*line)
634                {
635                  free (line);
636                  continue;
637                }
638            }
639          else
640           line_error (_("@quote-arg only useful for single-argument macros"));
641        }
642
643      if (*line == COMMAND_PREFIX
644          && (strncmp (line + 1, "macro ", 6) == 0
645              || strncmp (line + 1, "rmacro ", 7) == 0))
646        depth++;
647
648      /* Incorrect implementation of nesting -- just check that the last
649         @end matches what we started with.  Since nested macros don't
650         work in TeX anyway, this isn't worth the trouble to get right.  */
651      if (*line == COMMAND_PREFIX && strncmp (line + 1, "end macro", 9) == 0)
652        {
653          depth--;
654          last_end = "macro";
655        }
656      if (*line == COMMAND_PREFIX && strncmp (line + 1, "end rmacro", 9) == 0)
657        {
658          depth--;
659          last_end = "rmacro";
660        }
661
662      if (depth)
663        {
664          if ((body_index + strlen (line) + 3) >= body_size)
665            body = xrealloc (body, body_size += 3 + strlen (line));
666          strcpy (body + body_index, line);
667          body_index += strlen (line);
668          body[body_index++] = '\n';
669          body[body_index] = 0;
670        }
671      free (line);
672    }
673
674  /* Check that @end matched the macro command.  */
675  if (!STREQ (last_end, mactype))
676    warning (_("mismatched @end %s with @%s"), last_end, mactype);
677
678  /* If it was an empty macro like
679     @macro foo
680     @end macro
681     create an empty body.  (Otherwise, the macro is not expanded.)  */
682  if (!body)
683    {
684      body = (char *)malloc(1);
685      *body = 0;
686    }
687
688  /* We now have the name, the arglist, and the body.  However, BODY
689     includes the final newline which preceded the `@end macro' text.
690     Delete it. */
691  if (body && strlen (body))
692    body[strlen (body) - 1] = 0;
693
694  if (recursive)
695    flags |= ME_RECURSE;
696
697  add_macro (name, arglist, body, input_filename, defining_line, flags);
698
699  if (macro_expansion_output_stream && !executing_string)
700    remember_itext (input_text, input_text_offset);
701}
702
703void
704cm_macro ()
705{
706  define_macro ("macro", 0);
707}
708
709void
710cm_rmacro ()
711{
712  define_macro ("rmacro", 1);
713}
714
715/* Delete the macro with name NAME.  The macro is deleted from the list,
716   but it is also returned.  If there was no macro defined, NULL is
717   returned. */
718
719static MACRO_DEF *
720delete_macro (name)
721     char *name;
722{
723  int i;
724  MACRO_DEF *def;
725
726  def = NULL;
727
728  for (i = 0; macro_list && (def = macro_list[i]); i++)
729    if (strcmp (def->name, name) == 0)
730      {
731        memmove (macro_list + i, macro_list + i + 1,
732               ((macro_list_len + 1) - i) * sizeof (MACRO_DEF *));
733        macro_list_len--;
734        break;
735      }
736  return def;
737}
738
739void
740cm_unmacro ()
741{
742  int i;
743  char *line, *name;
744  MACRO_DEF *def;
745
746  if (macro_expansion_output_stream && !executing_string)
747    me_append_before_this_command ();
748
749  get_rest_of_line (0, &line);
750
751  for (i = 0; line[i] && !whitespace (line[i]); i++);
752  name = xmalloc (i + 1);
753  memcpy (name, line, i);
754  name[i] = 0;
755
756  def = delete_macro (name);
757
758  if (def)
759    {
760      free (def->source_file);
761      free (def->name);
762      free (def->body);
763
764      if (def->arglist)
765        {
766          int i;
767
768          for (i = 0; def->arglist[i]; i++)
769            free (def->arglist[i]);
770
771          free (def->arglist);
772        }
773
774      free (def);
775    }
776
777  free (line);
778  free (name);
779
780  if (macro_expansion_output_stream && !executing_string)
781    remember_itext (input_text, input_text_offset);
782}
783
784/* How to output sections of the input file verbatim. */
785
786/* Set the value of POINTER's offset to OFFSET. */
787ITEXT *
788remember_itext (pointer, offset)
789     char *pointer;
790     int offset;
791{
792  int i;
793  ITEXT *itext = NULL;
794
795  /* If we have no info, initialize a blank list. */
796  if (!itext_info)
797    {
798      itext_info = xmalloc ((itext_size = 10) * sizeof (ITEXT *));
799      for (i = 0; i < itext_size; i++)
800        itext_info[i] = NULL;
801    }
802
803  /* If the pointer is already present in the list, then set the offset. */
804  for (i = 0; i < itext_size; i++)
805    if ((itext_info[i]) &&
806        (itext_info[i]->pointer == pointer))
807      {
808        itext = itext_info[i];
809        itext_info[i]->offset = offset;
810        break;
811      }
812
813  if (i == itext_size)
814    {
815      /* Find a blank slot (or create a new one), and remember the
816         pointer and offset. */
817      for (i = 0; i < itext_size; i++)
818        if (itext_info[i] == NULL)
819          break;
820
821      /* If not found, then add some slots. */
822      if (i == itext_size)
823        {
824          int j;
825
826          itext_info = xrealloc
827            (itext_info, (itext_size += 10) * sizeof (ITEXT *));
828
829          for (j = i; j < itext_size; j++)
830            itext_info[j] = NULL;
831        }
832
833      /* Now add the pointer and the offset. */
834      itext_info[i] = xmalloc (sizeof (ITEXT));
835      itext_info[i]->pointer = pointer;
836      itext_info[i]->offset = offset;
837      itext = itext_info[i];
838    }
839  return itext;
840}
841
842/* Forget the input text associated with POINTER. */
843void
844forget_itext (pointer)
845     char *pointer;
846{
847  int i;
848
849  for (i = 0; i < itext_size; i++)
850    if (itext_info[i] && (itext_info[i]->pointer == pointer))
851      {
852        free (itext_info[i]);
853        itext_info[i] = NULL;
854        break;
855      }
856}
857
858/* Append the text which appeared in input_text from the last offset to
859   the character just before the command that we are currently executing. */
860void
861me_append_before_this_command ()
862{
863  int i;
864
865  for (i = input_text_offset; i && (input_text[i] != COMMAND_PREFIX); i--)
866    ;
867  maybe_write_itext (input_text, i);
868}
869
870/* Similar to execute_string, but only takes a single string argument,
871   and remembers the input text location, etc. */
872void
873me_execute_string (execution_string)
874     char *execution_string;
875{
876  int saved_escape_html = escape_html;
877  int saved_in_paragraph = in_paragraph;
878  escape_html = me_executing_string == 0;
879  in_paragraph = 0;
880
881  pushfile ();
882  input_text_offset = 0;
883  /* The following xstrdup is so we can relocate input_text at will.  */
884  input_text = xstrdup (execution_string);
885  input_filename = xstrdup (input_filename);
886  input_text_length = strlen (execution_string);
887
888  remember_itext (input_text, 0);
889
890  me_executing_string++;
891  reader_loop ();
892  free (input_text);
893  free (input_filename);
894  popfile ();
895  me_executing_string--;
896
897  in_paragraph = saved_in_paragraph;
898  escape_html = saved_escape_html;
899}
900
901/* A wrapper around me_execute_string which saves and restores
902   variables important for output generation.  This is called
903   when we need to produce macro-expanded output for input which
904   leaves no traces in the Info output.  */
905void
906me_execute_string_keep_state (execution_string, append_string)
907     char *execution_string, *append_string;
908{
909  int op_orig, opcol_orig, popen_orig;
910  int fill_orig, newline_orig, indent_orig, meta_pos_orig;
911
912  remember_itext (input_text, input_text_offset);
913  op_orig = output_paragraph_offset;
914  meta_pos_orig = meta_char_pos;
915  opcol_orig = output_column;
916  popen_orig = paragraph_is_open;
917  fill_orig = filling_enabled;
918  newline_orig = last_char_was_newline;
919  filling_enabled = 0;
920  indent_orig = no_indent;
921  no_indent = 1;
922  me_execute_string (execution_string);
923  if (append_string)
924    write_region_to_macro_output (append_string, 0, strlen (append_string));
925  output_paragraph_offset = op_orig;
926  meta_char_pos = meta_pos_orig;
927  output_column = opcol_orig;
928  paragraph_is_open = popen_orig;
929  filling_enabled = fill_orig;
930  last_char_was_newline = newline_orig;
931  no_indent = indent_orig;
932}
933
934/* Append the text which appears in input_text from the last offset to
935   the current OFFSET. */
936void
937append_to_expansion_output (offset)
938     int offset;
939{
940  int i;
941  ITEXT *itext = NULL;
942
943  for (i = 0; i < itext_size; i++)
944    if (itext_info[i] && itext_info[i]->pointer == input_text)
945      {
946        itext = itext_info[i];
947        break;
948      }
949
950  if (!itext)
951    return;
952
953  if (offset > itext->offset)
954    {
955      write_region_to_macro_output (input_text, itext->offset, offset);
956      remember_itext (input_text, offset);
957    }
958}
959
960/* Only write this input text iff it appears in our itext list. */
961void
962maybe_write_itext (pointer, offset)
963     char *pointer;
964     int offset;
965{
966  int i;
967  ITEXT *itext = NULL;
968
969  for (i = 0; i < itext_size; i++)
970    if (itext_info[i] && (itext_info[i]->pointer == pointer))
971      {
972        itext = itext_info[i];
973        break;
974      }
975
976  if (itext && (itext->offset < offset))
977    {
978      write_region_to_macro_output (itext->pointer, itext->offset, offset);
979      remember_itext (pointer, offset);
980    }
981}
982
983void
984write_region_to_macro_output (string, start, end)
985     char *string;
986     int start, end;
987{
988  if (macro_expansion_output_stream)
989    fwrite (string + start, 1, end - start, macro_expansion_output_stream);
990}
991
992/* Aliases. */
993
994typedef struct alias_struct
995{
996  char *alias;
997  char *mapto;
998  struct alias_struct *next;
999} alias_type;
1000
1001static alias_type *aliases;
1002
1003/* @alias */
1004void
1005cm_alias ()
1006{
1007  alias_type *a = xmalloc (sizeof (alias_type));
1008
1009  skip_whitespace ();
1010  get_until_in_line (1, "=", &(a->alias));
1011  canon_white (a->alias);
1012
1013  discard_until ("=");
1014  skip_whitespace ();
1015  get_until_in_line (0, " ", &(a->mapto));
1016
1017  a->next = aliases;
1018  aliases = a;
1019}
1020
1021/* Perform an alias expansion.  Called from read_command.  */
1022char *
1023alias_expand (tok)
1024     char *tok;
1025{
1026  alias_type *findit = aliases;
1027
1028  while (findit)
1029    if (strcmp (findit->alias, tok) == 0)
1030      {
1031	free (tok);
1032	return alias_expand (xstrdup (findit->mapto));
1033      }
1034    else
1035      findit = findit->next;
1036
1037  return tok;
1038}
1039
1040/* definfoenclose implementation.  */
1041
1042/* This structure is used to track enclosure macros.  When an enclosure
1043   macro is recognized, a pointer to the enclosure block corresponding
1044   to its name is saved in the brace element for its argument. */
1045typedef struct enclose_struct
1046{
1047  char *enclose;
1048  char *before;
1049  char *after;
1050  struct enclose_struct *next;
1051} enclosure_type;
1052
1053static enclosure_type *enclosures;
1054
1055typedef struct enclosure_stack_struct
1056{
1057    enclosure_type *current;
1058    struct enclosure_stack_struct *next;
1059} enclosure_stack_type;
1060
1061static enclosure_stack_type *enclosure_stack;
1062
1063/* @definfoenclose */
1064void
1065cm_definfoenclose ()
1066{
1067  enclosure_type *e = xmalloc (sizeof (enclosure_type));
1068
1069  skip_whitespace ();
1070  get_until_in_line (1, ",", &(e->enclose));
1071  discard_until (",");
1072  get_until_in_line (0, ",", &(e->before));
1073  discard_until (",");
1074  get_until_in_line (0, "\n", &(e->after));
1075
1076  e->next = enclosures;
1077  enclosures = e;
1078}
1079
1080/* If TOK is an enclosure command, push it on the enclosure stack and
1081   return 1.  Else return 0.  */
1082
1083int
1084enclosure_command (tok)
1085     char *tok;
1086{
1087  enclosure_type *findit = enclosures;
1088
1089  while (findit)
1090    if (strcmp (findit->enclose, tok) == 0)
1091      {
1092        enclosure_stack_type *new = xmalloc (sizeof (enclosure_stack_type));
1093        new->current = findit;
1094        new->next = enclosure_stack;
1095        enclosure_stack = new;
1096
1097        return 1;
1098      }
1099    else
1100      findit = findit->next;
1101
1102  return 0;
1103}
1104
1105/* actually perform the enclosure expansion */
1106void
1107enclosure_expand (arg, start, end)
1108     int arg, start, end;
1109{
1110  if (arg == START)
1111    add_word (enclosure_stack->current->before);
1112  else
1113    {
1114      enclosure_stack_type *temp;
1115
1116      add_word (enclosure_stack->current->after);
1117
1118      temp = enclosure_stack;
1119      enclosure_stack = enclosure_stack->next;
1120      free (temp);
1121    }
1122}
1123