(root)/
texinfo-7.1/
tp/
Texinfo/
XS/
parsetexi/
handle_commands.c
       1  /* handle_commands.c -- what to do when a command name is first read */
       2  /* Copyright 2010-2023 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published by
       6     the Free Software Foundation, either version 3 of the License, or
       7     (at your option) any later version.
       8  
       9     This program is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <http://www.gnu.org/licenses/>. */
      16  
      17  #include <config.h>
      18  #include <stdlib.h>
      19  #include <string.h>
      20  #include <ctype.h>
      21  
      22  #include "parser.h"
      23  #include "def.h"
      24  #include "debug.h"
      25  #include "tree.h"
      26  #include "input.h"
      27  #include "text.h"
      28  
      29  /* Return a containing @itemize or @enumerate if inside it. */
      30  ELEMENT *
      31  item_container_parent (ELEMENT *current)
      32  {
      33    if ((current->cmd == CM_item
      34         || current->type == ET_before_item)
      35        && current->parent
      36        && ((current->parent->cmd == CM_itemize
      37             || current->parent->cmd == CM_enumerate)))
      38      {
      39        return current->parent;
      40      }
      41    return 0;
      42  }
      43  
      44  /* Check that there are no text holding environments (currently
      45     checking only paragraphs and preformatted) in contents. */
      46  int
      47  check_no_text (ELEMENT *current)
      48  {
      49    int after_paragraph = 0;
      50    int i, j;
      51    for (i = 0; i < current->contents.number; i++)
      52      {
      53        enum element_type t;
      54        ELEMENT *f;
      55        f = current->contents.list[i];
      56        t = f->type;
      57        if (t == ET_paragraph)
      58          {
      59            after_paragraph = 1;
      60            break;
      61          }
      62        else if (t == ET_preformatted)
      63          {
      64            for (j = 0; j < f->contents.number; j++)
      65              {
      66                ELEMENT *g = f->contents.list[j];
      67                if ((g->text.end > 0
      68                     && g->text.text[strspn (g->text.text, whitespace_chars)])
      69                    || (g->cmd && g->cmd != CM_c
      70                        && g->cmd != CM_comment
      71                        && g->type != ET_index_entry_command))
      72                  {
      73                    after_paragraph = 1;
      74                    break;
      75                  }
      76              }
      77            if (after_paragraph)
      78              break;
      79          }
      80      }
      81    return after_paragraph;
      82  }
      83  
      84  int
      85  in_paragraph (ELEMENT *current)
      86  {
      87    while (current->parent
      88           && (command_flags(current->parent) & CF_brace)
      89           && !(command_data(current->parent->cmd).data == BRACE_context))
      90      {
      91        current = current->parent->parent;
      92      }
      93    if (current->type == ET_paragraph)
      94      return 1;
      95    else
      96      return 0;
      97  }
      98  
      99  /* Return end of argument before comment and whitespace. */
     100  char *
     101  skip_to_comment (char *q, int *has_comment)
     102  {
     103    char *q1;
     104  
     105    while (1)
     106      {
     107        q1 = strstr (q, "@c");
     108        if (!q1)
     109          {
     110            q = q + strlen (q);
     111            break;
     112          }
     113  
     114        /* q is advanced to after @c/@comment, whether there is indeed
     115           a comment or not.  In case there is no @c/@comment, this allows
     116           to advance on the line and loop to search again for @c/@comment */
     117        q = read_comment (q1, has_comment);
     118        if (*has_comment)
     119          {
     120            /* replace q at the start of the comment */
     121            q = q1;
     122            break;
     123          }
     124      }
     125  
     126    /* q is now either at the end of the string, or at the start of a comment.
     127       Find the start of any trailing whitespace. */
     128    while (strchr (whitespace_chars, q[-1]))
     129      q--;
     130  
     131    return q;
     132  }
     133  
     134  /* Return end of argument before comment and whitespace if the
     135     line is followed either by whitespaces or a comment. */
     136  char *
     137  skip_to_comment_if_comment_or_spaces (char *after_argument,
     138                                   int *has_comment)
     139  {
     140    char *r = skip_to_comment (after_argument, has_comment);
     141  
     142    if (!strchr (whitespace_chars, *after_argument)
     143        && *after_argument != '@')
     144      return 0;
     145  
     146    if (*after_argument == '@')
     147      {
     148        /* Check for a comment, e.g. "@set flag@c comment" */
     149        if (after_argument != r)
     150          return 0;
     151      }
     152    return r;
     153  }
     154  
     155  /* Process argument to raw line command. */
     156  ELEMENT *
     157  parse_rawline_command (char *line, enum command_id cmd,
     158                         int *has_comment, int *special_arg)
     159  {
     160  #define ADD_ARG(string, len) do { \
     161    ELEMENT *E = new_element (ET_NONE); \
     162    text_append_n (&E->text, string, len); \
     163    add_to_element_contents (args, E); \
     164  } while (0)
     165  
     166    ELEMENT *args = new_element (ET_NONE);
     167    char *p = 0, *q = 0, *r = 0;
     168    char *value = 0;
     169  
     170    *special_arg = 1;
     171  
     172    switch (cmd)
     173      {
     174      case CM_set:
     175        {
     176        p = line;
     177        p += strspn (p, whitespace_chars);
     178        if (!*p)
     179          goto set_no_name;
     180        if (!isascii_alnum (*p) && *p != '-' && *p != '_')
     181          goto set_invalid;
     182        q = strpbrk (p,
     183                     " \t\f\r\n"       /* whitespace */
     184                     "{\\}~^+\"<>|@"); /* other bytes that aren't allowed */
     185        if (q)
     186          {
     187          /* see also read_flag_name function in end_line.c */
     188            r = skip_to_comment_if_comment_or_spaces (q, has_comment);
     189            if (!r)
     190              goto set_invalid;
     191          }
     192        else /* very specific case of end of text fragment after name
     193                without anything following the name, in particular
     194                without new line */
     195          q = p + strlen(p);
     196  
     197        ADD_ARG(p, q - p); /* name */
     198  
     199        p = q + strspn (q, whitespace_chars);
     200        /* Actually, whitespace characters except form feed. */
     201  
     202        if (r >= p)
     203          ADD_ARG(p, r - p); /* value */
     204        else
     205          ADD_ARG("", 0);
     206  
     207        store_value (args->contents.list[0]->text.text,
     208                     args->contents.list[1]->text.text);
     209  
     210        break;
     211      set_no_name:
     212        line_error ("@set requires a name");
     213        break;
     214      set_invalid:
     215        line_error ("bad name for @set");
     216        break;
     217        }
     218      case CM_clear:
     219        {
     220        char *flag = 0;
     221        p = line;
     222        p += strspn (p, whitespace_chars);
     223        if (!*p)
     224          goto clear_no_name;
     225        q = p;
     226        flag = read_flag_name (&q);
     227        if (!flag)
     228          goto clear_invalid;
     229        r = skip_to_comment_if_comment_or_spaces (q, has_comment);
     230        if (!r || r != q)
     231          goto clear_invalid; /* Trailing argument. */
     232  
     233        ADD_ARG (p, q - p);
     234        clear_value (flag);
     235        free (flag);
     236  
     237        break;
     238      clear_no_name:
     239        line_error ("@clear requires a name");
     240        break;
     241      clear_invalid:
     242        free (flag);
     243        line_error ("bad name for @clear");
     244        break;
     245        }
     246      case CM_unmacro:
     247        p = line;
     248        p += strspn (p, whitespace_chars);
     249        if (!*p)
     250          goto unmacro_noname;
     251        q = p;
     252        value = read_command_name (&q);
     253        if (!value)
     254          goto unmacro_badname;
     255        r = skip_to_comment_if_comment_or_spaces (q, has_comment);
     256        if (!r || r != q)
     257          goto clear_invalid; /* Trailing argument. */
     258        delete_macro (value);
     259        ADD_ARG(value, q - p);
     260        debug ("UNMACRO %s", value);
     261        free (value);
     262        break;
     263      unmacro_noname:
     264        line_error ("@unmacro requires a name");
     265        break;
     266      unmacro_badname:
     267        line_error ("bad name for @unmacro");
     268        break;
     269      case CM_clickstyle:
     270        p = line;
     271        p += strspn (p, whitespace_chars);
     272        if (*p++ != '@')
     273          goto clickstyle_invalid;
     274        q = p;
     275        value = read_command_name (&q);
     276        if (!value)
     277          goto clickstyle_invalid;
     278        ADD_ARG (p - 1, q - p + 1);
     279        free (global_clickstyle); global_clickstyle = value;
     280        /* if strlen is not used to guard against checking after the end of q,
     281           for some reason, valgrind does not find that the *(q+1) could be
     282           unallocated */
     283        if (strlen (q) >= 2 && !memcmp (q, "{}", 2))
     284          q += 2;
     285        r = skip_to_comment_if_comment_or_spaces (q, has_comment);
     286        if (!r || r != q)
     287          {
     288            char *end_line;
     289            char *line_nonl;
     290            q += strspn (q, whitespace_chars);
     291            /* remove new line for the message */
     292            line_nonl = strdup (q);
     293            end_line = strchr (line_nonl, '\n');
     294            if (end_line)
     295              *end_line = '\0';
     296            line_warn ("remaining argument on @%s line: %s",
     297                       command_name(cmd), line_nonl);
     298            free (line_nonl);
     299          }
     300        break;
     301      clickstyle_invalid:
     302        line_error ("@clickstyle should only accept an @-command as argument, "
     303                     "not `%s'", line);
     304        free (value);
     305        break;
     306      default:
     307        *special_arg = 0;
     308        ADD_ARG (line, strlen(line));
     309      }
     310  
     311    return args;
     312  #undef ADD_ARG
     313  }
     314  
     315  /* symbol skipspace other */
     316  ELEMENT *
     317  handle_other_command (ELEMENT *current, char **line_inout,
     318                       enum command_id cmd, int *status,
     319                       ELEMENT **command_element)
     320  {
     321    ELEMENT *command_e = 0;
     322    char *line = *line_inout;
     323    int arg_spec;
     324  
     325    *status = STILL_MORE_TO_PROCESS;
     326  
     327    arg_spec = command_data(cmd).data;
     328    if (arg_spec != NOBRACE_skipspace)
     329      {
     330        command_e = new_element (ET_NONE);
     331        command_e->cmd = cmd;
     332        add_to_element_contents (current, command_e);
     333        if (command_data(cmd).flags & CF_in_heading_spec
     334            && !(command_data(current_context_command()).flags & CF_heading_spec))
     335          {
     336            line_error ("@%s should only appear in heading or footing",
     337                        command_name(cmd));
     338          }
     339  
     340        if (arg_spec == NOBRACE_symbol)
     341          {
     342            if (cmd == CM_BACKSLASH && current_context () != ct_math)
     343              {
     344                line_warn ("@\\ should only appear in math context");
     345              }
     346            if (cmd == CM_NEWLINE)
     347              {
     348                current = end_line (current);
     349                *status = GET_A_NEW_LINE;
     350              }
     351          }
     352        else  /* NOBRACE_other */
     353          {
     354            register_global_command (command_e);
     355            if (close_preformatted_command(cmd))
     356              current = begin_preformatted (current);
     357          }
     358      }
     359    else
     360      {
     361        /* @item can occur in several contents: in an @itemize, a @table, or
     362           a @multitable. */
     363        if (cmd == CM_item || cmd == CM_headitem || cmd == CM_tab)
     364          {
     365            ELEMENT *parent;
     366  
     367            /* @itemize or @enumerate */
     368            if ((parent = item_container_parent (current)))
     369              {
     370                if (cmd == CM_item)
     371                  {
     372                    debug ("ITEM CONTAINER");
     373                    counter_inc (&count_items);
     374                    command_e = new_element (ET_NONE);
     375                    command_e->cmd = CM_item;
     376  
     377                    add_extra_integer (command_e, "item_number",
     378                                       counter_value (&count_items, parent));
     379  
     380                    add_to_element_contents (parent, command_e);
     381                    current = command_e;
     382                  }
     383                else
     384                  {
     385                    line_error ("@%s not meaningful inside `@%s' block",
     386                                command_name(cmd),
     387                                command_name(parent->cmd));
     388                  }
     389                current = begin_preformatted (current);
     390              }
     391            /* @table, @vtable, @ftable */
     392            else if ((parent = item_line_parent (current)))
     393              {
     394                line_error ("@%s not meaningful inside `@%s' block",
     395                            command_name(cmd),
     396                            command_name(parent->cmd));
     397                current = begin_preformatted (current);
     398              }
     399            /* In a @multitable */
     400            else if ((parent = item_multitable_parent (current)))
     401              {
     402                long max_columns = 0;
     403                KEY_PAIR *k;
     404  
     405                k = lookup_extra (parent, "max_columns");
     406                if (k)
     407                  max_columns = (long) k->value;
     408  
     409                if (max_columns == 0)
     410                  {
     411                    line_warn ("@%s in empty multitable",
     412                               command_name(cmd));
     413                  }
     414                else if (cmd == CM_tab)
     415                  {
     416                    ELEMENT *row;
     417                    row = last_contents_child (parent);
     418                    if (row->type == ET_before_item)
     419                      line_error ("@tab before @item");
     420                    else if (counter_value (&count_cells, row)
     421                             >= max_columns)
     422                      {
     423                        line_error ("too many columns in multitable item"
     424                                    " (max %d)", max_columns);
     425                      }
     426                    else
     427                      {
     428                        counter_inc (&count_cells);
     429                        command_e = new_element (ET_NONE);
     430                        command_e->cmd = cmd;
     431                        add_to_element_contents (row, command_e);
     432                        current = command_e;
     433                        debug ("TAB");
     434  
     435                        add_extra_integer (current, "cell_number",
     436                                           counter_value (&count_cells, row));
     437                      }
     438                  }
     439                else /* @item or @headitem */
     440                  {
     441                    ELEMENT *row;
     442  
     443                    debug ("ROW");
     444                    row = new_element (ET_row);
     445                    add_to_element_contents (parent, row);
     446  
     447                    /* Note that the "row_number" extra value,
     448                       isn't actually used anywhere at present. */
     449                    add_extra_integer (row, "row_number",
     450                                       parent->contents.number - 1);
     451  
     452                    command_e = new_element (ET_NONE);
     453                    command_e->cmd = cmd;
     454                    add_to_element_contents (row, command_e);
     455                    current = command_e;
     456  
     457                    if (counter_value (&count_cells, parent) != -1)
     458                      counter_pop (&count_cells);
     459                    counter_push (&count_cells, row, 1);
     460                    add_extra_integer (current, "cell_number",
     461                                       counter_value (&count_cells, row));
     462                  }
     463                current = begin_preformatted (current);
     464              } /* In @multitable */
     465            else if (cmd == CM_tab)
     466              {
     467                line_error ("ignoring @tab outside of multitable");
     468                current = begin_preformatted (current);
     469              }
     470            else
     471              {
     472                line_error ("@%s outside of table or list",
     473                            command_name(cmd));
     474                current = begin_preformatted (current);
     475              }
     476            if (command_e)
     477              command_e->source_info = current_source_info;
     478          }
     479        else
     480          {
     481            command_e = new_element (ET_NONE);
     482            command_e->cmd = cmd;
     483            command_e->source_info = current_source_info;
     484            add_to_element_contents (current, command_e);
     485            if ((cmd == CM_indent || cmd == CM_noindent)
     486                 && in_paragraph (current))
     487              {
     488                line_warn ("@%s is useless inside of a paragraph",
     489                           command_name(cmd));
     490              }
     491          }
     492        start_empty_line_after_command (current, &line, 0);
     493      }
     494  
     495    *line_inout = line;
     496    *command_element = command_e;
     497    return current;
     498  }
     499  
     500  /* STATUS is set to GET_A_NEW_LINE if we should get a new line after this,
     501     to FINISHED_TOTALLY if we should stop processing completely. */
     502  /* data_cmd (used for the information on the command) and cmd (for the
     503     command name) is different for the only multicategory command, @item */
     504  ELEMENT *
     505  handle_line_command (ELEMENT *current, char **line_inout,
     506                       enum command_id cmd, enum command_id data_cmd,
     507                       int *status, ELEMENT **command_element)
     508  {
     509    ELEMENT *command_e = 0;
     510    char *line = *line_inout;
     511    int arg_spec;
     512  
     513    *status = STILL_MORE_TO_PROCESS;
     514  
     515    /* Root commands (like @node) and @bye */
     516    if (command_data(data_cmd).flags & CF_root || cmd == CM_bye)
     517      {
     518        ELEMENT *closed_elt; /* Not used */
     519        current = close_commands (current, 0, &closed_elt, cmd);
     520        /* if parse_texi_line is called on a line with a node/section then
     521           it will directly be in the root_line, otherwise it is not directly
     522           in the root, but in another container */
     523        if (current->type != ET_root_line)
     524          {
     525            current = current->parent;
     526            if (!current)
     527              fatal ("no parent element");
     528          }
     529      }
     530  
     531    /* Look up information about this command
     532       ( text line lineraw specific special ). */
     533    arg_spec = command_data(data_cmd).data;
     534  
     535    /* All the cases using the raw line.
     536       For some commands, the arguments are determined especially from the
     537       raw line, for other the line is taken as is as argument, and possibly
     538       later ignored for commands without arg.
     539     */
     540    if (arg_spec == LINE_lineraw)
     541      {
     542        ELEMENT *args = 0;
     543        enum command_id equivalent_cmd = 0;
     544        int has_comment = 0;
     545        int special_arg = 0;
     546        int ignored = 0;
     547  
     548        if (cmd == CM_insertcopying)
     549          {
     550            ELEMENT *p = current;
     551            while (p)
     552              {
     553                if (p->cmd == CM_copying)
     554                  {
     555                    line_error ("@%s not allowed inside `@copying' block",
     556                                command_name(cmd));
     557                    ignored = 1;
     558                    break;
     559                  }
     560                p = p->parent;
     561              }
     562          }
     563  
     564        /* If the current input is the result of a macro expansion,
     565           it may not be a complete line.  Check for this and acquire the rest
     566           of the line if necessary. */
     567        if (!strchr (line, '\n'))
     568          {
     569            char *line2;
     570            SOURCE_INFO save_src_info;
     571  
     572            input_push_text (strdup (line), current_source_info.line_nr, 0, 0);
     573  
     574            save_src_info = current_source_info;
     575            /* REMARK the source marks (mostly end of macro/value expansion) will
     576               be associated to the previous element in current, as the command being
     577               considered has not been added already, although the end of macro
     578               expansion is located after the command opening.  Wrongly placed
     579               mark sources are unavoidable, as the line is not parsed as usual
     580               and macro/value expansion happen here in advance and not while
     581               the remaining of the line is parsed. */
     582  
     583            line2 = new_line (current);
     584            if (line2)
     585              {
     586                line = line2;
     587                current_source_info = save_src_info;
     588              }
     589          }
     590  
     591        command_e = new_element (ET_NONE);
     592        command_e->cmd = cmd;
     593  
     594        args = parse_rawline_command (line, cmd,
     595                                      &has_comment, &special_arg);
     596        if (special_arg)
     597          add_info_string_dup (command_e, "arg_line", line);
     598  
     599        /* Handle @set txicodequoteundirected as an
     600           alternative to @codequoteundirected. */
     601        if (cmd == CM_set || cmd == CM_clear)
     602          {
     603            if (args->contents.number > 0
     604                && args->contents.list[0]->text.end > 0)
     605              {
     606                if (!strcmp (args->contents.list[0]->text.text,
     607                             "txicodequoteundirected"))
     608                  equivalent_cmd = CM_codequoteundirected;
     609                else if (!strcmp (args->contents.list[0]->text.text,
     610                                  "txicodequotebacktick"))
     611                  equivalent_cmd = CM_codequotebacktick;
     612              }
     613          }
     614        if (equivalent_cmd)
     615          {
     616            char *arg = 0;
     617            ELEMENT *line_args;
     618            ELEMENT *e;
     619            ELEMENT *spaces_before = new_element (ET_NONE);
     620            ELEMENT *spaces_after = new_element (ET_NONE);
     621  
     622            if (cmd == CM_set)
     623              arg = "on";
     624            else
     625              arg = "off";
     626  
     627            /* Now manufacture the parse tree for the equivalent
     628               command and add it to the tree. */
     629  
     630            destroy_element_and_children (args);
     631            args = new_element (ET_NONE);
     632            e = new_element (ET_NONE);
     633            text_append (&e->text, arg);
     634            add_to_element_contents (args, e);
     635  
     636            destroy_element_and_children (command_e);
     637            command_e = new_element (ET_NONE);
     638            command_e->cmd = equivalent_cmd;
     639            command_e->source_info = current_source_info;
     640  
     641            line_args = new_element (ET_line_arg);
     642            add_to_element_args (command_e, line_args);
     643            add_extra_misc_args (command_e, "misc_args", args);
     644            text_append (&spaces_before->text, " ");
     645            add_info_element_oot (command_e, "spaces_before_argument", spaces_before);
     646  
     647            text_append (&spaces_after->text, "\n");
     648            add_info_element_oot (line_args, "spaces_after_argument",
     649                                  spaces_after);
     650  
     651            e = new_element (ET_NONE);
     652            text_append (&e->text, arg);
     653            add_to_element_contents (line_args, e);
     654  
     655            add_to_element_contents (current, command_e);
     656          }
     657        else
     658          {
     659            int i;
     660            if (!ignored)
     661              {
     662                add_to_element_contents (current, command_e);
     663  
     664                for (i = 0; i < args->contents.number; i++)
     665                  {
     666                    ELEMENT *rawline_arg = new_element (ET_rawline_arg);
     667                    text_append_n (&rawline_arg->text,
     668                                   args->contents.list[i]->text.text,
     669                                   args->contents.list[i]->text.end);
     670                    add_to_element_args (command_e, rawline_arg);
     671                  }
     672              }
     673            else
     674              {
     675                destroy_element_and_children (command_e);
     676                command_e = 0;
     677              }
     678            destroy_element_and_children (args);
     679          }
     680  
     681        if (cmd == CM_raisesections)
     682          {
     683            global_info.sections_level++;
     684          }
     685        else if (cmd == CM_lowersections)
     686          {
     687            global_info.sections_level--;
     688          }
     689  
     690        if (command_e)
     691          register_global_command (command_e);
     692  
     693        /* This does nothing for the command being processed, as there is
     694           no line context setup nor line_args, but it closes a line or block
     695           line @-commands the raw line command is on.  For c/comment
     696           this corresponds to legitimate constructs, not for other raw line
     697           commands.
     698         */
     699        current = end_line (current);
     700  
     701        if (cmd == CM_bye)
     702          {
     703            *status = FINISHED_TOTALLY;
     704            goto funexit;
     705          }
     706  
     707        if (close_preformatted_command(cmd))
     708          current = begin_preformatted (current);
     709  
     710        *status = GET_A_NEW_LINE;
     711        goto funexit;
     712      }
     713    else
     714      {
     715        ELEMENT *arg;
     716  
     717        /* text, line, or specific.
     718           (This includes handling of "@end", which is LINE_text.) */
     719        if (cmd == CM_item || cmd == CM_itemx)
     720          {
     721            ELEMENT *parent;
     722            if ((parent = item_line_parent (current)))
     723              {
     724                debug ("ITEM LINE %s", command_name(cmd));
     725                current = parent;
     726                gather_previous_item (current, cmd);
     727              }
     728            else
     729              {
     730                line_error ("@%s outside of table or list",
     731                            command_name(cmd));
     732                current = begin_preformatted (current);
     733              }
     734            command_e = new_element (ET_NONE);
     735            command_e->cmd = cmd;
     736            command_e->source_info = current_source_info;
     737            add_to_element_contents (current, command_e);
     738          }
     739        else
     740          {
     741            /* Add to contents */
     742            command_e = new_element (ET_NONE);
     743            command_e->cmd = cmd;
     744            command_e->source_info = current_source_info;
     745  
     746            if (cmd == CM_nodedescription)
     747              {
     748                if (current_node)
     749                  {
     750                    KEY_PAIR *k = lookup_extra (current_node, "node_description");
     751                    if (k && k->value)
     752                      {
     753                        ELEMENT *e_description = (ELEMENT *) k->value;
     754                        if (e_description->cmd == cmd)
     755                          line_warn ("multiple node @nodedescription");
     756                        else
     757                          /* silently replace nodedescriptionblock */
     758                          add_extra_element (current_node, "node_description",
     759                                             command_e);
     760                      }
     761                    else
     762                      add_extra_element (current_node, "node_description",
     763                                         command_e);
     764                    add_extra_element (command_e, "element_node", current_node);
     765                  }
     766                else
     767                  line_warn ("@nodedescription outside of any node");
     768              }
     769            else if (cmd == CM_subentry)
     770              {
     771                long level = 1;
     772                ELEMENT *parent = current->parent;
     773  
     774                if (!(command_flags(parent) & CF_index_entry_command)
     775                    && parent->cmd != CM_subentry)
     776                  {
     777                    line_warn ("@subentry should only occur in an index entry");
     778                  }
     779  
     780                add_extra_element (parent, "subentry", command_e);
     781  
     782                if (parent->cmd == CM_subentry)
     783                  {
     784                    KEY_PAIR *k = lookup_extra (parent, "level");
     785                    if (k && k->value)
     786                      level = (long) k->value + 1;
     787                  }
     788                add_extra_integer (command_e, "level", level);
     789                if (level > 2)
     790                  {
     791                    line_error
     792                      ("no more than two levels of index subentry are allowed");
     793                  }
     794  
     795                /* Do not make the @subentry element a child of the index
     796                   command.  This means that spaces are preserved properly
     797                   when converting back to Texinfo. */
     798                current = end_line (current);
     799              }
     800  
     801            add_to_element_contents (current, command_e);
     802  
     803            if (command_data(data_cmd).flags & CF_sectioning_heading)
     804              {
     805                if (global_info.sections_level)
     806                  {
     807                    add_extra_integer (command_e, "sections_level",
     808                                       global_info.sections_level);
     809                  }
     810              }
     811  
     812            /* @def*x */
     813            if (command_data(data_cmd).flags & CF_def)
     814              {
     815                enum command_id base_command;
     816                int after_paragraph;
     817                int appropriate_command;
     818                enum command_id cmdname;
     819                char *val;
     820  
     821                if (cmd == CM_defline || cmd == CM_deftypeline)
     822                  {
     823                    base_command = cmd;
     824                    add_extra_string_dup (command_e, "original_def_cmdname",
     825                                          command_name(cmd));
     826                    add_extra_string_dup (command_e, "def_command",
     827                                          command_name(cmd));
     828                  }
     829                else
     830                  {
     831                    /* Find the command with "x" stripped from the end, e.g.
     832                       deffnx -> deffn. */
     833  
     834                    char *base_name;
     835                    int base_len;
     836  
     837                    base_name = command_name(cmd);
     838                    add_extra_string_dup (command_e, "original_def_cmdname",
     839                                          base_name);
     840                    base_name = strdup (base_name);
     841                    base_len = strlen (base_name);
     842                    if (base_name[base_len - 1] != 'x')
     843                      fatal ("no x at end of def command name");
     844                    base_name[base_len - 1] = '\0';
     845                    base_command = lookup_command (base_name);
     846                    if (base_command == CM_NONE)
     847                      fatal ("no def base command");
     848                    add_extra_string (command_e, "def_command", base_name);
     849                  }
     850  
     851                cmdname = current->cmd;
     852                if (cmdname != CM_defblock)
     853                  after_paragraph = check_no_text (current);
     854                else
     855                  after_paragraph = 0;
     856                push_context (ct_def, cmd);
     857                command_e->type = ET_def_line;
     858  
     859                /* Check txidefnamenospace flag */
     860                val = fetch_value ("txidefnamenospace");
     861                if (val)
     862                  add_extra_integer (command_e, "omit_def_name_space", 1);
     863  
     864                if (cmdname == base_command || cmdname == CM_defblock)
     865                  appropriate_command = 1;
     866                else
     867                  appropriate_command = 0;
     868  
     869                if (appropriate_command)
     870                  {
     871                    ELEMENT *e = pop_element_from_contents (current);
     872                    /* e should be the same as command_e */
     873                    /* Gather an "inter_def_item" element. */
     874                    gather_def_item (current, cmd);
     875                    add_to_element_contents (current, e);
     876                  }
     877                if (!appropriate_command || after_paragraph)
     878                  {
     879                    /* error - deffnx not after deffn */
     880                    line_error ("must be after `@%s' to use `@%s'",
     881                                 command_name(base_command),
     882                                 command_name(cmd));
     883                    add_extra_integer (command_e, "not_after_command", 1);
     884                  }
     885              }
     886          }
     887  
     888        /* change 'current' to its last child.  This is command_e.  */
     889        current = last_contents_child (current);
     890        arg = new_element (ET_line_arg);
     891        add_to_element_args (current, arg);
     892  
     893        if (command_data(data_cmd).flags & CF_contain_basic_inline)
     894          push_command (&nesting_context.basic_inline_stack_on_line, cmd);
     895  
     896        /* LINE_specific commands arguments are handled in a specific way.
     897           The only other line commands that have more than one argument is
     898           node, so the following condition only applies to node */
     899        if (arg_spec != LINE_specific
     900            && command_data (data_cmd).args_number > 1)
     901          {
     902            counter_push (&count_remaining_args,
     903                          current,
     904                          command_data (data_cmd).args_number - 1);
     905          }
     906        if (cmd == CM_author)
     907          {
     908            ELEMENT *parent = current;
     909            int found = 0;
     910            while (parent->parent)
     911              {
     912                parent = parent->parent;
     913                if (parent->type == ET_brace_command_context)
     914                  break;
     915                if (parent->cmd == CM_titlepage)
     916                  {
     917                    add_extra_element (current, "titlepage", parent);
     918                    found = 1; break;
     919                  }
     920                else if (parent->cmd == CM_quotation
     921                         || parent->cmd == CM_smallquotation)
     922                  {
     923                    KEY_PAIR *k; ELEMENT *e;
     924                    k = lookup_extra (parent, "authors");
     925                    if (k)
     926                      e = (ELEMENT *) k->value;
     927                    else
     928                      {
     929                        e = new_element (ET_NONE);
     930                        add_extra_contents (parent, "authors", e);
     931                      }
     932                    add_to_contents_as_array (e, current);
     933                    add_extra_element (current, "quotation", parent);
     934                    found = 1; break;
     935                  }
     936              }
     937            if (!found)
     938              line_warn ("@author not meaningful outside "
     939                         "`@titlepage' and `@quotation' environments");
     940          }
     941        else if (cmd == CM_dircategory && current_node)
     942          line_warn ("@dircategory after first node");
     943        else if (cmd == CM_printindex && current_node)
     944          add_extra_integer (current_node, "isindex", 1);
     945  
     946        current = last_args_child (current);
     947  
     948        /* add 'line' to context_stack.  This will be the
     949           case while we read the argument on this line. */
     950        if (!(command_data(data_cmd).flags & CF_def))
     951          push_context (ct_line, cmd);
     952        start_empty_line_after_command (current, &line, command_e);
     953      }
     954  
     955    if (command_e)
     956      register_global_command (command_e);
     957    if (cmd == CM_dircategory)
     958      add_to_contents_as_array (&global_info.dircategory_direntry, command_e);
     959  
     960  funexit:
     961    *line_inout = line;
     962    *command_element = command_e;
     963    return current;
     964  }
     965  
     966  struct expanded_format expanded_formats[] = {
     967      "html", 0,
     968      "docbook", 0,
     969      "plaintext", 1,
     970      "tex", 0,
     971      "xml", 0,
     972      "info", 1,
     973      "latex", 0,
     974  };
     975  
     976  void
     977  clear_expanded_formats (void)
     978  {
     979    int i;
     980    for (i = 0; i < sizeof (expanded_formats)/sizeof (*expanded_formats);
     981         i++)
     982      {
     983        expanded_formats[i].expandedp = 0;
     984      }
     985  }
     986  
     987  void
     988  add_expanded_format (char *format)
     989  {
     990    int i;
     991    for (i = 0; i < sizeof (expanded_formats)/sizeof (*expanded_formats);
     992         i++)
     993      {
     994        if (!strcmp (format, expanded_formats[i].format))
     995          {
     996            expanded_formats[i].expandedp = 1;
     997            break;
     998          }
     999      }
    1000    if (!strcmp (format, "plaintext"))
    1001      add_expanded_format ("info");
    1002  }
    1003  
    1004  int
    1005  format_expanded_p (char *format)
    1006  {
    1007    int i;
    1008    for (i = 0; i < sizeof (expanded_formats)/sizeof (*expanded_formats);
    1009         i++)
    1010      {
    1011        if (!strcmp (format, expanded_formats[i].format))
    1012          return expanded_formats[i].expandedp;
    1013      }
    1014    return 0;
    1015  }
    1016  
    1017  /* A command name has been read that starts a multiline block, which should
    1018     end in @end <command name>.  The block will be processed until
    1019     "end_line_misc_line" in end_line.c processes the @end command. */
    1020  ELEMENT *
    1021  handle_block_command (ELEMENT *current, char **line_inout,
    1022                        enum command_id cmd, int *get_new_line,
    1023                        ELEMENT **command_element)
    1024  {
    1025    char *line = *line_inout;
    1026    unsigned long flags = command_data(cmd).flags;
    1027    ELEMENT *block = 0;
    1028  
    1029    /* New macro being defined. */
    1030    if (cmd == CM_macro || cmd == CM_rmacro || cmd == CM_linemacro)
    1031      {
    1032        block = parse_macro_command_line (cmd, &line, current);
    1033        add_to_element_contents (current, block);
    1034        current = block;
    1035  
    1036        /* A new line should be read immediately after this.  */
    1037        line = strchr (line, '\0');
    1038        *get_new_line = 1;
    1039        goto funexit;
    1040      }
    1041    else
    1042      {
    1043        ELEMENT *bla;   /* block line arg element */
    1044        if (command_data(cmd).data == BLOCK_menu
    1045            && (current->type == ET_menu_comment
    1046                || current->type == ET_menu_entry_description))
    1047          {
    1048            /* This is, in general, caused by @detailmenu within @menu */
    1049            if (current->type == ET_menu_comment)
    1050              current = close_container(current);
    1051            else /* menu_entry_description */
    1052              {
    1053                current = close_container(current);
    1054                if (current->type == ET_menu_entry)
    1055                  current = current->parent;
    1056                else
    1057                  {
    1058                    bug_message ("menu description parent not a menu_entry: %s",
    1059                                 element_type_name (current));
    1060                    abort ();
    1061                  }
    1062              }
    1063          }
    1064  
    1065        if (flags & CF_def)
    1066          {
    1067            ELEMENT *def_line;
    1068            char *val;
    1069            push_context (ct_def, cmd);
    1070            block = new_element (ET_NONE);
    1071            block->cmd = cmd;
    1072            block->source_info = current_source_info;
    1073            add_to_element_contents (current, block);
    1074            current = block;
    1075  
    1076            def_line = new_element (ET_def_line);
    1077            def_line->source_info = current_source_info;
    1078            add_to_element_contents (current, def_line);
    1079            current = def_line;
    1080            add_extra_string_dup (current, "def_command", command_name(cmd));
    1081            add_extra_string_dup (current, "original_def_cmdname", 
    1082                                  command_name(cmd));
    1083            /* Check txidefnamenospace flag */
    1084            val = fetch_value ("txidefnamenospace");
    1085            if (val)
    1086              add_extra_integer (current, "omit_def_name_space", 1);
    1087          }
    1088        else
    1089          {
    1090            block = new_element (ET_NONE);
    1091  
    1092            block->cmd = cmd;
    1093            add_to_element_contents (current, block);
    1094            current = block;
    1095          }
    1096  
    1097        /* Check if 'block args command' */
    1098        if (command_data(cmd).flags & CF_preformatted)
    1099          push_context (ct_preformatted, cmd);
    1100        else if (cmd == CM_displaymath)
    1101          push_context (ct_math, cmd);
    1102        else if (command_data(cmd).data == BLOCK_format_raw)
    1103          {
    1104            push_context (ct_rawpreformatted, cmd);
    1105          }
    1106        else if (command_data(cmd).data == BLOCK_region)
    1107          {
    1108            push_command (&nesting_context.regions_stack, cmd);
    1109          }
    1110  
    1111        if (command_data(cmd).data == BLOCK_menu)
    1112          {
    1113            push_context (ct_preformatted, cmd);
    1114  
    1115            if (cmd == CM_direntry)
    1116              add_to_contents_as_array (&global_info.dircategory_direntry, 
    1117                                        block);
    1118  
    1119            if (current_node)
    1120              {
    1121                if (cmd == CM_direntry && conf.show_menu)
    1122                  {
    1123                    line_warn ("@direntry after first node");
    1124                  }
    1125                else if (cmd == CM_menu)
    1126                  {
    1127                    if (!(command_flags(current->parent) & CF_root))
    1128                      line_warn ("@menu in invalid context");
    1129                    /* Add to array of menus for current node.  Currently
    1130                       done in Perl code. */
    1131                  }
    1132              }
    1133          }
    1134  
    1135        if (cmd == CM_nodedescriptionblock)
    1136          {
    1137            if (current_node)
    1138              {
    1139                KEY_PAIR *k = lookup_extra (current_node, "node_long_description");
    1140                if (k && k->value)
    1141                  line_warn ("multiple node @nodedescriptionblock");
    1142                 else
    1143                  {
    1144                    KEY_PAIR *kn = lookup_extra (current_node, "node_description");
    1145  
    1146                    if (!kn || !kn->value)
    1147                      add_extra_element (current_node, "node_description",
    1148                                         block);
    1149  
    1150                    add_extra_element (current_node, "node_long_description",
    1151                                       block);
    1152                  }
    1153                add_extra_element (block, "element_node", current_node);
    1154              }
    1155            else
    1156              line_warn ("@nodedescriptionblock outside of any node");
    1157  
    1158          }
    1159  
    1160        if (cmd == CM_itemize || cmd == CM_enumerate)
    1161          counter_push (&count_items, current, 0);
    1162  
    1163        bla = new_element (ET_block_line_arg);
    1164        add_to_element_args (current, bla);
    1165  
    1166        if (command_data (current->cmd).args_number > 1)
    1167          {
    1168            counter_push (&count_remaining_args,
    1169                          current,
    1170                          command_data (current->cmd).args_number - 1);
    1171          }
    1172        else if (command_data (current->cmd).flags & CF_variadic)
    1173          {
    1174            /* Unlimited args */
    1175            counter_push (&count_remaining_args, current,
    1176                          COUNTER_VARIADIC);
    1177          }
    1178  
    1179        current = bla;
    1180        if (!(command_data(cmd).flags & CF_def))
    1181          push_context (ct_line, cmd);
    1182        if (command_data(cmd).flags & CF_contain_basic_inline)
    1183          push_command (&nesting_context.basic_inline_stack_block, cmd);
    1184  
    1185        block->source_info = current_source_info;
    1186        register_global_command (block);
    1187        start_empty_line_after_command (current, &line, block);
    1188      }
    1189  
    1190  funexit:
    1191    *line_inout = line;
    1192    *command_element = block;
    1193    return current;
    1194  }
    1195  
    1196  ELEMENT *
    1197  handle_brace_command (ELEMENT *current, char **line_inout, enum command_id cmd,
    1198                        ELEMENT **command_element)
    1199  {
    1200    char *line = *line_inout;
    1201    ELEMENT *command_e;
    1202  
    1203    debug ("OPEN BRACE @%s", command_name(cmd));
    1204  
    1205    command_e = new_element (ET_NONE);
    1206    command_e->cmd = cmd;
    1207  
    1208    /* The line number information is only ever used for brace commands
    1209       if the command is given with braces, but it's easier just to always
    1210       store the information. */
    1211    command_e->source_info = current_source_info;
    1212  
    1213    add_to_element_contents (current, command_e);
    1214  
    1215    if (cmd == CM_sortas)
    1216      {
    1217        if (!(command_flags(current->parent) & CF_index_entry_command)
    1218            && current->parent->cmd != CM_subentry)
    1219          {
    1220            line_warn ("@%s should only appear in an index entry",
    1221                       command_name(cmd));
    1222          }
    1223      }
    1224  
    1225    current = command_e;
    1226  
    1227    if (cmd == CM_click)
    1228      {
    1229        add_extra_string_dup (command_e, "clickstyle", global_clickstyle);
    1230      }
    1231    else if (cmd == CM_kbd)
    1232      {
    1233        if (kbd_formatted_as_code(current))
    1234          {
    1235            add_extra_integer (command_e, "code", 1);
    1236          }
    1237      }
    1238    else if (command_data(cmd).flags & CF_INFOENCLOSE)
    1239      {
    1240        INFO_ENCLOSE *ie = lookup_infoenclose (cmd);
    1241        if (ie)
    1242          {
    1243            add_extra_string_dup (command_e, "begin", ie->begin);
    1244            add_extra_string_dup (command_e, "end", ie->end);
    1245          }
    1246        command_e->type = ET_definfoenclose_command;
    1247      }
    1248  
    1249    *line_inout = line;
    1250    *command_element = command_e;
    1251    return current;
    1252  }