1  /* Copyright 2010-2023 Free Software Foundation, Inc.
       2  
       3     This program is free software: you can redistribute it and/or modify
       4     it under the terms of the GNU General Public License as published by
       5     the Free Software Foundation, either version 3 of the License, or
       6     (at your option) any later version.
       7  
       8     This program is distributed in the hope that it will be useful,
       9     but WITHOUT ANY WARRANTY; without even the implied warranty of
      10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      11     GNU General Public License for more details.
      12  
      13     You should have received a copy of the GNU General Public License
      14     along with this program.  If not, see <http://www.gnu.org/licenses/>. */
      15  
      16  #include <config.h>
      17  #include "parser.h"
      18  
      19  /* Return the parent if in an item_line command, @*table */
      20  ELEMENT *
      21  item_line_parent (ELEMENT *current)
      22  {
      23    if (current->type == ET_before_item && current->parent)
      24      current = current->parent;
      25  
      26    if (item_line_command (current->cmd))
      27      return current;
      28  
      29    return 0;
      30  }
      31  
      32  /* Return the parent if in a multitable. */
      33  ELEMENT *
      34  item_multitable_parent (ELEMENT *current)
      35  {
      36    if (current->cmd == CM_headitem
      37        || current->cmd == CM_item
      38        || current->cmd == CM_tab)
      39      {
      40        if (current->parent && current->parent->parent)
      41          current = current->parent->parent;
      42      }
      43    else if (current->type == ET_before_item)
      44      {
      45        current = current->parent;
      46      }
      47  
      48    if (current->cmd == CM_multitable)
      49      return current;
      50  
      51    return 0;
      52  }
      53  
      54  /* Put the contents of a @table row in a ET_table_entry container, containing
      55     a ET_table_term element and a ET_table_definition element.  The elements of
      56     this row currently occur the end of the contents of CURRENT as immediate
      57     children.
      58  
      59     NEXT_COMMAND is the command that ends this row, usually CM_item.  It is
      60     null at the end of a @table.  If NEXT_COMMAND is given as CM_itemx, gather
      61     an ET_inter_item container instead.  */
      62  void
      63  gather_previous_item (ELEMENT *current, enum command_id next_command)
      64  {
      65    ELEMENT *table_after_terms, *e;
      66    enum element_type type;
      67    int i, contents_count;
      68    int begin = -1, end = -1, term_begin = -1;
      69  
      70    if (last_contents_child(current)
      71        && last_contents_child(current)->type == ET_before_item)
      72      {
      73        if (next_command == CM_itemx)
      74          line_error ("@itemx should not begin @%s", command_name(current->cmd));
      75        return;
      76      }
      77  
      78    type = next_command != CM_itemx ? ET_table_definition : ET_inter_item;
      79  
      80    /* Starting from the end, collect everything that is not a ET_item
      81       or ET_itemx and put it into the ET_table_definition/ET_inter_item. */
      82    contents_count = current->contents.number;
      83    for (i = contents_count - 1; i >= 0; i--)
      84      {
      85        e = contents_child_by_index (current, i);
      86        if (e->cmd == CM_item || e->cmd == CM_itemx)
      87          {
      88            begin = i + 1;
      89            break;
      90          }
      91      }
      92    if (begin == -1)
      93      begin = 0;
      94  
      95    if (next_command)
      96      {
      97        /* Don't absorb trailing index entries as they are included with a
      98           following @item. */
      99        for (i = contents_count - 1; i >= begin; i--)
     100          {
     101            e = contents_child_by_index (current, i);
     102            if (e->type != ET_index_entry_command)
     103              {
     104                end = i + 1;
     105                break;
     106              }
     107          }
     108      }
     109    if (end == -1)
     110      end = contents_count;
     111  
     112    table_after_terms = new_element (type);
     113  
     114    /* Move everything from 'begin' onwards to be children of
     115       table_after_terms. */
     116    insert_slice_into_contents (table_after_terms, 0, current, begin, end);
     117    for (i = 0; i < table_after_terms->contents.number; i++)
     118      contents_child_by_index(table_after_terms, i)->parent = table_after_terms;
     119    remove_slice_from_contents (current, begin, end);
     120  
     121    if (type == ET_table_definition)
     122      {
     123        ELEMENT *before_item = 0;
     124        ELEMENT *table_entry = new_element (ET_table_entry);
     125        ELEMENT *table_term = new_element (ET_table_term);
     126        add_to_element_contents (table_entry, table_term);
     127  
     128        /* We previously collected elements into a ET_table_definition.  Now
     129           do the same for ET_table_term. */
     130         for (i = begin - 1; i >= 0; i--)
     131           {
     132             e = contents_child_by_index (current, i);
     133             if (e->type == ET_before_item
     134                 || e->type == ET_table_entry)
     135               {
     136                 if (e->type == ET_before_item)
     137                   before_item = e;
     138                 term_begin = i + 1;
     139                 break;
     140               }
     141           }
     142        if (term_begin == -1)
     143          term_begin = 0;
     144  
     145        insert_slice_into_contents (table_term, 0, current,
     146                                    term_begin, begin);
     147        for (i = 0; i < table_term->contents.number; i++)
     148          contents_child_by_index(table_term, i)->parent = table_term;
     149        remove_slice_from_contents (current, term_begin, begin);
     150        if (before_item)
     151          {
     152            /* Reparent any trailing index entries in the before_item to the
     153               beginning of table term. */
     154            while (before_item->contents.number > 0
     155                     && (last_contents_child(before_item)->type
     156                           == ET_index_entry_command
     157                         || last_contents_child(before_item)->cmd == CM_c
     158                         || last_contents_child(before_item)->cmd
     159                           == CM_comment))
     160              {
     161                ELEMENT *e = pop_element_from_contents (before_item);
     162                insert_into_contents (table_term, e, 0);
     163              }
     164          }
     165  
     166        if (table_after_terms->contents.number > 0)
     167          add_to_element_contents (table_entry, table_after_terms);
     168        else
     169          destroy_element (table_after_terms);
     170  
     171        insert_into_contents (current, table_entry, term_begin);
     172      }
     173    else /* Gathering ET_inter_item between @item and @itemx */
     174      {
     175        /* Text between @item and @itemx is only allowed in a few cases:
     176           comments, empty lines, or index entries. */
     177        if (check_no_text (table_after_terms))
     178          line_error ("@itemx must follow @item");
     179  
     180        if (table_after_terms->contents.number > 0)
     181          insert_into_contents (current, table_after_terms, begin);
     182        else
     183          destroy_element (table_after_terms);
     184      }
     185  }