(root)/
texinfo-7.1/
tp/
Texinfo/
XS/
parsetexi/
macro.c
       1  /* Copyright 2010-2023 Free Software Foundation, Inc.
       2  
       3     This program is free software: you can redistribute it and/or modify
       4     it under the terms of the GNU General Public License as published by
       5     the Free Software Foundation, either version 3 of the License, or
       6     (at your option) any later version.
       7  
       8     This program is distributed in the hope that it will be useful,
       9     but WITHOUT ANY WARRANTY; without even the implied warranty of
      10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      11     GNU General Public License for more details.
      12  
      13     You should have received a copy of the GNU General Public License
      14     along with this program.  If not, see <http://www.gnu.org/licenses/>. */
      15  
      16  #include <config.h>
      17  #include <stdlib.h>
      18  #include <string.h>
      19  #include <ctype.h>
      20  #include <stdio.h>
      21  
      22  #include "parser.h"
      23  #include "debug.h"
      24  #include "tree.h"
      25  #include "text.h"
      26  #include "input.h"
      27  #include "convert.h"
      28  #include "source_marks.h"
      29  #include "macro.h"
      30  
      31  COUNTER count_toplevel_braces;
      32  
      33  static MACRO *macro_list;
      34  static size_t macro_number;
      35  static size_t macro_space;
      36  
      37  
      38  /* Macro definition. */
      39  
      40  MACRO *
      41  lookup_macro_and_slot (enum command_id cmd, size_t *free_slot)
      42  {
      43    int i;
      44    if (free_slot)
      45      *free_slot = 0;
      46  
      47    for (i = 0; i < macro_number; i++)
      48      {
      49        if (macro_list[i].cmd == cmd)
      50          return &macro_list[i];
      51        if (free_slot && !*free_slot && macro_list[i].cmd == 0)
      52          *free_slot = i;
      53      }
      54    return 0;
      55  }
      56  
      57  void
      58  new_macro (char *name, ELEMENT *macro)
      59  {
      60    enum command_id new;
      61    MACRO *m = 0;
      62    size_t free_slot = 0;
      63  
      64    /* Check for an existing definition first for us to overwrite. */
      65    new = lookup_command (name);
      66    if (new)
      67      m = lookup_macro_and_slot (new, &free_slot);
      68    if (!m)
      69      {
      70        size_t macro_index;
      71        if (free_slot)
      72          macro_index = free_slot;
      73        else
      74          {
      75            if (macro_number == macro_space)
      76              {
      77                macro_list = realloc (macro_list,
      78                                (macro_space += 5) * sizeof (MACRO));
      79                if (!macro_list)
      80                  fatal ("realloc failed");
      81              }
      82            macro_index = macro_number;
      83            macro_number++;
      84          }
      85        new = add_texinfo_command (name);
      86        m = &macro_list[macro_index];
      87        m->cmd = new;
      88        new &= ~USER_COMMAND_BIT;
      89        user_defined_command_data[new].flags |= CF_MACRO;
      90      }
      91    else
      92      {
      93        free (m->macro_name);
      94      }
      95  
      96    m->macro_name = strdup (name);
      97    m->element = macro;
      98  
      99    m->macrobody = convert_contents_to_texinfo (macro);
     100  }
     101  
     102  /* CMD will be either CM_macro or CM_rmacro.  Read the line defining a macro's 
     103     name and the arguments it takes, and return this information in a new 
     104     ELEMENT. */
     105  ELEMENT *
     106  parse_macro_command_line (enum command_id cmd, char **line_inout,
     107                            ELEMENT *parent)
     108  {
     109    char *line = *line_inout;
     110    ELEMENT *macro, *macro_name;
     111    char *name, *args_ptr;
     112    int index;
     113  
     114    macro = new_element (ET_NONE);
     115    macro->cmd = cmd;
     116    macro->source_info = current_source_info;
     117  
     118    add_info_string_dup (macro, "arg_line", line);
     119  
     120    line += strspn (line, whitespace_chars);
     121    name = read_command_name (&line);
     122  
     123    if (!name)
     124      {
     125        line_error ("@%s requires a name", command_name (cmd));
     126        add_extra_integer (macro, "invalid_syntax", 1);
     127        return macro;
     128      }
     129  
     130    if (*line && *line != '{' && *line != '@'
     131        && !strchr (whitespace_chars, *line))
     132      {
     133        line_error ("bad name for @%s", command_name (cmd));
     134        add_extra_integer (macro, "invalid_syntax", 1);
     135        free (name);
     136        return macro;
     137      }
     138  
     139    debug ("MACRO @%s %s", command_name (cmd), name);
     140  
     141    macro_name = new_element (ET_macro_name);
     142    text_append (&macro_name->text, name);
     143    free (name);
     144    add_to_element_args (macro, macro_name);
     145  
     146    args_ptr = line;
     147    args_ptr += strspn (args_ptr, whitespace_chars);
     148  
     149    if (*args_ptr != '{')
     150      {
     151        /* Either error or no args. */
     152        goto check_trailing;
     153      }
     154    args_ptr++;
     155  
     156    index = 0;
     157    while (1)
     158      {
     159        /* args_ptr is after a '{' or ','.  INDEX holds the number of
     160           the macro argument */
     161  
     162        char *q, *q2;
     163        ELEMENT *arg;
     164  
     165        args_ptr += strspn (args_ptr, whitespace_chars);
     166  
     167        /* Find end of current argument. */
     168        q = args_ptr;
     169        while (*q != '\0' && *q != ',' && *q != '}')
     170          q++;
     171  
     172        if (!*q)
     173          {
     174            /* End of string reached before closing brace. */
     175            goto check_trailing;
     176          }
     177  
     178        /* Disregard trailing whitespace. */
     179        q2 = q;
     180        while (q2 > args_ptr && strchr (whitespace_chars, q2[-1]))
     181          q2--;
     182  
     183        if (q2 == args_ptr)
     184          {
     185            /* argument is completely whitespace */
     186            if (*q == ',')
     187              {
     188                line_error ("bad or empty @%s formal argument: ",
     189                            command_name(cmd));
     190                arg = new_element (ET_macro_arg);
     191                add_to_element_args (macro, arg);
     192                text_append_n (&arg->text, "", 0);
     193                add_extra_integer (macro, "invalid_syntax", 1);
     194              }
     195          }
     196        else
     197          {
     198            arg = new_element (ET_macro_arg);
     199            text_append_n (&arg->text, args_ptr, q2 - args_ptr);
     200            add_to_element_args (macro, arg);
     201  
     202            /* Check the argument name. */
     203              {
     204                char *p;
     205                for (p = args_ptr; p < q2; p++)
     206                  {
     207                    if (!isascii_alnum (*p) && *p != '_' && *p != '-')
     208                      {
     209                        char saved = *q2; *q2 = 0;
     210                        line_error ("bad or empty @%s formal argument: %s",
     211                                    command_name(cmd), args_ptr);
     212                        *q2 = saved;
     213                        add_extra_integer (macro, "invalid_syntax", 1);
     214                        break;
     215                      }
     216                  }
     217              }
     218          }
     219  
     220        args_ptr = q + 1;
     221  
     222        if (*q == '}')
     223          break;
     224  
     225        index++;
     226      }
     227  
     228   check_trailing:
     229    line = args_ptr;
     230    line += strspn (line, whitespace_chars);
     231    if (*line && *line != '@')
     232      {
     233        char *argument_str = strdup (line);
     234        /* remove new line for the message */
     235        char *end_line = strchr (argument_str, '\n');
     236  
     237        if (end_line)
     238              *end_line = '\0';
     239        line_error ("bad syntax for @%s argument: %s",
     240                    command_name(cmd), argument_str);
     241        free (argument_str);
     242        add_extra_integer (macro, "invalid_syntax", 1);
     243      }
     244    //line += strlen (line); /* Discard rest of line. */
     245  
     246    *line_inout = line;
     247    return macro;
     248  }
     249  
     250  
     251  /* Macro use. */
     252  
     253  /* Return index into given arguments to look for the value of NAME.
     254     Return -1 if not found. */
     255  
     256  int
     257  lookup_macro_parameter (char *name, ELEMENT *macro)
     258  {
     259    int i, pos;
     260    ELEMENT **args;
     261    
     262    /* Find 'arg' in MACRO parameters. */
     263    args = macro->args.list;
     264    pos = 0;
     265    for (i = 0; i < macro->args.number; i++)
     266      {
     267        if (args[i]->type == ET_macro_arg)
     268          {
     269            if (!strcmp (args[i]->text.text, name))
     270              return pos;
     271            pos++;
     272          }
     273      }
     274    return -1;
     275  }
     276  
     277  /* not done by _close_container as argument is in args and not in
     278     contents. */
     279  /* Currently unused */
     280  ELEMENT *
     281  remove_empty_arg (ELEMENT *argument)
     282  {
     283    ELEMENT *current = close_container (argument);
     284    if (is_container_empty (argument)
     285        && argument->source_mark_list.number == 0)
     286      {
     287        ELEMENT *last_child = last_args_child (current);
     288        if (last_child == argument)
     289          destroy_element (pop_element_from_args (current));
     290      }
     291    return current;
     292  }
     293  
     294  /* LINE points the opening brace in a macro invocation.  CMD is the command
     295     identifier of the macro command.  Return array of the arguments.  Return
     296     value to be freed by caller.  */
     297  void
     298  expand_macro_arguments (ELEMENT *macro, char **line_inout, enum command_id cmd,
     299                          ELEMENT *current)
     300  {
     301    char *line = *line_inout;
     302    char *pline = line;
     303    TEXT *arg;
     304    int braces_level = 1;
     305    int args_total;
     306    int whitespaces_len;
     307    ELEMENT *argument = new_element (ET_brace_command_arg);
     308    ELEMENT *argument_content = new_element (ET_NONE);
     309  
     310    add_to_element_args (current, argument);
     311    text_append_n (&argument_content->text, "", 0);
     312    add_to_element_contents (argument, argument_content);
     313    arg = &(argument_content->text);
     314  
     315    args_total = macro->args.number - 1;
     316  
     317    /* *pline is '{', advance past the open brace, start at braces_level = 1 */
     318    pline++;
     319    whitespaces_len = strspn (pline, whitespace_chars);
     320    if (whitespaces_len > 0)
     321      {
     322        ELEMENT *spaces_element = new_element (ET_NONE);
     323        text_append_n (&spaces_element->text, pline, whitespaces_len);
     324        add_info_element_oot (current, "spaces_before_argument",
     325                              spaces_element);
     326        pline += whitespaces_len;
     327      }
     328  
     329    while (braces_level > 0)
     330      {
     331        /* At the beginning of this loop pline is at the start
     332           of an argument. */
     333        char *sep;
     334  
     335        sep = pline + strcspn (pline, "\\,{}");
     336        if (!*sep)
     337          {
     338            debug ("MACRO ARG end of line");
     339            text_append (arg, pline);
     340            line = new_line (argument);
     341            if (!line)
     342              {
     343                line_error ("@%s missing closing brace", command_name (cmd));
     344                remove_empty_content (argument);
     345                line = "\n";
     346                goto funexit;
     347              }
     348            pline = line;
     349            continue;
     350          }
     351  
     352        text_append_n (arg, pline, sep - pline);
     353  
     354        switch (*sep)
     355          {
     356          case '\\':
     357            if (!strchr ("\\{},", sep[1]))
     358              text_append_n (arg, sep, 1);
     359            if (sep[1])
     360              {
     361                text_append_n (arg, &sep[1], 1);
     362                pline = sep + 2;
     363                if (sep[1] == ',')
     364                  line_warn ("use %s instead of %s in macro arg",
     365                             "@comma{}", "\\,");
     366              }
     367            else
     368              pline = sep + 1;
     369            break;
     370          case '{':
     371            braces_level++;
     372            text_append_n (arg, sep, 1);
     373            pline = sep + 1;
     374            break;
     375          case '}':
     376            braces_level--;
     377            if (braces_level > 0)
     378              text_append_n (arg, sep, 1);
     379            else
     380              /* end of arguments */
     381              remove_empty_content (argument);
     382            pline = sep + 1;
     383            break;
     384          case ',':
     385            pline = sep + 1;
     386            if (braces_level > 1)
     387              text_append_n (arg, sep, 1);
     388            else
     389              {
     390                if (current->args.number < args_total)
     391                  {
     392                    char *p = pline;
     393  
     394                    remove_empty_content (argument);
     395  
     396                    /* new argument */
     397                    argument = new_element (ET_brace_command_arg);
     398                    argument_content = new_element (ET_NONE);
     399                    add_to_element_args (current, argument);
     400                    text_append_n (&argument_content->text, "", 0);
     401                    add_to_element_contents (argument, argument_content);
     402                    arg = &(argument_content->text);
     403                    pline += strspn (pline, whitespace_chars);
     404                    if (pline - p)
     405                      {
     406                        ELEMENT *spaces_element = new_element (ET_NONE);
     407                        text_append_n (&spaces_element->text, p, pline - p);
     408                        add_info_element_oot (argument, "spaces_before_argument",
     409                                              spaces_element);
     410                      }
     411                    debug ("MACRO NEW ARG");
     412                  }
     413                else
     414                  /* too many args */
     415                  {
     416                    if (args_total != 1)
     417                      line_error ("macro `%s' called with too many args",
     418                                  command_name(cmd));
     419                    text_append_n (arg, sep, 1);
     420                  }
     421              }
     422            break;
     423          }
     424      }
     425  
     426    line = pline;
     427  
     428    if (args_total == 0
     429        && (current->args.number > 1
     430            || current->args.list[0]->contents.number > 0))
     431      {
     432        line_error
     433          ("macro `%s' declared without argument called with an argument",
     434           command_name(cmd));
     435      }
     436    debug ("END MACRO ARGS EXPANSION");
     437  
     438  funexit:
     439    *line_inout = line;
     440  }
     441  
     442  void
     443  set_toplevel_braces_nr (COUNTER *counter, ELEMENT* element)
     444  {
     445    int toplevel_braces_nr = counter_value (counter,
     446                                            element);
     447    if (toplevel_braces_nr)
     448      add_extra_integer (element,
     449                         "toplevel_braces_nr",
     450                         toplevel_braces_nr);
     451    counter_pop (counter);
     452  }
     453  
     454  void
     455  expand_linemacro_arguments (ELEMENT *macro, char **line_inout,
     456                              enum command_id cmd, ELEMENT *current)
     457  {
     458    char *line = *line_inout;
     459    char *pline = line;
     460    TEXT *arg;
     461    int braces_level = 0;
     462    int args_total;
     463    int spaces_nr;
     464    int i;
     465    ELEMENT *argument = new_element (ET_NONE);
     466    ELEMENT *argument_content = new_element (ET_NONE);
     467    counter_push (&count_toplevel_braces, argument_content, 0);
     468  
     469    add_to_element_args (current, argument);
     470    text_append_n (&argument_content->text, "", 0);
     471    add_to_element_contents (argument, argument_content);
     472    arg = &(argument_content->text);
     473  
     474    spaces_nr = strspn (pline, whitespace_chars_except_newline);
     475    if (spaces_nr)
     476      {
     477        ELEMENT *spaces_element = new_element (ET_NONE);
     478        text_append_n (&spaces_element->text, line, spaces_nr);
     479        add_info_element_oot (argument, "spaces_before_argument",
     480                              spaces_element);
     481  
     482        pline += spaces_nr;
     483      }
     484  
     485    args_total = macro->args.number - 1;
     486  
     487    while (1)
     488      {
     489        char *sep;
     490  
     491        sep = pline + strcspn (pline, linecommand_expansion_delimiters);
     492        if (!*sep)
     493          {
     494            debug_nonl ("LINEMACRO ARGS no separator %d '", braces_level);
     495            debug_print_protected_string (pline); debug ("'");
     496            if (braces_level > 0)
     497              {
     498                text_append (arg, pline);
     499  
     500                line = new_line (argument);
     501                if (!line)
     502                  {
     503                    line_error ("@%s missing closing brace", command_name (cmd));
     504                    line = "";
     505                    goto funexit;
     506                  }
     507              }
     508            else
     509              {
     510                int text_len = strcspn (pline, "\n");
     511                line = pline + text_len;
     512                text_append_n (arg, pline, text_len);
     513                if (! *line)
     514                  {
     515                    /* happens when @ protects the end of line, at the very end
     516                       of a text fragment and with macro expansion */
     517                    line = new_line (argument);
     518                    if (!line)
     519                      {
     520                        debug ("LINEMACRO ARGS end no EOL");
     521                        line = "";
     522                        goto funexit;
     523                      }
     524                  }
     525                else
     526                  {
     527                    /* end of macro call with an end of line */
     528                    goto funexit;
     529                  }
     530              }
     531            pline = line;
     532            continue;
     533          }
     534  
     535        text_append_n (arg, pline, sep - pline);
     536  
     537        switch (*sep)
     538          {
     539            int single_char;
     540            char *command;
     541            int whitespaces_len;
     542          case '@':
     543            pline = sep + 1;
     544            command = parse_command_name (&pline, &single_char);
     545            if (command)
     546              {
     547                enum command_id cmd = lookup_command (command);
     548                if (braces_level <= 0 && cmd
     549                    && (cmd == CM_comment || cmd == CM_c))
     550                  {
     551                    line = sep;
     552                    goto funexit;
     553                  }
     554                text_append_n (arg, sep, 1);
     555                text_append (arg, command);
     556                if (cmd && (command_data(cmd).flags & CF_brace)
     557                    && strchr (whitespace_chars, *pline)
     558                    && ((command_flags(current) & CF_accent)
     559                     || conf.ignore_space_after_braced_command_name))
     560                  {
     561                    int whitespaces_len = strspn (pline, whitespace_chars);
     562                    text_append_n (arg, pline, whitespaces_len);
     563                    pline += whitespaces_len;
     564                  }
     565                free (command);
     566              }
     567            else
     568              {
     569                text_append_n (arg, sep, 1);
     570              }
     571            break;
     572          case '{':
     573            braces_level++;
     574            text_append_n (arg, sep, 1);
     575            pline = sep + 1;
     576            break;
     577          case '}':
     578            braces_level--;
     579            text_append_n (arg, sep, 1);
     580            pline = sep + 1;
     581            if (braces_level == 0)
     582              counter_inc (&count_toplevel_braces);
     583            break;
     584          /* spaces */
     585          default:
     586            pline = sep;
     587            whitespaces_len = strspn (pline, whitespace_chars_except_newline);
     588  
     589            if (braces_level > 0
     590                || current->args.number >= args_total)
     591              {
     592                text_append_n (arg, pline, whitespaces_len);
     593              }
     594            else
     595              {
     596                ELEMENT *spaces_element = new_element (ET_NONE);
     597  
     598                set_toplevel_braces_nr (&count_toplevel_braces,
     599                                        argument_content);
     600  
     601                argument = new_element (ET_NONE);
     602                argument_content = new_element (ET_NONE);
     603                counter_push (&count_toplevel_braces, argument_content, 0);
     604  
     605                add_to_element_args (current, argument);
     606                text_append_n (&argument_content->text, "", 0);
     607                add_to_element_contents (argument, argument_content);
     608                arg = &(argument_content->text);
     609  
     610                text_append_n (&spaces_element->text, pline,
     611                               whitespaces_len);
     612                add_info_element_oot (argument, "spaces_before_argument",
     613                                      spaces_element);
     614                debug ("LINEMACRO NEW ARG");
     615              }
     616            pline += whitespaces_len;
     617            break;
     618          }
     619      }
     620  
     621   funexit:
     622    set_toplevel_braces_nr (&count_toplevel_braces,
     623                            argument_content);
     624    for (i = 0; i < current->args.number; i++)
     625      {
     626        ELEMENT *argument_content = current->args.list[i]->contents.list[0];
     627        KEY_PAIR *k = lookup_extra (argument_content, "toplevel_braces_nr");
     628        if (k)
     629          {
     630            if ((intptr_t) k->value == 1)
     631              {
     632                int text_len = strlen(argument_content->text.text);
     633                if (argument_content->text.text[0] == '{'
     634                    && argument_content->text.text[text_len -1] == '}')
     635                  {
     636                    char *braced_text = strdup (argument_content->text.text);
     637                    debug_nonl ("TURN to bracketed %d ", i);
     638                    debug_print_element (argument_content, 0); debug ("");
     639  
     640                    text_reset (&argument_content->text);
     641                    text_append_n (&argument_content->text,
     642                                   braced_text+1, text_len -2);
     643                    free(braced_text);
     644                    argument_content->type = ET_bracketed_linemacro_arg;
     645                  }
     646              }
     647  
     648            k->key = "";
     649            k->type = extra_deleted;
     650          }
     651      }
     652    debug ("END LINEMACRO ARGS EXPANSION");
     653  
     654    *line_inout = line;
     655  }
     656  /* ARGUMENTS element holds the arguments used in the macro invocation.
     657     EXPANDED gets the result of the expansion. */
     658  void
     659  expand_macro_body (MACRO *macro_record, ELEMENT *arguments, TEXT *expanded)
     660  {
     661    int pos; /* Index into arguments. */
     662    ELEMENT *macro;
     663    char *macrobody;
     664    char *ptext;
     665  
     666    macro = macro_record->element;
     667  
     668    macrobody = macro_record->macrobody;
     669  
     670    /* Initialize TEXT object. */
     671    expanded->end = 0;
     672  
     673    if (!macrobody)
     674      return;
     675  
     676    ptext = macrobody;
     677    while (1)
     678      {
     679        /* At the start of this loop ptext is at the beginning or
     680           just after the last backslash sequence. */
     681  
     682        char *bs; /* Pointer to next backslash. */
     683  
     684        bs = strchrnul (ptext, '\\');
     685        text_append_n (expanded, ptext, bs - ptext);
     686        if (!*bs)
     687          break; /* End of body. */
     688  
     689        ptext = bs + 1;
     690        if (*ptext == '\\')
     691          {
     692            text_append_n (expanded, "\\", 1); /* Escaped backslash (\\). */
     693            ptext++;
     694          }
     695        else
     696          {
     697            bs = strchr (ptext, '\\');
     698            if (!bs)
     699              {
     700                /* unpaired backslash */
     701                text_append (expanded, ptext);
     702                return;
     703              }
     704  
     705            *bs = '\0';
     706            pos = lookup_macro_parameter (ptext, macro);
     707            if (pos == -1)
     708              {
     709                line_error ("\\ in @%s expansion followed `%s' instead of "
     710                            "parameter name or \\",
     711                            macro->args.list[0]->text.text,
     712                            ptext);
     713                text_append (expanded, "\\");
     714                text_append (expanded, ptext);
     715              }
     716            else
     717              {
     718                if (arguments && pos < arguments->args.number)
     719                  {
     720                    ELEMENT *argument = args_child_by_index (arguments, pos);
     721                    if (argument->contents.number > 0)
     722                      text_append (expanded,
     723                        last_contents_child (
     724                          args_child_by_index (arguments, pos))->text.text);
     725                  }
     726              }
     727            *bs = '\\';
     728            ptext = bs + 1;
     729          }
     730      }
     731  }
     732  
     733  MACRO *
     734  lookup_macro (enum command_id cmd)
     735  {
     736    int i;
     737  
     738    for (i = 0; i < macro_number; i++)
     739      {
     740        if (macro_list[i].cmd == cmd)
     741          return &macro_list[i];
     742      }
     743    return 0;
     744  }
     745  
     746  void
     747  unset_macro_record (MACRO *m)
     748  {
     749    if (!m)
     750      return;
     751  
     752    m->cmd = 0;
     753    free (m->macro_name);
     754    m->macro_name = 0;
     755    free (m->macrobody);
     756    m->macrobody = 0;
     757    m->element = 0;
     758  }
     759  
     760  void
     761  delete_macro (char *name)
     762  {
     763    enum command_id cmd;
     764    MACRO *m;
     765    cmd = lookup_command (name);
     766    if (!cmd)
     767      return;
     768    m = lookup_macro (cmd);
     769    unset_macro_record (m);
     770    remove_texinfo_command (cmd);
     771  }
     772  
     773  void
     774  wipe_macros (void)
     775  {
     776    int i;
     777  
     778    for (i = 0; i < macro_number; i++)
     779      {
     780        free (macro_list[i].macro_name);
     781        free (macro_list[i].macrobody);
     782      }
     783    macro_number = 0;
     784  }
     785  
     786  /* Handle macro expansion.  CMD is the macro command.
     787     The returned element is an out of tree element holding the call
     788     arguments also associated to the macro expansion source mark */
     789  ELEMENT *
     790  handle_macro (ELEMENT *current, char **line_inout, enum command_id cmd)
     791  {
     792    char *line, *p;
     793    MACRO *macro_record;
     794    ELEMENT *macro;
     795    TEXT expanded;
     796    char *expanded_macro_text;
     797    int args_number;
     798    SOURCE_MARK *macro_source_mark;
     799    ELEMENT *macro_call_element = new_element (ET_NONE);
     800    int error = 0;
     801  
     802    line = *line_inout;
     803    text_init (&expanded);
     804  
     805    macro_record = lookup_macro (cmd);
     806    if (!macro_record)
     807      fatal ("no macro record");
     808    macro = macro_record->element;
     809  
     810    if (macro->cmd == CM_macro)
     811      macro_call_element->type = ET_macro_call;
     812    else if (macro->cmd == CM_rmacro)
     813      macro_call_element->type = ET_rmacro_call;
     814    else if (macro->cmd == CM_linemacro)
     815      macro_call_element->type = ET_linemacro_call;
     816  
     817    add_info_string_dup (macro_call_element, "command_name", command_name(cmd));
     818  
     819    /* It is important to check for expansion before the expansion and
     820       not after, as during the expansion, the text may go past the
     821       call.  In particular for user defined linemacro which generally
     822       get the final new line from following text.
     823    */
     824  
     825    macro_expansion_nr++;
     826    debug ("MACRO EXPANSION NUMBER %d %s", macro_expansion_nr, command_name(cmd));
     827  
     828    if (macro->cmd != CM_rmacro)
     829      {
     830        if (expanding_macro (command_name(cmd)))
     831          {
     832            line_error ("recursive call of macro %s is not allowed; "
     833                        "use @rmacro if needed", command_name(cmd));
     834            error = 1;
     835          }
     836      }
     837  
     838    if (conf.max_macro_call_nesting
     839        && macro_expansion_nr > conf.max_macro_call_nesting)
     840      {
     841        line_warn (
     842           "macro call nested too deeply "
     843           "(set MAX_MACRO_CALL_NESTING to override; current value %d)",
     844                  conf.max_macro_call_nesting);
     845        error = 1;
     846      }
     847  
     848    if (macro->cmd == CM_linemacro)
     849      {
     850        expand_linemacro_arguments (macro, &line, cmd, macro_call_element);
     851      }
     852    else
     853      {
     854        /* Get number of args. - 1 for the macro name. */
     855        args_number = macro->args.number - 1;
     856  
     857        p = line + strspn (line, whitespace_chars);
     858        if (*p == '{')
     859          {
     860            if (p - line > 0)
     861              {
     862                ELEMENT *spaces_element = new_element (ET_NONE);
     863                text_append_n (&spaces_element->text, line, p - line);
     864                add_info_element_oot (macro_call_element, "spaces_after_cmd_before_arg",
     865                                      spaces_element);
     866  
     867              }
     868            line = p;
     869            expand_macro_arguments (macro, &line, cmd, macro_call_element);
     870          }
     871        /* Warning depending on the number of arguments this macro
     872           is supposed to take. */
     873        else if (args_number != 1)
     874          {
     875            if (args_number > 1)
     876              line_warn ("@%s defined with zero or more than one argument should "
     877                         "be invoked with {}", command_name(cmd));
     878            /* As agreed on the bug-texinfo mailing list, no warn when zero
     879               arg and not called with {}. */
     880          }
     881        else
     882          {
     883            ELEMENT *arg_elt = new_element (ET_line_arg);
     884            add_to_element_args (macro_call_element, arg_elt);
     885  
     886            while (1)
     887              {
     888                if (*line == '\0')
     889                  {
     890                  /* If it takes a single line of input, and we don't have a
     891                     full line of input already, call new_line. */
     892                    line = new_line (arg_elt);
     893                    if (!line)
     894                      {
     895                        line = "";
     896                        break;
     897                      }
     898                  }
     899                else
     900                  {
     901                    int leading_spaces_added = 0;
     902                    if (arg_elt->contents.number == 0)
     903                      {
     904                        int leading_spaces_nr = strspn (line,
     905                                                 whitespace_chars_except_newline);
     906                        if (leading_spaces_nr)
     907                          {
     908                            ELEMENT *internal_space
     909                              = new_element (ET_internal_spaces_before_argument);
     910                            text_append_n (&internal_space->text, line,
     911                                           leading_spaces_nr);
     912                            add_extra_element (internal_space,
     913                                               "spaces_associated_command",
     914                                               macro_call_element);
     915                            add_to_element_contents (arg_elt, internal_space);
     916  
     917                            line += leading_spaces_nr;
     918  
     919                            leading_spaces_added = 1;
     920                          }
     921                      }
     922                    if (! leading_spaces_added)
     923                      {
     924                        char *p = strchr (line, '\n');
     925                        if (!p)
     926                          {
     927                            arg_elt = merge_text (arg_elt, line, 0);
     928                            line += strlen(line);
     929                          }
     930                        else
     931                          {
     932                            *p = '\0';
     933                            arg_elt = merge_text (arg_elt, line, 0);
     934                            line = "\n";
     935                            break;
     936                          }
     937                      }
     938                  }
     939              }
     940          }
     941      }
     942  
     943    if (error)
     944      {
     945        macro_expansion_nr--;
     946        destroy_element_and_children (macro_call_element);
     947        macro_call_element = 0;
     948        goto funexit;
     949      }
     950  
     951    expand_macro_body (macro_record, macro_call_element, &expanded);
     952  
     953    if (expanded.text)
     954      {
     955        if (expanded.text[expanded.end - 1] == '\n')
     956          expanded.text[--expanded.end] = '\0';
     957        expanded_macro_text = expanded.text;
     958      }
     959    else
     960      /* we want to always have a text for the source mark */
     961      expanded_macro_text = strdup ("");
     962  
     963    debug ("MACROBODY: %s||||||", expanded_macro_text);
     964  
     965    if (macro->cmd == CM_linemacro)
     966      macro_source_mark = new_source_mark (SM_type_linemacro_expansion);
     967    else
     968      macro_source_mark = new_source_mark (SM_type_macro_expansion);
     969    macro_source_mark->status = SM_status_start;
     970    macro_source_mark->element = macro_call_element;
     971    register_source_mark (current, macro_source_mark);
     972  
     973    /* first put the line that was interrupted by the macro call
     974       on the input pending text stack */
     975    input_push_text (strdup (line), current_source_info.line_nr, 0, 0);
     976  
     977    /* Put expansion in front of the current line. */
     978    input_push_text (expanded_macro_text, current_source_info.line_nr,
     979                     command_name(cmd), 0);
     980  
     981    set_input_source_mark (macro_source_mark);
     982  
     983    /* not really important as line is ignored by the caller if there
     984       was no macro expansion error */
     985    line = strchr (line, '\0');
     986  
     987   funexit:
     988    *line_inout = line;
     989    return macro_call_element;
     990  }
     991  
     992  
     993  /* @set and @value */
     994  
     995  typedef struct {
     996      char *name;
     997      char *value;
     998  } VALUE;
     999  
    1000  static VALUE *value_list;
    1001  static size_t value_number;
    1002  static size_t value_space;
    1003  
    1004  void
    1005  wipe_values (void)
    1006  {
    1007    size_t i;
    1008    for (i = 0; i < value_number; i++)
    1009      {
    1010        free (value_list[i].name);
    1011        free (value_list[i].value);
    1012      }
    1013    value_number = 0;
    1014  }
    1015  
    1016  void
    1017  store_value (char *name, char *value)
    1018  {
    1019    int i;
    1020    VALUE *v = 0;
    1021    int len;
    1022  
    1023    len = strlen (name);
    1024  
    1025    /* Check if already defined. */
    1026    for (i = 0; i < value_number; i++)
    1027      {
    1028        if (!strncmp (value_list[i].name, name, len) && !value_list[i].name[len])
    1029          {
    1030            v = &value_list[i];
    1031            free (v->name); free (v->value);
    1032            break;
    1033          }
    1034      }
    1035  
    1036    if (!v)
    1037      {
    1038        if (value_number == value_space)
    1039          {
    1040            value_list = realloc (value_list, (value_space += 5) * sizeof (VALUE));
    1041          }
    1042        v = &value_list[value_number++];
    1043      }
    1044  
    1045    v->name = strdup (name);
    1046    v->value = strdup (value);
    1047  
    1048    /* Internal Texinfo flag */
    1049    if (!strncmp (name, "txi", 3))
    1050      {
    1051        int val = (strcmp(value, "0") != 0);
    1052        if (!strcmp (name, "txiindexbackslashignore"))
    1053          global_info.ignored_chars.backslash = val;
    1054        else if (!strcmp (name, "txiindexhyphenignore"))
    1055          global_info.ignored_chars.hyphen = val;
    1056        else if (!strcmp (name, "txiindexlessthanignore"))
    1057          global_info.ignored_chars.lessthan = val;
    1058        else if (!strcmp (name, "txiindexatsignignore"))
    1059          global_info.ignored_chars.atsign = val;
    1060  
    1061        /* also: txicodequotebacktick, txicodequoteundirected,
    1062           txicommandconditionals.  Deal with them here? */
    1063      }
    1064  }
    1065  
    1066  void
    1067  clear_value (char *name)
    1068  {
    1069    int i;
    1070    for (i = 0; i < value_number; i++)
    1071      {
    1072        if (!strcmp (value_list[i].name, name))
    1073          {
    1074            value_list[i].name[0] = '\0';
    1075            value_list[i].value[0] = '\0';
    1076          }
    1077      }
    1078    /* Internal Texinfo flag */
    1079    if (!strncmp (name, "txi", 3))
    1080      {
    1081        if (!strcmp (name, "txiindexbackslashignore"))
    1082          global_info.ignored_chars.backslash = 0;
    1083        else if (!strcmp (name, "txiindexhyphenignore"))
    1084          global_info.ignored_chars.hyphen = 0;
    1085        else if (!strcmp (name, "txiindexlessthanignore"))
    1086          global_info.ignored_chars.lessthan = 0;
    1087        else if (!strcmp (name, "txiindexatsignignore"))
    1088          global_info.ignored_chars.atsign = 0;
    1089  
    1090        /* also: txicodequotebacktick, txicodequoteundirected,
    1091           txicommandconditionals.  Deal with them here? */
    1092      }
    1093  }
    1094  
    1095  char *
    1096  fetch_value (char *name)
    1097  {
    1098    int i;
    1099    for (i = 0; i < value_number; i++)
    1100      {
    1101        if (!strcmp (value_list[i].name, name))
    1102          return value_list[i].value;
    1103      }
    1104  
    1105    /* special value always returned as 1 to mark that @ifcommandnotdefined
    1106        is implemented.  Note that in most cases it is also set from perl
    1107        using the configuration passed to the parser */
    1108    if (!strcmp (name, "txicommandconditionals"))
    1109      return "1";
    1110    return 0;
    1111  }
    1112  
    1113  
    1114  static INFO_ENCLOSE *infoencl_list;
    1115  static size_t infoencl_number;
    1116  static size_t infoencl_space;
    1117  
    1118  INFO_ENCLOSE *
    1119  lookup_infoenclose (enum command_id cmd)
    1120  {
    1121    int i;
    1122    for (i = 0; i < infoencl_number; i++)
    1123      {
    1124        if (infoencl_list[i].cmd == cmd)
    1125          return &infoencl_list[i];
    1126      }
    1127    return 0;
    1128  }
    1129  
    1130  void
    1131  add_infoenclose (enum command_id cmd, char *begin, char *end)
    1132  {
    1133    int i;
    1134    INFO_ENCLOSE *ie = 0;
    1135  
    1136    /* Check if already defined. */
    1137    for (i = 0; i < infoencl_number; i++)
    1138      {
    1139        if (infoencl_list[i].cmd == cmd)
    1140          {
    1141            ie = &infoencl_list[i];
    1142            free (ie->begin);
    1143            free (ie->end);
    1144            break;
    1145          }
    1146      }
    1147  
    1148    if (!ie)
    1149      {
    1150        if (infoencl_number == infoencl_space)
    1151          {
    1152            infoencl_list = realloc (infoencl_list,
    1153                                     (infoencl_space += 5)
    1154                                     * sizeof (INFO_ENCLOSE));
    1155          }
    1156        ie = &infoencl_list[infoencl_number++];
    1157      }
    1158  
    1159    ie->cmd = cmd;
    1160    ie->begin = strdup (begin);
    1161    ie->end = strdup (end);
    1162  }
    1163