1  /* end_line.c -- what to do at the end of a whole line of input */
       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  #include <stdio.h>
      22  
      23  #include "parser.h"
      24  #include "debug.h"
      25  #include "text.h"
      26  #include "input.h"
      27  #include "convert.h"
      28  #include "labels.h"
      29  #include "indices.h"
      30  #include "def.h"
      31  #include "source_marks.h"
      32  #include "handle_commands.h"
      33  
      34  static int
      35  isascii_alpha (int c)
      36  {
      37    return (((c & ~0x7f) == 0) && isalpha(c));
      38  }
      39  
      40  static int
      41  is_decimal_number (char *string)
      42  {
      43    char *p = string;
      44    char *first_digits = 0;
      45    char *second_digits = 0;
      46    
      47    if (string[0] == '\0')
      48      return 0;
      49  
      50    if (strchr (digit_chars, *p))
      51      p = first_digits = string + strspn (string, digit_chars);
      52  
      53    if (*p == '.')
      54      {
      55        p++;
      56        if (strchr (digit_chars, *p))
      57          p = second_digits = p + strspn (p, digit_chars);
      58      }
      59  
      60    if (*p /* Bytes remaining at end of argument. */
      61        || (!first_digits && !second_digits)) /* Need digits either 
      62                                                 before or after the 
      63                                                 decimal point. */
      64      {
      65        return 0;
      66      }
      67  
      68    return 1;
      69  }
      70  
      71  static int
      72  is_whole_number (char *string)
      73  {
      74    if (string[strspn (string, digit_chars)] == '\0')
      75      return 1;
      76    return 0;
      77  }
      78  
      79  /* Parse the arguments to a line command.  Return an element whose contents
      80     is an array of the arguments.  For some commands, there is further 
      81     processing of the arguments (for example, for an @alias, remember the
      82     alias.) */
      83  ELEMENT *
      84  parse_line_command_args (ELEMENT *line_command)
      85  {
      86  #define ADD_ARG(string) do { \
      87      ELEMENT *E = new_element (ET_NONE); \
      88      text_append (&E->text, string); \
      89      add_to_element_contents (line_args, E); \
      90  } while (0)
      91  
      92    ELEMENT *arg = line_command->args.list[0];
      93    ELEMENT *line_args;
      94    enum command_id cmd;
      95    char *line;
      96  
      97    cmd = line_command->cmd;
      98    if (arg->contents.number == 0)
      99     {
     100       command_error (line_command, "@%s missing argument", command_name(cmd));
     101       add_extra_integer (line_command, "missing_argument", 1);
     102       return 0;
     103     }
     104  
     105    if (arg->contents.number > 1 || arg->contents.list[0]->text.end == 0)
     106      {
     107        line_error ("superfluous argument to @%s", command_name (cmd));
     108      }
     109    if (arg->contents.list[0]->text.end == 0)
     110      return 0;
     111  
     112    line_args = new_element (ET_NONE);
     113    line = arg->contents.list[0]->text.text;
     114  
     115    switch (cmd)
     116      {
     117      case CM_alias:
     118        {
     119          /* @alias NEW = EXISTING */
     120          char *new = 0, *existing = 0;
     121          enum command_id new_cmd, existing_cmd;
     122  
     123          new = read_command_name (&line);
     124          if (!new)
     125            goto alias_invalid;
     126  
     127          line += strspn (line, whitespace_chars);
     128          if (*line != '=')
     129            goto alias_invalid;
     130          line++;
     131          line += strspn (line, whitespace_chars);
     132  
     133          if (!isascii_alnum (*line))
     134            goto alias_invalid;
     135          existing = read_command_name (&line);
     136          if (!existing)
     137            goto alias_invalid;
     138  
     139          if (*line)
     140            goto alias_invalid; /* Trailing argument. */
     141  
     142          ADD_ARG(new);
     143          ADD_ARG(existing);
     144  
     145          existing_cmd = lookup_command (existing);
     146          if (!existing_cmd)
     147            {
     148              /* supposedly existing command not defined.  Pre-register a
     149                 user-defined command */
     150              existing_cmd = add_texinfo_command (existing);
     151              user_defined_command_data[existing_cmd & ~USER_COMMAND_BIT].flags
     152                                                                   |= CF_UNKNOWN;
     153            }
     154          else
     155            {
     156              if (command_data(existing_cmd).flags & CF_block)
     157                line_warn ("environment command %s as argument to @alias",
     158                           command_name(existing_cmd));
     159            }
     160  
     161          if (command_data(existing_cmd).flags & CF_ALIAS)
     162            {
     163              enum command_id alias_exist_cmd = command_data(existing_cmd).data;
     164              if (! strcmp(command_name(alias_exist_cmd), new))
     165                line_warn ("recursive alias definition of %s through %s ignored",
     166                              new, command_name(existing_cmd));
     167              else
     168                existing_cmd = alias_exist_cmd;
     169            }
     170  
     171          /* Remember the alias. */
     172          new_cmd = add_texinfo_command (new);
     173          new_cmd &= ~USER_COMMAND_BIT;
     174          user_defined_command_data[new_cmd].flags |= CF_ALIAS;
     175          user_defined_command_data[new_cmd].data = existing_cmd;
     176          if (existing_cmd & USER_COMMAND_BIT)
     177            {
     178              enum command_id user_data_cmd = existing_cmd & ~USER_COMMAND_BIT;
     179              user_defined_command_data[user_data_cmd].flags |= CF_REGISTERED;
     180            }
     181  
     182          /* Note the data field is an int, existing_cmd is
     183             enum command_id, so would have problems if enum command_id
     184             were wider than an int. */
     185  
     186          free (new); free (existing);
     187  
     188          break;
     189        alias_invalid:
     190          line_error ("bad argument to @alias");
     191          free (new); free (existing);
     192          break;
     193        }
     194      case CM_definfoenclose:
     195        {
     196          /* @definfoenclose phoo,//,\\ */
     197          char *new_command = 0, *start = 0, *end = 0;
     198          enum command_id new_cmd;
     199          int len;
     200  
     201          new_command = read_command_name (&line);
     202          if (!new_command)
     203            goto definfoenclose_invalid;
     204  
     205          line += strspn (line, whitespace_chars);
     206          if (*line != ',')
     207            goto definfoenclose_invalid;
     208          line++;
     209          line += strspn (line, whitespace_chars);
     210  
     211          /* TODO: Can we have spaces in the delimiters? */
     212          len = strcspn (line, ",");
     213          start = strndup (line, len);
     214          line += len;
     215  
     216          if (!*line)
     217            goto definfoenclose_invalid; /* Not enough args. */
     218          line++; /* Past ','. */
     219          line += strspn (line, whitespace_chars);
     220          len = strcspn (line, ",");
     221          end = strndup (line, len);
     222  
     223          if (*line == ',')
     224            goto definfoenclose_invalid; /* Too many args. */
     225  
     226          /* Remember it. */
     227          new_cmd = add_texinfo_command (new_command);
     228          add_infoenclose (new_cmd, start, end);
     229          debug ("DEFINFOENCLOSE @%s: %s, %s", new_command, start, end);
     230          new_cmd &= ~USER_COMMAND_BIT;
     231  
     232          user_defined_command_data[new_cmd].flags
     233            |= (CF_INFOENCLOSE | CF_brace);
     234          user_defined_command_data[new_cmd].data = BRACE_style_other;
     235          user_defined_command_data[new_cmd].args_number = 1;
     236  
     237          ADD_ARG(new_command); free (new_command);
     238          ADD_ARG(start); free (start);
     239          ADD_ARG(end); free (end);
     240  
     241          break;
     242        definfoenclose_invalid:
     243          line_error ("bad argument to @definfoenclose");
     244          free (new_command); free (start); free (end);
     245          break;
     246        }
     247      case CM_columnfractions:
     248        {
     249          /*  @multitable @columnfractions .33 .33 .33 */
     250          ELEMENT *new;
     251          char *p, *q;
     252  
     253          if (!*line)
     254            {
     255              line_error ("empty @columnfractions");
     256              break;
     257            }
     258          p = line;
     259          while (1)
     260            {
     261              char *arg;
     262  
     263              p += strspn (p, whitespace_chars);
     264              if (!*p)
     265                break;
     266              q = strpbrk (p, whitespace_chars);
     267              if (!q)
     268                q = p + strlen (p);
     269              
     270              arg = strndup (p, q - p);
     271  
     272              /* Check argument is valid. */
     273              if (!is_decimal_number (arg))
     274                {
     275                  line_error ("column fraction not a number: %s", arg);
     276                }
     277              else
     278                {
     279                  new = new_element (ET_NONE);
     280                  text_append_n (&new->text, p, q - p);
     281                  add_to_element_contents (line_args, new);
     282                }
     283              free (arg);
     284              p = q;
     285            }
     286          break;
     287        }
     288      case CM_sp:
     289        {
     290          /* Argument is at least one digit. */
     291          if (strchr (digit_chars, *line)
     292              && !*(line + 1 + strspn (line + 1, digit_chars)))
     293            {
     294              ADD_ARG(line);
     295            }
     296          else
     297            line_error ("@sp arg must be numeric, not `%s'", line);
     298          break;
     299        }
     300      case CM_defindex:
     301      case CM_defcodeindex:
     302        {
     303          char *name = 0;
     304          char *p = line;
     305  
     306          name = read_command_name (&p);
     307          if (*p)
     308            goto defindex_invalid; /* Trailing characters. */
     309  
     310          /* Disallow index names NAME where it is likely that for
     311             a source file BASE.texi, there will be other files called
     312             BASE.NAME in the same directory.  This is to prevent such
     313             files being overwritten by the files read by texindex. */
     314          {
     315            static char *forbidden_index_names[] = {
     316              "cp", "fn", "ky", "pg", "tp", "vr",
     317              "cps", "fns", "kys", "pgs", "tps", "vrs",
     318              "info", "ps", "pdf", "htm", "html",
     319              "log", "aux", "dvi", "texi", "txi",
     320              "texinfo", "tex", "bib", 0
     321            };
     322            char **ptr;
     323            for (ptr = forbidden_index_names; *ptr; ptr++)
     324              if (!strcmp (name, *ptr))
     325                goto defindex_reserved;
     326          }
     327  
     328          add_index (name, cmd == CM_defcodeindex ? 1 : 0);
     329          ADD_ARG(name);
     330  
     331          free (name);
     332          break;
     333        defindex_invalid:
     334          line_error ("bad argument to @%s: %s",
     335                       command_name(cmd), line);
     336          free (name);
     337          break;
     338        defindex_reserved:
     339          line_error ("reserved index name %s", name);
     340          free (name);
     341          break;
     342        }
     343      case CM_synindex:
     344      case CM_syncodeindex:
     345        {
     346          /* synindex FROM TO */
     347          char *from = 0, *to = 0;
     348          INDEX *from_index, *to_index;
     349          char *p = line;
     350  
     351          if (!isascii_alnum (*p))
     352            goto synindex_invalid;
     353          from = read_command_name (&p);
     354          if (!from)
     355            goto synindex_invalid;
     356  
     357          p += strspn (p, whitespace_chars);
     358  
     359          if (!isascii_alnum (*p))
     360            goto synindex_invalid;
     361          to = read_command_name (&p);
     362          if (!to)
     363            goto synindex_invalid;
     364          if (*p)
     365            goto synindex_invalid; /* More at end of line. */
     366  
     367          from_index = index_by_name (from);
     368          to_index = index_by_name (to);
     369          if (!from_index)
     370            line_error ("unknown source index in @%s: %s",
     371                        command_name(cmd), from);
     372          if (!to_index)
     373            line_error ("unknown destination index in @%s: %s",
     374                        command_name(cmd), to);
     375  
     376          if (from_index && to_index)
     377            {
     378              INDEX *current_to = to_index;
     379              /* Find ultimate index this merges to. */
     380              current_to = ultimate_index (current_to);
     381  
     382              if (current_to != from_index)
     383                {
     384                  from_index->merged_in = current_to;
     385                  from_index->in_code = (cmd == CM_syncodeindex);
     386                  ADD_ARG(from);
     387                  ADD_ARG(to);
     388                  /* Note that 'current_to' may not end up as the index
     389                     'from_index' merges into if there are further @synindex 
     390                     commands. */
     391                }
     392              else
     393                line_warn ("@%s leads to a merging of %s in itself, ignoring",
     394                            command_name(cmd), from);
     395            }
     396  
     397          free (from);
     398          free (to);
     399  
     400          break;
     401        synindex_invalid:
     402          line_error ("bad argument to @%s: %s",
     403                       command_name(cmd), line);
     404          free (from); free (to);
     405          break;
     406        }
     407      case CM_printindex:
     408        {
     409          char *arg;
     410          char *p = line;
     411          arg = read_command_name (&p);
     412          if (!arg || *p)
     413            line_error ("bad argument to @printindex: %s", line);
     414          else
     415            {
     416              INDEX *idx = index_by_name (arg);
     417              if (!idx)
     418                line_error ("unknown index `%s' in @printindex", arg);
     419              else
     420                {
     421                  if (idx->merged_in)
     422                    {
     423                      INDEX *i2;
     424                      for (i2 = idx; (i2->merged_in); i2 = i2->merged_in)
     425                        ;
     426                      line_warn
     427                        ("printing an index `%s' merged in another one, `%s'",
     428                         arg, i2->name);
     429                    }
     430                  if (!current_node && !current_section
     431                        && nesting_context.regions_stack.top == 0)
     432                    {
     433                      line_warn ("printindex before document beginning: "
     434                                  "@printindex %s", arg);
     435                    }
     436                  ADD_ARG (arg);
     437                }
     438            }
     439          free (arg);
     440          break;
     441        }
     442      case CM_everyheadingmarks:
     443      case CM_everyfootingmarks:
     444      case CM_evenheadingmarks:
     445      case CM_oddheadingmarks:
     446      case CM_evenfootingmarks:
     447      case CM_oddfootingmarks:
     448        {
     449          if (!strcmp (line, "top") || !strcmp (line, "bottom"))
     450            {
     451              ADD_ARG (line);
     452            }
     453          else
     454            line_error ("@%s arg must be `top' or `bottom', not `%s'",
     455                         command_name(cmd), line);
     456  
     457          break;
     458        }
     459      case CM_fonttextsize:
     460        {
     461          if (!strcmp (line, "10") || !strcmp (line, "11"))
     462            {
     463              ADD_ARG (line);
     464            }
     465          else
     466            line_error ("Only @fonttextsize 10 or 11 is supported, not "
     467                         "`%s'", line);
     468          break;
     469        }
     470      case CM_footnotestyle:
     471        {
     472          if (!strcmp (line, "separate") || !strcmp (line, "end"))
     473            {
     474              ADD_ARG(line);
     475            }
     476          else
     477            line_error ("@footnotestyle arg must be "
     478                         "`separate' or `end', not `%s'", line);
     479          break;
     480        }
     481      case CM_setchapternewpage:
     482        {
     483          if (!strcmp (line, "on") || !strcmp (line, "off")
     484              || !strcmp (line, "odd"))
     485            {
     486              ADD_ARG(line);
     487            }
     488          else
     489            line_error ("@setchapternewpage arg must be "
     490                         "`on', `off' or `odd', not `%s'", line);
     491          break;
     492        }
     493      case CM_need:
     494        {
     495          /* valid: 2, 2., .2, 2.2 */
     496  
     497          if (is_decimal_number (line))
     498            ADD_ARG(line);
     499          else
     500            line_error ("bad argument to @need: %s", line);
     501  
     502          break;
     503        }
     504      case CM_paragraphindent:
     505        {
     506          if (!strcmp (line, "none") || !strcmp (line, "asis")
     507              || is_whole_number (line))
     508            ADD_ARG(line);
     509          else
     510            line_error ("@paragraphindent arg must be "
     511                         "numeric/`none'/`asis', not `%s'", line);
     512          break;
     513        }
     514      case CM_firstparagraphindent:
     515        {
     516          if (!strcmp (line, "none") || !strcmp (line, "insert"))
     517            {
     518              ADD_ARG(line);
     519            }
     520          else
     521            line_error ("@firstparagraphindent arg must be "
     522                         "`none' or `insert', not `%s'", line);
     523  
     524          break;
     525        }
     526      case CM_exampleindent:
     527        {
     528          if (!strcmp (line, "asis") || is_whole_number (line))
     529            ADD_ARG(line);
     530          else
     531            line_error ("@exampleindent arg must be "
     532                         "numeric/`asis', not `%s'", line);
     533          break;
     534        }
     535      case CM_frenchspacing:
     536      case CM_xrefautomaticsectiontitle:
     537      case CM_codequoteundirected:
     538      case CM_codequotebacktick:
     539      case CM_deftypefnnewline:
     540      case CM_microtype:
     541        {
     542          if (!strcmp (line, "on") || !strcmp (line, "off"))
     543            {
     544              ADD_ARG(line);
     545            }
     546          else
     547            line_error ("expected @%s on or off, not `%s'",
     548                        command_name(cmd), line);
     549  
     550          break;
     551        }
     552      case CM_kbdinputstyle:
     553        {
     554          if (!strcmp (line, "code"))
     555            global_kbdinputstyle = kbd_code;
     556          else if (!strcmp (line, "example"))
     557            global_kbdinputstyle = kbd_example;
     558          else if (!strcmp (line, "distinct"))
     559            global_kbdinputstyle = kbd_distinct;
     560          else goto kdbinputstyle_invalid;
     561  
     562          ADD_ARG(line);
     563  
     564          if (0)
     565            {
     566          kdbinputstyle_invalid:
     567            line_error ("@kbdinputstyle arg must be "
     568                         "`code'/`example'/`distinct', not `%s'", line);
     569            }
     570          break;
     571        }
     572      case CM_allowcodebreaks:
     573        {
     574          if (!strcmp (line, "true") || !strcmp (line, "false"))
     575            {
     576              ADD_ARG(line);
     577            }
     578          else
     579            line_error ("@allowcodebreaks arg must be "
     580                         "`true' or `false', not `%s'", line);
     581          break;
     582        }
     583      case CM_urefbreakstyle:
     584        {
     585          if (!strcmp (line, "after") || !strcmp (line, "before")
     586              || !strcmp (line, "none"))
     587            {
     588              ADD_ARG(line);
     589            }
     590          else
     591            line_error ("@urefbreakstyle arg must be "
     592                         "`after'/`before'/`none', not `%s'", line);
     593          break;
     594        }
     595      case CM_headings:
     596        {
     597          if (!strcmp (line, "off") || !strcmp (line, "on")
     598              || !strcmp (line, "single") || !strcmp (line, "double")
     599              || !strcmp (line, "singleafter") || !strcmp (line, "doubleafter"))
     600            {
     601              ADD_ARG(line);
     602            }
     603          else
     604            line_error ("bad argument to @headings: %s", line);
     605          break;
     606        }
     607      default:
     608        ;
     609      }
     610    if (line_args->contents.number == 0)
     611      {
     612        destroy_element (line_args);
     613        return 0;
     614      }
     615    else
     616      return line_args;
     617  
     618  #undef ADD_ARG
     619  }
     620  
     621  /* for now done in Texinfo::Convert::NodeNameNormalization, but could be
     622     good to do in Parser/XS */
     623  /* Array of recorded @float's. */
     624  
     625  FLOAT_RECORD *floats_list = 0;
     626  size_t floats_number = 0;
     627  size_t floats_space = 0;
     628  
     629  
     630  char *
     631  parse_float_type (ELEMENT *current)
     632  {
     633    char *normalized;
     634    if (current->args.number > 0)
     635      {
     636        /* TODO convert_to_texinfo is incorrect here, conversion should follow
     637           code of Texinfo::Convert::NodeNameNormalization::convert_to_normalized */
     638        normalized = convert_to_texinfo (current->args.list[0]);
     639      }
     640    else
     641      normalized = strdup ("");
     642    add_extra_string (current, "float_type", normalized);
     643    return normalized;
     644  }
     645  
     646  ELEMENT *
     647  end_line_def_line (ELEMENT *current)
     648  {
     649    enum command_id def_command;
     650    DEF_ARG **def_info = 0;
     651    KEY_PAIR *k;
     652    ELEMENT *index_entry = 0; /* Index entry text. */
     653    ELEMENT *def_info_name = 0;
     654    ELEMENT *def_info_class = 0;
     655    ELEMENT *def_info_category = 0;
     656    int i = 0;
     657    enum context top_context = pop_context ();
     658  
     659    if (top_context != ct_def)
     660      fatal ("def context expected");
     661  
     662    k = lookup_extra (current->parent, "def_command");
     663    def_command = lookup_command ((char *) k->value);
     664  
     665    debug_nonl ("END DEF LINE %s; current ",
     666                 command_name(def_command));
     667    debug_print_element (current, 1); debug ("");
     668  
     669    def_info = parse_def (def_command, current);
     670  
     671    /* def_line */
     672    current = current->parent;
     673  
     674    /* Record the index entry if def_info is not empty. */
     675  
     676    while (def_info[i] != 0 && def_info[i]->element != 0)
     677      {
     678        if (!strcmp(def_info[i]->arg_type, "name"))
     679          def_info_name = def_info[i]->element;
     680        else if (!strcmp(def_info[i]->arg_type, "class"))
     681          def_info_class = def_info[i]->element;
     682        else if (!strcmp(def_info[i]->arg_type, "category"))
     683          def_info_category = def_info[i]->element;
     684        free (def_info[i]->arg_type);
     685        free (def_info[i]);
     686        i++;
     687      }
     688    free (def_info);
     689  
     690    if (def_info_category)
     691      {
     692        if (def_info_name)
     693          {
     694            char *t;
     695            /* Set index_entry unless an empty ET_bracketed_arg. */
     696            if (def_info_name->type == ET_bracketed_arg
     697                && (def_info_name->contents.number == 0
     698                    || (def_info_name->contents.number == 1
     699                        && (t = def_info_name->contents.list[0]->text.text)
     700                        && t[strspn (t, whitespace_chars)] == '\0')))
     701              {
     702              }
     703            else
     704              index_entry = def_info_name;
     705          }
     706  
     707        if (index_entry)
     708          {
     709            if (def_info_class &&
     710                (def_command == CM_defop
     711                    || def_command == CM_deftypeop
     712                    || def_command == CM_defmethod
     713                    || def_command == CM_deftypemethod
     714                    || def_command == CM_defivar
     715                    || def_command == CM_deftypeivar
     716                    || def_command == CM_deftypecv))
     717              {
     718                /* def_index_element will be set in
     719                   Texinfo::Translations::complete_indices */
     720                if (global_documentlanguage)
     721                  add_extra_string_dup (current, "documentlanguage",
     722                                        global_documentlanguage);
     723              }
     724            else
     725              {
     726                add_extra_element (current, "def_index_element",
     727                                   index_entry);
     728              }
     729  
     730            if (def_command != CM_defline && def_command != CM_deftypeline)
     731              enter_index_entry (def_command, current);
     732          }
     733        else
     734          {
     735            k = lookup_extra (current, "original_def_cmdname");
     736            command_warn (current, "missing name for @%s", (char *) k->value);
     737          }
     738      }
     739    else
     740      {
     741        k = lookup_extra (current, "original_def_cmdname");
     742        command_warn (current, "missing category for @%s", (char *) k->value);
     743      }
     744  
     745  
     746    current = current->parent;
     747    current = begin_preformatted (current);
     748  
     749    return current;
     750  }
     751  
     752  /* Actions to be taken at the end of a line that started a block that
     753     has to be ended with "@end". */
     754  ELEMENT *
     755  end_line_starting_block (ELEMENT *current)
     756  {
     757    KEY_PAIR *k;
     758  
     759    enum command_id command;
     760  
     761    if (current->parent->type == ET_def_line)
     762      command = current->parent->parent->cmd;
     763    else
     764      command = current->parent->cmd;
     765  
     766    if (command_data(command).flags & CF_contain_basic_inline)
     767        (void) pop_command (&nesting_context.basic_inline_stack_block);
     768    isolate_last_space (current);
     769  
     770    if (current->parent->type == ET_def_line)
     771      return end_line_def_line (current);
     772  
     773    if (pop_context () != ct_line)
     774      fatal ("line context expected");
     775  
     776    debug_nonl ("END BLOCK LINE: ");
     777    debug_print_element (current, 1); debug ("");
     778  
     779    /* @multitable args */
     780    if (command == CM_multitable
     781        && (k = lookup_extra (current->parent, "columnfractions")))
     782      {
     783        ELEMENT *misc_cmd = (ELEMENT *) k->value;
     784        ELEMENT *misc_args;
     785  
     786        if ((misc_args = lookup_extra_element (misc_cmd, "misc_args")))
     787          {
     788            add_extra_integer (current->parent, "max_columns",
     789                               misc_args->contents.number);
     790          }
     791        else
     792          {
     793            add_extra_integer (current->parent, "max_columns", 0);
     794            k->key = "";
     795            k->type = extra_deleted;
     796          }
     797      }
     798    else if (command == CM_multitable)
     799      {
     800        int i;
     801        int max_columns = 0;
     802  
     803        for (i = 0; i < current->contents.number; i++)
     804          {
     805            ELEMENT *e = contents_child_by_index(current, i);
     806  
     807            if (e->type == ET_bracketed_arg)
     808              {
     809                max_columns++;
     810              }
     811            else if (e->text.end > 0)
     812              {
     813                /*
     814                TODO: this should be a warning or an error - all prototypes
     815                on a @multitable line should be in braces, as documented in the
     816                Texinfo manual.
     817                 */
     818              }
     819            else
     820              {
     821                if (e->cmd != CM_c && e->cmd != CM_comment)
     822                  {
     823                    char *texi;
     824                    texi = convert_to_texinfo (e);
     825                    command_warn (current,
     826                                  "unexpected argument on @%s line: %s",
     827                                  command_name(current->parent->cmd),
     828                                  texi);
     829                    free (texi);
     830                  }
     831              }
     832          }
     833  
     834        {
     835        add_extra_integer (current->parent, "max_columns", max_columns);
     836        if (max_columns == 0)
     837          command_warn (current->parent, "empty multitable");
     838        }
     839      }
     840  
     841    current = current->parent;
     842    if (counter_value (&count_remaining_args, current) != -1)
     843      counter_pop (&count_remaining_args);
     844  
     845    if (command == CM_float)
     846      {
     847        /* char *float_type = ""; */
     848        ELEMENT *float_label_element = 0;
     849        current->source_info = current_source_info;
     850        if (current->args.number >= 2)
     851          {
     852            float_label_element = args_child_by_index (current, 1);
     853          }
     854        check_register_target_element_label (float_label_element, current);
     855        /* for now done in Texinfo::Convert::NodeNameNormalization, but could be
     856           good to do in Parser/XS */
     857        /*
     858        float_type = parse_float_type (current);
     859        */
     860        /* add to global 'floats' array */
     861        /*
     862        if (floats_number == floats_space)
     863          {
     864            floats_list = realloc (floats_list,
     865                                   (floats_space += 5) * sizeof (FLOAT_RECORD));
     866          }
     867        floats_list[floats_number].type = float_type;
     868        floats_list[floats_number++].element = current;
     869        */
     870        if (current_section)
     871          add_extra_element (current, "float_section", current_section);
     872      }
     873    else if (command_data(command).flags & CF_blockitem)
     874      {
     875        KEY_PAIR *k;
     876        if (command == CM_enumerate)
     877          {
     878            char *spec = "1";
     879  
     880            if (current->args.number > 0
     881                && current->args.list[0]->contents.number > 0)
     882              {
     883                ELEMENT *g;
     884                if (current->args.list[0]->contents.number > 1)
     885                  command_error (current, "superfluous argument to @%s",
     886                                 command_name(current->cmd));
     887                g = current->args.list[0]->contents.list[0];
     888                /* Check if @enumerate specification is either a single
     889                   letter or a string of digits. */
     890                if ((g->text.end == 1
     891                      && isascii_alpha (g->text.text[0]))
     892                    || (g->text.end > 0
     893                        && !*(g->text.text
     894                              + strspn (g->text.text, digit_chars))))
     895                  {
     896                    spec = g->text.text;
     897                  }
     898                else
     899                  command_error (current, "bad argument to @%s",
     900                                 command_name(command));
     901              }
     902            add_extra_string_dup (current, "enumerate_specification", spec);
     903          }
     904        else if (item_line_command (command))
     905          {
     906            KEY_PAIR *k;
     907            k = lookup_extra (current, "command_as_argument");
     908            if (!k)
     909              {
     910                if (current->args.number > 0
     911                    && current->args.list[0]->contents.number > 0)
     912                  {
     913                    char *texi_arg;
     914  
     915                    /* expand the contents to avoid surrounding spaces */
     916                    texi_arg = convert_contents_to_texinfo (current->args.list[0]);
     917                    command_error (current, "bad argument to @%s: %s",
     918                                   command_name(command), texi_arg);
     919                    free (texi_arg);
     920                  }
     921                else
     922                  {
     923                    command_error (current, "missing @%s argument",
     924                                   command_name(command));
     925                  }
     926              }
     927            else
     928              {
     929                ELEMENT *e = (ELEMENT *) k->value;
     930                if (!(command_flags(e) & CF_brace)
     931                    || (command_data(e->cmd).data == BRACE_noarg))
     932                  {
     933                    command_error (current,
     934                                   "command @%s not accepting argument in brace "
     935                                   "should not be on @%s line",
     936                                   command_name(e->cmd),
     937                                   command_name(command));
     938                    k->key = "";
     939                    k->type = extra_deleted;
     940                  }
     941              }
     942          }
     943  
     944        /* check that command_as_argument of the @itemize is alone on the line,
     945           otherwise it is not a command_as_argument */
     946        else if (command == CM_itemize)
     947          {
     948            KEY_PAIR *k;
     949            k = lookup_extra (current, "command_as_argument");
     950            if (k)
     951              {
     952                int i;
     953                ELEMENT *e = args_child_by_index (current, 0);
     954  
     955                for (i = 0; i < e->contents.number; i++)
     956                  {
     957                    if (contents_child_by_index (e, i) == (ELEMENT *) k->value)
     958                      {
     959                        i++;
     960                        break;
     961                      }
     962                  }
     963                for (; i < e->contents.number; i++)
     964                  {
     965                    ELEMENT *f = contents_child_by_index (e, i);
     966                    if (f->cmd != CM_c
     967                        && f->cmd != CM_comment
     968                        && !(f->text.end > 0
     969                             && !*(f->text.text
     970                                   + strspn (f->text.text, whitespace_chars))))
     971                      {
     972                        ((ELEMENT *) k->value)->type = ET_NONE;
     973                        k->key = "";
     974                        k->type = extra_deleted;
     975                        break;
     976                      }
     977                  }
     978              }
     979          }
     980  
     981        /* Check if command_as_argument isn't an accent command */
     982        k = lookup_extra (current, "command_as_argument");
     983        if (k && k->value)
     984          {
     985            enum command_id cmd = ((ELEMENT *) k->value)->cmd;
     986            if (cmd && (command_data(cmd).flags & CF_accent))
     987              {
     988                command_warn (current, "accent command `@%s' "
     989                              "not allowed as @%s argument",
     990                              command_name(cmd),
     991                              command_name(command));
     992                k->key = "";
     993                k->value = 0;
     994                k->type = extra_deleted;
     995              }
     996          }
     997        /* if no command_as_argument given, default to @bullet for
     998           @itemize, and @asis for @table. */
     999        if (command == CM_itemize
    1000            && (current->args.number == 0
    1001                || current->args.list[0]->contents.number == 0))
    1002          {
    1003            ELEMENT *e;
    1004            ELEMENT *block_line_arg;
    1005            if (last_args_child(current)
    1006                && last_args_child(current)->type == ET_block_line_arg)
    1007              {
    1008                block_line_arg = last_args_child(current);
    1009              }
    1010            else
    1011              {
    1012                block_line_arg = new_element (ET_block_line_arg);
    1013                insert_into_args (current, block_line_arg, 0);
    1014              }
    1015  
    1016            e = new_element (ET_command_as_argument_inserted);
    1017            e->cmd = CM_bullet;
    1018            insert_into_contents (block_line_arg, e, 0);
    1019            add_extra_element (current, "command_as_argument", e);
    1020          }
    1021        else if (item_line_command (command)
    1022            && !lookup_extra (current, "command_as_argument"))
    1023          {
    1024            ELEMENT *e;
    1025  
    1026            e = new_element (ET_command_as_argument_inserted);
    1027            e->cmd = CM_asis;
    1028            insert_into_args (current, e, 0);
    1029            add_extra_element (current, "command_as_argument", e);
    1030          }
    1031  
    1032        {
    1033          ELEMENT *bi = new_element (ET_before_item);
    1034          add_to_element_contents (current, bi);
    1035          current = bi;
    1036        }
    1037      } /* CF_blockitem */
    1038    else if (command_data (command).args_number == 0
    1039             && (! (command_data (command).flags & CF_variadic))
    1040             && current->args.number > 0
    1041             && current->args.list[0]->contents.number > 0)
    1042      {
    1043        char *texi_arg;
    1044  
    1045        /* expand the contents to avoid surrounding spaces */
    1046        texi_arg = convert_contents_to_texinfo (current->args.list[0]);
    1047        command_warn (current, "unexpected argument on @%s line: %s",
    1048                       command_name(command), texi_arg);
    1049        free (texi_arg);
    1050      }
    1051  
    1052    if (command_data(command).data == BLOCK_conditional)
    1053      {
    1054        int iftrue = 0; /* Whether the conditional is true. */
    1055        int bad_line = 1;
    1056        if (command == CM_ifclear || command == CM_ifset
    1057            || command == CM_ifcommanddefined
    1058            || command == CM_ifcommandnotdefined)
    1059          {
    1060            if (current->args.number == 1
    1061                && current->args.list[0]->contents.number == 1)
    1062              {
    1063                ELEMENT *arg_elt = current->args.list[0]->contents.list[0];
    1064                if (arg_elt->text.end > 0)
    1065                  {
    1066                    char *name = arg_elt->text.text;
    1067                    char *p = name + strspn (name, whitespace_chars);
    1068                    if (!*p)
    1069                      {
    1070                        line_error ("@%s requires a name", command_name(command));
    1071                        bad_line = 0;
    1072                      }
    1073                    else
    1074                      {
    1075                        char *p = name;
    1076                        char *flag = read_flag_name (&p);
    1077                        if (flag && !*p)
    1078                          {
    1079                            bad_line = 0;
    1080                            if (command == CM_ifclear || command == CM_ifset)
    1081                              {
    1082                                char *val = fetch_value (flag);
    1083                                if (val)
    1084                                  iftrue = 1;
    1085                                if (command == CM_ifclear)
    1086                                  iftrue = !iftrue;
    1087                              }
    1088                            else /* command == CM_ifcommanddefined
    1089                                    || command == CM_ifcommandnotdefined */
    1090                              {
    1091                                enum command_id c = lookup_command (flag);
    1092                                if (c)
    1093                                  iftrue = 1;
    1094                                if (command == CM_ifcommandnotdefined)
    1095                                  iftrue = !iftrue;
    1096                              }
    1097                            debug ("CONDITIONAL @%s %s: %d",
    1098                                   command_name(command), flag, iftrue);
    1099                          }
    1100                        free (flag);
    1101                      }
    1102                  }
    1103              }
    1104            else
    1105              {
    1106                line_error ("@%s requires a name", command_name(command));
    1107                bad_line = 0;
    1108              }
    1109            if (bad_line)
    1110              line_error ("bad name for @%s", command_name(command));
    1111          }
    1112        else if (!memcmp (command_name(command), "if", 2)) /* e.g. @ifhtml */
    1113          {
    1114            int i; char *p;
    1115            /* Handle @if* and @ifnot* */
    1116  
    1117            p = command_name(command) + 2; /* After "if". */
    1118            if (!memcmp (p, "not", 3))
    1119              p += 3; /* After "not". */
    1120            for (i = 0; i < sizeof (expanded_formats)/sizeof (*expanded_formats);
    1121                 i++)
    1122              {
    1123                if (!strcmp (p, expanded_formats[i].format))
    1124                  {
    1125                    iftrue = expanded_formats[i].expandedp;
    1126                    break;
    1127                  }
    1128              }
    1129            if (!memcmp (command_name(command), "ifnot", 5))
    1130              iftrue = !iftrue;
    1131            debug ("CONDITIONAL @%s format %s: %d", command_name(command),
    1132                   p, iftrue);
    1133          }
    1134        else
    1135          bug_message ("unknown conditional command @%s", command_name(command));
    1136  
    1137        if (iftrue)
    1138          {
    1139            ELEMENT *e;
    1140            SOURCE_MARK *source_mark;
    1141            current = current->parent;
    1142            e = pop_element_from_contents (current);
    1143            e->parent = 0;
    1144            source_mark = new_source_mark (SM_type_expanded_conditional_command);
    1145            source_mark->status = SM_status_start;
    1146            source_mark->element = e;
    1147            register_source_mark (current, source_mark);
    1148            debug ("PUSH BEGIN COND %s", command_name(command));
    1149            push_conditional_stack (command, source_mark);
    1150          }
    1151      }
    1152  
    1153    if (command_data(command).data == BLOCK_menu)
    1154      {
    1155        /* Start reading a menu.  Processing will continue in
    1156           in menus.c. */
    1157  
    1158        ELEMENT *menu_comment = new_element (ET_menu_comment);
    1159        add_to_element_contents (current, menu_comment);
    1160        current = menu_comment;
    1161        debug ("MENU_COMMENT OPEN");
    1162      }
    1163    if (command_data(command).data == BLOCK_format_raw
    1164        && format_expanded_p (command_name(command)))
    1165      {
    1166        ELEMENT *rawpreformatted = new_element (ET_rawpreformatted);
    1167        add_to_element_contents (current, rawpreformatted);
    1168        current = rawpreformatted;
    1169      }
    1170    if (command_data(command).data != BLOCK_raw
    1171        && command_data(command).data != BLOCK_conditional)
    1172      current = begin_preformatted (current);
    1173  
    1174    return current;
    1175  }
    1176  
    1177  /* Actions to be taken at the end of an argument to a line command
    1178     not starting a block.  @end is processed in here. */
    1179  ELEMENT *
    1180  end_line_misc_line (ELEMENT *current)
    1181  {
    1182    enum command_id cmd;
    1183    enum command_id data_cmd;
    1184    int arg_spec;
    1185    ELEMENT *misc_cmd;
    1186    char *end_command = 0;
    1187    enum command_id end_id = CM_NONE;
    1188    int included_file = 0;
    1189    SOURCE_MARK *include_source_mark = 0;
    1190  
    1191    data_cmd = cmd = current->parent->cmd;
    1192    /* we are in a command line context, so the @item command information is
    1193       associated to CM_item_LINE */
    1194    if (cmd == CM_item)
    1195      data_cmd = CM_item_LINE;
    1196  
    1197    if (command_data(data_cmd).flags & CF_contain_basic_inline)
    1198      (void) pop_command (&nesting_context.basic_inline_stack_on_line);
    1199    isolate_last_space (current);
    1200  
    1201    if (current->parent->type == ET_def_line)
    1202      return end_line_def_line (current);
    1203  
    1204    current = current->parent;
    1205    misc_cmd = current;
    1206  
    1207    arg_spec = command_data(data_cmd).data;
    1208  
    1209    debug ("MISC END %s", command_name(cmd));
    1210  
    1211    if (pop_context () != ct_line)
    1212      fatal ("line context expected");
    1213  
    1214    if (arg_spec == LINE_specific)
    1215      {
    1216        ELEMENT *args = parse_line_command_args (current);
    1217        if (args)
    1218          add_extra_misc_args (current, "misc_args", args);
    1219      }
    1220    else if (arg_spec == LINE_text)
    1221      {
    1222        char *text = 0;
    1223        int superfluous_arg = 0;
    1224  
    1225        if (current->args.number > 0)
    1226          text = convert_to_text (current->args.list[0], &superfluous_arg);
    1227  
    1228        if (!text || !strcmp (text, ""))
    1229          {
    1230            if (!superfluous_arg)
    1231              line_warn ("@%s missing argument", command_name(cmd));
    1232            add_extra_integer (current, "missing_argument", 1);
    1233            free (text);
    1234          }
    1235        else
    1236          {
    1237            add_extra_string (current, "text_arg", text);
    1238            if (current->cmd == CM_end)
    1239              {
    1240                char *line = text;
    1241  
    1242                /* Set end_command - used below. */
    1243                end_command = read_command_name (&line);
    1244                if (end_command)
    1245                  {
    1246                    /* Check if argument is a block Texinfo command. */
    1247                    end_id = lookup_command (end_command);
    1248                    if (end_id == 0 || !(command_data(end_id).flags & CF_block))
    1249                      {
    1250                        command_warn (current, "unknown @end %s", end_command);
    1251                        free (end_command); end_command = 0;
    1252                      }
    1253                    else
    1254                      {
    1255                        debug ("END BLOCK @end %s", end_command);
    1256  
    1257                        /* If there is superfluous text after @end argument, set
    1258                           superfluous_arg such that the error message triggered by an
    1259                           unexpected @-command on the @end line is issued below.  Note
    1260                           that superfluous_arg may also be true if it was set above. */
    1261                        if (end_command
    1262                            && line[strspn (line, whitespace_chars)] != '\0')
    1263                            superfluous_arg = 1;
    1264                      }
    1265                  }
    1266                /* if superfluous_arg is set there is a similar and somewhat
    1267                   better error message below */
    1268                else if (!superfluous_arg)
    1269                  {
    1270                    command_error (current, "bad argument to @end: %s", line);
    1271                  }
    1272              }
    1273            else if (superfluous_arg)
    1274              {
    1275                /* An error message is issued below. */
    1276              }
    1277            else if (current->cmd == CM_include)
    1278              {
    1279                int status;
    1280                char *fullpath, *sys_filename;
    1281  
    1282                sys_filename = encode_file_name (text);
    1283                fullpath = locate_include_file (sys_filename);
    1284  
    1285                if (!fullpath)
    1286                  {
    1287                    command_error (current,
    1288                                   "@include: could not find %s", text);
    1289                  }
    1290                else
    1291                  {
    1292                    status = input_push_file (fullpath);
    1293                    if (status)
    1294                      {
    1295                        char *decoded_file_path
    1296                           = convert_to_utf8 (strdup(fullpath));
    1297                        command_error (current,
    1298                                       "@include: could not open %s: %s",
    1299                                       decoded_file_path,
    1300                                       strerror (status));
    1301                        free (decoded_file_path);
    1302                      }
    1303                    else
    1304                      {
    1305                        included_file = 1;
    1306                        debug ("Included %s", fullpath);
    1307  
    1308                        include_source_mark = new_source_mark (SM_type_include);
    1309                        include_source_mark->status = SM_status_start;
    1310                        set_input_source_mark (include_source_mark);
    1311                      }
    1312                    free (fullpath);
    1313                  }
    1314              }
    1315            else if (current->cmd == CM_verbatiminclude)
    1316              {
    1317                if (global_input_encoding_name)
    1318                  add_extra_string_dup (current, "input_encoding_name",
    1319                                        global_input_encoding_name);
    1320              }
    1321            else if (current->cmd == CM_documentencoding)
    1322              {
    1323                int i; char *p, *normalized_text, *q;
    1324                int encoding_set;
    1325                char *input_encoding = 0;
    1326                int possible_encoding = 0;
    1327  
    1328                normalized_text = strdup (text);
    1329                q = normalized_text;
    1330                /* lower case, trim non-ascii characters and keep only alphanumeric
    1331                   characters, - and _.  iconv also seems to trim non alphanumeric
    1332                   non - _ characters */
    1333                for (p = text; *p; p++)
    1334                  {
    1335                    /* check if ascii and alphanumeric */
    1336                    if (isascii_alnum(*p))
    1337                      {
    1338                        possible_encoding = 1;
    1339                        *q = tolower (*p);
    1340                        q++;
    1341                      }
    1342                    else if (*p == '_' || *p == '-')
    1343                      {
    1344                        *q = *p;
    1345                        q++;
    1346                      }
    1347                  }
    1348                *q = '\0';
    1349  
    1350                if (! possible_encoding)
    1351                  command_warn (current, "bad encoding name `%s'",
    1352                                text);
    1353                else
    1354                  {
    1355  
    1356              /* Warn if the encoding is not one of the encodings supported as an
    1357                 argument to @documentencoding, documented in Texinfo manual */
    1358                    {
    1359                      char *texinfo_encoding = 0;
    1360                      static char *canonical_encodings[] = {
    1361                        "us-ascii", "utf-8", "iso-8859-1",
    1362                        "iso-8859-15","iso-8859-2","koi8-r", "koi8-u",
    1363                        0
    1364                      };
    1365                      char *text_lc;
    1366  
    1367                      text_lc = strdup (text);
    1368                      for (p = text_lc; *p; p++)
    1369                        if (isascii_alpha (*p))
    1370                          *p = tolower (*p);
    1371  
    1372                      for (i = 0; (canonical_encodings[i]); i++)
    1373                        {
    1374                          if (!strcmp (text_lc, canonical_encodings[i]))
    1375                            {
    1376                              texinfo_encoding = canonical_encodings[i];
    1377                              break;
    1378                            }
    1379                        }
    1380                      free (text_lc);
    1381                      if (!texinfo_encoding)
    1382                        {
    1383                          command_warn (current, "encoding `%s' is not a "
    1384                                      "canonical texinfo encoding", text);
    1385                        }
    1386                    }
    1387  
    1388                /* Set input_encoding -- for output within an HTML file, used
    1389                                         in most output formats */
    1390                    {
    1391                      struct encoding_map {
    1392                          char *from; char *to;
    1393                      };
    1394  
    1395                    /* In the perl parser,
    1396                       lc(Encode::find_encoding()->mime_name()) is used */
    1397                    /* the Perl Parser calls Encode::find_encoding, so knows
    1398                       about more encodings than what we know about here.
    1399                     */
    1400                      static struct encoding_map map[] = {
    1401                            "utf-8", "utf-8",
    1402                            "utf8", "utf-8",
    1403                            "ascii",  "us-ascii",
    1404                            "shiftjis", "shift_jis",
    1405                            "latin1", "iso-8859-1",
    1406                            "latin-1", "iso-8859-1",
    1407                            "iso-8859-1",  "iso-8859-1",
    1408                            "iso-8859-2",  "iso-8859-2",
    1409                            "iso-8859-15", "iso-8859-15",
    1410                            "koi8-r",      "koi8-r",
    1411                            "koi8-u",      "koi8-u",
    1412                      };
    1413                      for (i = 0; i < sizeof map / sizeof *map; i++)
    1414                        {
    1415                         /* Elements in first column map to elements in
    1416                            second column.  Elements in second column map
    1417                            to themselves. */
    1418                          if (!strcasecmp (normalized_text, map[i].from)
    1419                               || !strcasecmp (normalized_text, map[i].to))
    1420                            {
    1421                              input_encoding = map[i].to;
    1422                              break;
    1423                            }
    1424                        }
    1425                    }
    1426                    if (!input_encoding)
    1427                      {
    1428                        input_encoding = normalized_text;
    1429                      }
    1430  
    1431                    /* set_input_encoding also sets global_input_encoding_name */
    1432                    encoding_set = set_input_encoding (input_encoding);
    1433                    if (encoding_set)
    1434                      {
    1435                        add_extra_string_dup (current, "input_encoding_name",
    1436                                              input_encoding);
    1437                      }
    1438                    else
    1439                      command_warn (current, "unhandled encoding name `%s'",
    1440                                    text);
    1441                  }
    1442                free (normalized_text);
    1443              }
    1444            else if (current->cmd == CM_documentlanguage)
    1445              {
    1446                char *p, *q;
    1447  
    1448                /* Texinfo::Common::warn_unknown_language checks with
    1449                   tp/Texinfo/Documentlanguages.pm, which is an automatically
    1450                   generated list of official IANA language codes.  For now,
    1451                   just check if the language code looks right. */
    1452  
    1453                p = text;
    1454                while (isascii_alpha (*p))
    1455                  p++;
    1456                if (*p && *p != '_')
    1457                  {
    1458                     /* non-alphabetic char in language code */
    1459                    command_warn (current, "%s is not a valid language code",
    1460                                  text);
    1461                  }
    1462                else
    1463                  {
    1464                    if (p - text > 4)
    1465                      {
    1466                        /* looks too long */
    1467                        char saved = *p;
    1468                        *p = 0;
    1469                        command_warn (current, "%s is not a valid language code",
    1470                                      text);
    1471                        *p = saved;
    1472                      }
    1473                    if (*p == '_')
    1474                      {
    1475                        q = p + 1;
    1476                        p = q;
    1477                        /* Language code should be of the form LL_CC,
    1478                           language code followed by country code. */
    1479                        while (isascii_alpha (*p))
    1480                          p++;
    1481                        if (*p || p - q > 4)
    1482                          {
    1483                            /* non-alphabetic char in country code or code
    1484                               is too long. */
    1485                            command_warn (current,
    1486                                          "%s is not a valid region code", q);
    1487                          }
    1488                      }
    1489                  }
    1490                set_documentlanguage (text);
    1491              }
    1492          }
    1493        if (superfluous_arg)
    1494          {
    1495            char *texi_line, *p, *p1;
    1496            p = convert_to_texinfo (args_child_by_index(current, 0));
    1497  
    1498            texi_line = p;
    1499  
    1500            texi_line += strspn (texi_line, whitespace_chars);
    1501  
    1502            /* Trim leading and trailing whitespace. */
    1503            p1 = strchr (texi_line, '\0');
    1504            if (p1 > texi_line)
    1505              {
    1506                while (p1 > texi_line && strchr (whitespace_chars, p1[-1]))
    1507                  p1--;
    1508                *p1 = '\0';
    1509              }
    1510            command_error (current, "bad argument to @%s: %s", 
    1511                           command_name(current->cmd), texi_line);
    1512            free (p);
    1513          }
    1514      }
    1515    else if (current->cmd == CM_node)
    1516      {
    1517        int i;
    1518  
    1519        for (i = 1; i < current->args.number && i < 4; i++)
    1520          {
    1521            ELEMENT * arg = current->args.list[i];
    1522            NODE_SPEC_EXTRA *direction_label_info = parse_node_manual (arg, 1);
    1523            if (direction_label_info->node_content)
    1524              add_extra_contents (arg, "node_content",
    1525                                  direction_label_info->node_content);
    1526            if (direction_label_info->manual_content)
    1527              add_extra_contents (arg, "manual_content",
    1528                                  direction_label_info->manual_content);
    1529            free (direction_label_info);
    1530          }
    1531  
    1532        /* Now take care of the node itself */
    1533        check_register_target_element_label (current->args.list[0], current);
    1534  
    1535        if (current_part
    1536            && !lookup_extra (current_part, "part_associated_section"))
    1537          {
    1538           /* we only associate a part to the following node if the
    1539              part is not already associate to a sectioning command,
    1540              but the part can be associated to the sectioning command later
    1541              if a sectioning command follows the node. */
    1542            add_extra_element (current, "node_preceding_part", current_part);
    1543            add_extra_element (current_part, "part_following_node",
    1544                                   current);
    1545          }
    1546        current_node = current;
    1547      }
    1548    else if (current->cmd == CM_listoffloats)
    1549      {
    1550        /* for now done in Texinfo::Convert::NodeNameNormalization, but could be
    1551           good to do in Parser/XS */
    1552        /* parse_float_type (current); */
    1553      }
    1554    else
    1555      {
    1556        /* All the other "line" commands. Check they have an argument. Empty 
    1557           @top is allowed. */
    1558        if (current->args.list[0]->contents.number == 0
    1559            && current->cmd != CM_top)
    1560          {
    1561            command_warn (current, "@%s missing argument", 
    1562                          command_name(current->cmd));
    1563            add_extra_integer (current, "missing_argument", 1);
    1564          }
    1565        else
    1566          {
    1567            if ((current->parent->cmd == CM_ftable
    1568                 || current->parent->cmd == CM_vtable)
    1569                && (current->cmd == CM_item || current->cmd == CM_itemx))
    1570              {
    1571                enter_index_entry (current->parent->cmd,
    1572                                   current);
    1573              }
    1574            else if (command_flags(current) & CF_index_entry_command)
    1575            /* Index commands */
    1576              {
    1577                enter_index_entry (current->cmd, current);
    1578                current->type = ET_index_entry_command;
    1579              }
    1580            /* if there is a brace command interrupting an index or subentry
    1581               command, replace the internal internal_spaces_before_brace_in_index
    1582               text type with its final type depending on whether there is
    1583               text after the brace command. */
    1584            if ((command_flags(current) & CF_index_entry_command
    1585                  || current->cmd == CM_subentry))
    1586              {
    1587                if (lookup_extra (current, "sortas")
    1588                     || lookup_extra (current, "seealso")
    1589                     || lookup_extra (current, "seeentry"))
    1590                  set_non_ignored_space_in_index_before_command(current->args.list[0]);
    1591              }
    1592          }
    1593      }
    1594  
    1595    current = current->parent;
    1596    if (end_command) /* Set above */
    1597      {
    1598        /* More processing of @end */
    1599        ELEMENT *end_elt;
    1600  
    1601        debug ("END COMMAND %s", end_command);
    1602        free (end_command);
    1603  
    1604        /* Reparent the "@end" element to be a child of the block element. */
    1605        end_elt = pop_element_from_contents (current);
    1606  
    1607        /* If not a conditional */
    1608        if (command_data(end_id).data != BLOCK_conditional
    1609            /* ignored conditional */
    1610            || current->cmd == end_id
    1611            /* not a non-ignored conditional */
    1612            || (conditional_number == 0
    1613                || top_conditional_stack ()->command != end_id))
    1614          {
    1615            ELEMENT *closed_command;
    1616            /* This closes tree elements (e.g. paragraphs) until we reach
    1617               end_command.  It can print an error if another block command
    1618               is found first. */
    1619            current = close_commands (current, end_id, &closed_command, 0);
    1620            if (!closed_command)
    1621              destroy_element_and_children (end_elt);
    1622            else
    1623              {
    1624                close_command_cleanup (closed_command);
    1625  
    1626                add_to_element_contents (closed_command, end_elt);
    1627  
    1628                if (command_data(closed_command->cmd).data == BLOCK_menu
    1629                    && command_data(current_context_command()).data == BLOCK_menu)
    1630                  {
    1631                    ELEMENT *e;
    1632                    debug ("CLOSE menu but still in menu context");
    1633                    e = new_element (ET_menu_comment);
    1634                    add_to_element_contents (current, e);
    1635                    current = e;
    1636                  }
    1637              }
    1638            if (close_preformatted_command (end_id))
    1639              current = begin_preformatted (current);
    1640          }
    1641        else
    1642          {
    1643            /* If we are in a non-ignored conditional, there is not
    1644               an element for the block in the tree; it is recorded
    1645               in the conditional stack.  Pop it, such that
    1646               the "@end" line does not appear in the final tree for a
    1647               conditional block. */
    1648            CONDITIONAL_STACK_ITEM *cond_info = pop_conditional_stack ();
    1649            SOURCE_MARK *end_source_mark;
    1650            SOURCE_MARK *cond_source_mark = cond_info->source_mark;
    1651  
    1652            debug ("POP END COND %s %s", command_name(end_id),
    1653                   command_name(cond_info->command));
    1654  
    1655            end_source_mark = new_source_mark (cond_source_mark->type);
    1656            end_source_mark->counter = cond_source_mark->counter;
    1657            end_source_mark->status = SM_status_end;
    1658            end_elt->parent = 0;
    1659            end_source_mark->element = end_elt;
    1660            register_source_mark (current, end_source_mark);
    1661          }
    1662      }
    1663    else
    1664      {
    1665       /* If a file was included, remove the include command completely.
    1666          Also ignore @setfilename in included file, as said in the manual. */
    1667        if (included_file || (cmd == CM_setfilename && top_file_index () > 0))
    1668          {
    1669            SOURCE_MARK *source_mark = 0;
    1670            if (included_file && include_source_mark)
    1671              source_mark = include_source_mark;
    1672            else
    1673              source_mark = new_source_mark (SM_type_setfilename);
    1674  
    1675            if (source_mark)
    1676              {
    1677                /* this is in order to keep source marks that are within a
    1678                  removed element.  For the XS parser it is also easier to
    1679                  manage the source mark memory which can stay associated
    1680                  to the element. */
    1681                source_mark->element = pop_element_from_contents (current);
    1682                register_source_mark (current, source_mark);
    1683              }
    1684          }
    1685        if (close_preformatted_command (cmd))
    1686          current = begin_preformatted (current);
    1687      }
    1688  
    1689    if (cmd == CM_setfilename && (current_node || current_section))
    1690      command_warn (misc_cmd, "@setfilename after the first element");
    1691    else if (cmd == CM_columnfractions)
    1692      {
    1693        /* Check if in multitable. */
    1694        if (!current->parent || current->parent->cmd != CM_multitable)
    1695          {
    1696            command_error (current,
    1697              "@columnfractions only meaningful on a @multitable line");
    1698          }
    1699        else
    1700          {
    1701            add_extra_element (current->parent, "columnfractions", misc_cmd);
    1702          }
    1703      }
    1704    else if (command_data(data_cmd).flags & CF_root)
    1705      {
    1706        current = last_contents_child (current);
    1707        if (cmd == CM_node)
    1708          counter_pop (&count_remaining_args);
    1709        
    1710        /* Set 'associated_section' extra key for a node. */
    1711        if (cmd != CM_node && cmd != CM_part)
    1712          {
    1713            if (current_node)
    1714              {
    1715                if (!lookup_extra (current_node, "associated_section"))
    1716                  {
    1717                    add_extra_element
    1718                      (current_node, "associated_section", current);
    1719                    add_extra_element
    1720                      (current, "associated_node", current_node);
    1721                  }
    1722              }
    1723  
    1724            if (current_part)
    1725              {
    1726                add_extra_element (current, "associated_part", current_part);
    1727                add_extra_element (current_part, "part_associated_section",
    1728                                   current);
    1729                if (current->cmd == CM_top)
    1730                  {
    1731                    line_error_ext (1, ¤t_part->source_info,
    1732                           "@part should not be associated with @top");
    1733                  }
    1734                current_part = 0;
    1735              }
    1736  
    1737            current_section = current;
    1738          }
    1739        else if (cmd == CM_part)
    1740          {
    1741            current_part = current;
    1742            if (current_node
    1743                && !lookup_extra (current_node, "associated_section"))
    1744              {
    1745                line_warn ("@node precedes @part, but parts may not be "
    1746                           "associated with nodes");
    1747              }
    1748          }
    1749      }
    1750  
    1751    return current;
    1752  }
    1753  
    1754  /* Actions to be taken when a whole line of input has been processed */
    1755  ELEMENT *
    1756  end_line (ELEMENT *current)
    1757  {
    1758    ELEMENT *current_old = current; /* Used at very end of function */
    1759  
    1760    /* If empty line, start a new paragraph. */
    1761    if (last_contents_child (current)
    1762        && last_contents_child (current)->type == ET_empty_line)
    1763      {
    1764        debug_nonl ("END EMPTY LINE in ");
    1765        debug_print_element (current, 0); debug ("");
    1766        if (current->type == ET_paragraph)
    1767          {
    1768            ELEMENT *e;
    1769            /* Remove empty_line element. */
    1770            e = pop_element_from_contents (current);
    1771  
    1772            current = end_paragraph (current, 0, 0);
    1773  
    1774            /* Add empty_line to higher-level element. */
    1775            add_to_element_contents (current, e);
    1776          }
    1777        else if (current->type == ET_preformatted
    1778                 && current->parent->type == ET_menu_entry_description)
    1779          {
    1780            /* happens for an empty line following a menu_description */
    1781            ELEMENT *empty_line, *e;
    1782            empty_line = pop_element_from_contents (current);
    1783            if (current->contents.number == 0)
    1784              {
    1785                /* it should not be possible to have source marks associated
    1786                   to that container */
    1787                current = current->parent;
    1788                destroy_element (pop_element_from_contents (current));
    1789              }
    1790            else
    1791              current = current->parent;
    1792  
    1793            current = current->parent->parent;
    1794            e = new_element (ET_menu_comment);
    1795            add_to_element_contents (current, e);
    1796  
    1797            current = e;
    1798            e = new_element (ET_preformatted);
    1799            add_to_element_contents (current, e);
    1800  
    1801            current = e;
    1802            e = new_element (ET_after_menu_description_line);
    1803            text_append (&e->text, empty_line->text.text);
    1804            transfer_source_marks (empty_line, e);
    1805            destroy_element (empty_line);
    1806            add_to_element_contents (current, e);
    1807  
    1808            debug ("MENU: END DESCRIPTION, OPEN COMMENT");
    1809          }
    1810        else if (in_paragraph_context (current_context ()))
    1811          {
    1812            current = end_paragraph (current, 0, 0);
    1813          }
    1814      }
    1815    /* The end of the line of a menu entry, without description. */
    1816    else if (current->type == ET_menu_entry_name
    1817             || current->type == ET_menu_entry_node)
    1818      {
    1819        current = end_line_menu_entry (current);
    1820      }
    1821    /* End of a line starting a block. */
    1822    else if (current->type == ET_block_line_arg)
    1823      {
    1824        current = end_line_starting_block (current);
    1825      }
    1826  
    1827    else if (current->type == ET_line_arg)
    1828      {
    1829        current = end_line_misc_line (current);
    1830      }
    1831  
    1832    /* 'line' or 'def' at top of "context stack" - this happens when
    1833       line commands are nested (always incorrectly?) */
    1834    if (current_context () == ct_line || current_context () == ct_def)
    1835      {
    1836        debug_nonl ("Still opened line/block command %s: ",
    1837                    context_name (current_context ()));
    1838        debug_print_element (current, 1); debug("");
    1839        if (current_context () == ct_def)
    1840          {
    1841            while (current->parent
    1842                   && current->parent->type != ET_def_line)
    1843              {
    1844                current = close_current (current, 0, 0);
    1845              }
    1846          }
    1847        else
    1848          {
    1849            while (current->parent
    1850                   && current->type != ET_line_arg
    1851                   && current->type != ET_block_line_arg)
    1852              {
    1853                current = close_current (current, 0, 0);
    1854              }
    1855          }
    1856  
    1857        /* Check is disabled as new_element can reuse storage. */
    1858        if (0 && current == current_old)
    1859          fatal ("infinite loop when closing commands");
    1860  
    1861        current = end_line (current);
    1862      }
    1863    return current;
    1864  }
    1865