/* Copyright 2010-2023 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#include
#include "parser.h"
/* Return the parent if in an item_line command, @*table */
ELEMENT *
item_line_parent (ELEMENT *current)
{
if (current->type == ET_before_item && current->parent)
current = current->parent;
if (item_line_command (current->cmd))
return current;
return 0;
}
/* Return the parent if in a multitable. */
ELEMENT *
item_multitable_parent (ELEMENT *current)
{
if (current->cmd == CM_headitem
|| current->cmd == CM_item
|| current->cmd == CM_tab)
{
if (current->parent && current->parent->parent)
current = current->parent->parent;
}
else if (current->type == ET_before_item)
{
current = current->parent;
}
if (current->cmd == CM_multitable)
return current;
return 0;
}
/* Put the contents of a @table row in a ET_table_entry container, containing
a ET_table_term element and a ET_table_definition element. The elements of
this row currently occur the end of the contents of CURRENT as immediate
children.
NEXT_COMMAND is the command that ends this row, usually CM_item. It is
null at the end of a @table. If NEXT_COMMAND is given as CM_itemx, gather
an ET_inter_item container instead. */
void
gather_previous_item (ELEMENT *current, enum command_id next_command)
{
ELEMENT *table_after_terms, *e;
enum element_type type;
int i, contents_count;
int begin = -1, end = -1, term_begin = -1;
if (last_contents_child(current)
&& last_contents_child(current)->type == ET_before_item)
{
if (next_command == CM_itemx)
line_error ("@itemx should not begin @%s", command_name(current->cmd));
return;
}
type = next_command != CM_itemx ? ET_table_definition : ET_inter_item;
/* Starting from the end, collect everything that is not a ET_item
or ET_itemx and put it into the ET_table_definition/ET_inter_item. */
contents_count = current->contents.number;
for (i = contents_count - 1; i >= 0; i--)
{
e = contents_child_by_index (current, i);
if (e->cmd == CM_item || e->cmd == CM_itemx)
{
begin = i + 1;
break;
}
}
if (begin == -1)
begin = 0;
if (next_command)
{
/* Don't absorb trailing index entries as they are included with a
following @item. */
for (i = contents_count - 1; i >= begin; i--)
{
e = contents_child_by_index (current, i);
if (e->type != ET_index_entry_command)
{
end = i + 1;
break;
}
}
}
if (end == -1)
end = contents_count;
table_after_terms = new_element (type);
/* Move everything from 'begin' onwards to be children of
table_after_terms. */
insert_slice_into_contents (table_after_terms, 0, current, begin, end);
for (i = 0; i < table_after_terms->contents.number; i++)
contents_child_by_index(table_after_terms, i)->parent = table_after_terms;
remove_slice_from_contents (current, begin, end);
if (type == ET_table_definition)
{
ELEMENT *before_item = 0;
ELEMENT *table_entry = new_element (ET_table_entry);
ELEMENT *table_term = new_element (ET_table_term);
add_to_element_contents (table_entry, table_term);
/* We previously collected elements into a ET_table_definition. Now
do the same for ET_table_term. */
for (i = begin - 1; i >= 0; i--)
{
e = contents_child_by_index (current, i);
if (e->type == ET_before_item
|| e->type == ET_table_entry)
{
if (e->type == ET_before_item)
before_item = e;
term_begin = i + 1;
break;
}
}
if (term_begin == -1)
term_begin = 0;
insert_slice_into_contents (table_term, 0, current,
term_begin, begin);
for (i = 0; i < table_term->contents.number; i++)
contents_child_by_index(table_term, i)->parent = table_term;
remove_slice_from_contents (current, term_begin, begin);
if (before_item)
{
/* Reparent any trailing index entries in the before_item to the
beginning of table term. */
while (before_item->contents.number > 0
&& (last_contents_child(before_item)->type
== ET_index_entry_command
|| last_contents_child(before_item)->cmd == CM_c
|| last_contents_child(before_item)->cmd
== CM_comment))
{
ELEMENT *e = pop_element_from_contents (before_item);
insert_into_contents (table_term, e, 0);
}
}
if (table_after_terms->contents.number > 0)
add_to_element_contents (table_entry, table_after_terms);
else
destroy_element (table_after_terms);
insert_into_contents (current, table_entry, term_begin);
}
else /* Gathering ET_inter_item between @item and @itemx */
{
/* Text between @item and @itemx is only allowed in a few cases:
comments, empty lines, or index entries. */
if (check_no_text (table_after_terms))
line_error ("@itemx must follow @item");
if (table_after_terms->contents.number > 0)
insert_into_contents (current, table_after_terms, begin);
else
destroy_element (table_after_terms);
}
}