/* handle_commands.c -- what to do when a command name is first read */
/* Copyright 2010-2020 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
#include
#include "parser.h"
#include "input.h"
#include "text.h"
/* Return a containing @itemize or @enumerate if inside it. */
ELEMENT *
item_container_parent (ELEMENT *current)
{
if ((current->cmd == CM_item
|| current->type == ET_before_item)
&& current->parent
&& ((current->parent->cmd == CM_itemize
|| current->parent->cmd == CM_enumerate)))
{
return current->parent;
}
return 0;
}
/* Check that there are no text holding environments (currently
checking only paragraphs and preformatted) in contents. */
int
check_no_text (ELEMENT *current)
{
int after_paragraph = 0;
int i, j;
for (i = 0; i < current->contents.number; i++)
{
enum element_type t;
ELEMENT *f;
f = current->contents.list[i];
t = f->type;
if (t == ET_paragraph)
{
after_paragraph = 1;
break;
}
else if (t == ET_preformatted
|| t == ET_rawpreformatted)
{
for (j = 0; j < f->contents.number; j++)
{
ELEMENT *g = f->contents.list[j];
if ((g->text.end > 0
&& g->text.text[strspn (g->text.text, whitespace_chars)])
|| (g->cmd && g->cmd != CM_c
&& g->cmd != CM_comment
&& g->type != ET_index_entry_command))
{
after_paragraph = 1;
break;
}
}
if (after_paragraph)
break;
}
}
return after_paragraph;
}
/* noarg skipspace */
ELEMENT *
handle_other_command (ELEMENT *current, char **line_inout,
enum command_id cmd, int *status)
{
ELEMENT *misc = 0;
char *line = *line_inout;
int arg_spec;
*status = STILL_MORE_TO_PROCESS;
arg_spec = command_data(cmd).data;
if (arg_spec == OTHER_noarg)
{
if (command_data(cmd).flags & CF_in_heading)
{
line_error ("@%s should only appear in heading or footing",
command_name(cmd));
}
misc = new_element (ET_NONE);
misc->cmd = cmd;
add_to_element_contents (current, misc);
register_global_command (misc);
if (close_preformatted_command(cmd))
current = begin_preformatted (current);
}
else
{
/* @item can occur in several contents: in an @itemize, a @table, or
a @multitable. */
if (cmd == CM_item || cmd == CM_headitem || cmd == CM_tab)
{
ELEMENT *parent;
/* @itemize or @enumerate */
if ((parent = item_container_parent (current)))
{
if (cmd == CM_item)
{
debug ("ITEM CONTAINER");
counter_inc (&count_items);
misc = new_element (ET_NONE);
misc->cmd = CM_item;
add_extra_integer (misc, "item_number",
counter_value (&count_items, parent));
add_to_element_contents (parent, misc);
current = misc;
}
else
{
line_error ("@%s not meaningful inside `@%s' block",
command_name(cmd),
command_name(parent->cmd));
}
current = begin_preformatted (current);
}
/* @table, @vtable, @ftable */
else if ((parent = item_line_parent (current)))
{
line_error ("@%s not meaningful inside `@%s' block",
command_name(cmd),
command_name(parent->cmd));
current = begin_preformatted (current);
}
/* In a @multitable */
else if ((parent = item_multitable_parent (current)))
{
if (cmd != CM_item && cmd != CM_headitem
&& cmd != CM_tab)
{
line_error ("@%s not meaningful inside @%s block",
command_name(cmd),
command_name(parent->cmd));
}
else
{
int max_columns = 0;
KEY_PAIR *prototypes;
prototypes = lookup_extra (parent, "prototypes");
if (prototypes)
max_columns = prototypes->value->contents.number;
else
{
prototypes = lookup_extra(parent, "columnfractions");
if (prototypes)
{
prototypes = lookup_extra((ELEMENT *) prototypes->value,
"misc_args");
if (prototypes)
max_columns = prototypes->value->contents.number;
}
}
if (max_columns == 0)
{
line_warn ("@%s in empty multitable",
command_name(cmd));
}
else if (cmd == CM_tab)
{
ELEMENT *row;
row = last_contents_child (parent);
if (row->type == ET_before_item)
line_error ("@tab before @item");
else if (counter_value (&count_cells, row)
>= max_columns)
{
line_error ("too many columns in multitable item"
" (max %d)", max_columns);
}
else
{
counter_inc (&count_cells);
misc = new_element (ET_NONE);
misc->cmd = cmd;
add_to_element_contents (row, misc);
current = misc;
debug ("TAB");
add_extra_integer (current, "cell_number",
counter_value (&count_cells, row));
}
}
else /* @item or @headitem */
{
ELEMENT *row;
debug ("ROW");
row = new_element (ET_row);
add_to_element_contents (parent, row);
/* Note that the "row_number" extra value,
isn't actually used anywhere at present. */
add_extra_integer (row, "row_number",
parent->contents.number - 1);
misc = new_element (ET_NONE);
misc->cmd = cmd;
add_to_element_contents (row, misc);
current = misc;
if (counter_value (&count_cells, parent) != -1)
counter_pop (&count_cells);
counter_push (&count_cells, row, 1);
add_extra_integer (current, "cell_number",
counter_value (&count_cells, row));
}
}
current = begin_preformatted (current);
} /* In @multitable */
else if (cmd == CM_tab)
{
line_error ("ignoring @tab outside of multitable");
current = begin_preformatted (current);
}
else
{
line_error ("@%s outside of table or list",
command_name(cmd));
current = begin_preformatted (current);
}
if (misc)
misc->line_nr = line_nr;
}
else
{
misc = new_element (ET_NONE);
misc->cmd = cmd;
misc->line_nr = line_nr;
add_to_element_contents (current, misc);
}
start_empty_line_after_command (current, &line, misc);
if (cmd == CM_indent || cmd == CM_noindent)
{
/* Start a new paragraph if not in one already. */
int spaces;
ELEMENT *paragraph;
/* Check if if we should change an ET_empty_line_after_command
element to ET_empty_spaces_after_command by looking ahead
to see what comes next. */
if (!strchr (line, '\n'))
{
char *line2;
input_push_text (strdup (line), 0);
line2 = new_line ();
if (line2)
line = line2;
}
spaces = strspn (line, whitespace_chars);
if (spaces > 0)
{
char saved = line[spaces];
line[spaces] = '\0';
current = merge_text (current, line);
line[spaces] = saved;
line += spaces;
}
if (*line
&& last_contents_child(current)->type
== ET_empty_line_after_command)
{
last_contents_child(current)->type
= ET_empty_spaces_after_command;
}
paragraph = begin_paragraph (current);
if (paragraph)
current = paragraph;
if (!*line)
{
*status = GET_A_NEW_LINE;
goto funexit;
}
}
}
funexit:
*line_inout = line;
return current;
}
/* STATUS is set to GET_A_NEW_LINE if we should get a new line after this,
to FINISHED_TOTALLY if we should stop processing completely. */
ELEMENT *
handle_line_command (ELEMENT *current, char **line_inout,
enum command_id cmd, int *status)
{
ELEMENT *misc = 0;
char *line = *line_inout;
int arg_spec;
*status = STILL_MORE_TO_PROCESS;
/* Root commands (like @node) and @bye */
if (command_data(cmd).flags & CF_root || cmd == CM_bye)
{
ELEMENT *closed_elt; /* Not used */
current = close_commands (current, 0, &closed_elt, cmd);
if (current->type == ET_text_root)
{
if (cmd != CM_bye)
{
/* Use document_root when we have nodes or sections. */
ELEMENT *new_root = new_element (ET_document_root);
add_to_element_contents (new_root, current);
current = new_root;
}
}
else
{
current = current->parent;
if (!current)
fatal ("no parent element");
}
}
/* Look up information about this command ( skipline text
line lineraw (a number) ). */
arg_spec = command_data(cmd).data;
/* All the cases using the raw line.
TODO: I don't understand what the difference is between these.
LINE_skipline is used where the command takes no argument at all. */
if (arg_spec == LINE_skipline || arg_spec == LINE_lineraw
|| arg_spec == LINE_special)
{
ELEMENT *args = 0;
enum command_id equivalent_cmd = 0;
int has_comment = 0;
int ignored = 0;
if (cmd == CM_insertcopying)
{
ELEMENT *p = current;
while (p)
{
if (p->cmd == CM_copying)
{
line_error ("@%s not allowed inside `@copying' block",
command_name(cmd));
ignored = 1;
break;
}
p = p->parent;
}
}
/* If the current input is the result of a macro expansion,
it may not be a complete line. Check for this and acquire the rest
of the line if necessary. */
if (!strchr (line, '\n'))
{
char *line2;
LINE_NR save_ln;
input_push_text (strdup (line), 0);
save_ln = line_nr;
line2 = new_line ();
if (line2)
{
line = line2;
line_nr = save_ln;
}
}
misc = new_element (ET_NONE);
misc->cmd = cmd;
if (arg_spec == LINE_skipline || arg_spec == LINE_lineraw)
{
ELEMENT *arg;
args = new_element (ET_NONE);
arg = new_element (ET_NONE);
add_to_element_contents (args, arg);
text_append (&arg->text, line);
}
else /* arg_spec == LINE_special */
{
args = parse_special_misc_command (line, cmd, &has_comment);
add_extra_string (misc, "arg_line", strdup (line));
}
/* Handle @set txicodequoteundirected as an
alternative to @codequoteundirected. */
if (cmd == CM_set || cmd == CM_clear)
{
if (args->contents.number > 0
&& args->contents.list[0]->text.end > 0)
{
if (!strcmp (args->contents.list[0]->text.text,
"txicodequoteundirected"))
equivalent_cmd = CM_codequoteundirected;
else if (!strcmp (args->contents.list[0]->text.text,
"txicodequotebacktick"))
equivalent_cmd = CM_codequotebacktick;
}
}
if (equivalent_cmd)
{
char *arg = 0;
ELEMENT *line_args;
ELEMENT *e;
if (cmd == CM_set)
arg = "on";
else
arg = "off";
/* Now manufacture the parse tree for the equivalent
command and add it to the tree. */
destroy_element_and_children (args);
args = new_element (ET_NONE);
e = new_element (ET_NONE);
text_append (&e->text, arg);
add_to_element_contents (args, e);
destroy_element_and_children (misc);
misc = new_element (ET_NONE);
misc->cmd = equivalent_cmd;
misc->line_nr = line_nr;
line_args = new_element (ET_line_arg);
add_to_element_args (misc, line_args);
add_extra_misc_args (misc, "misc_args", args);
add_extra_string_dup (misc, "spaces_before_argument", " ");
e = new_element (ET_NONE);
text_append (&e->text, arg);
add_to_element_contents (line_args, e);
e = new_element (ET_spaces_at_end);
text_append_n (&e->text, "\n", 1);
add_to_element_contents (line_args, e);
add_to_element_contents (current, misc);
}
else
{
int i;
if (!ignored)
{
add_to_element_contents (current, misc);
for (i = 0; i < args->contents.number; i++)
{
ELEMENT *misc_arg = new_element (ET_misc_arg);
text_append_n (&misc_arg->text,
args->contents.list[i]->text.text,
args->contents.list[i]->text.end);
add_to_element_args (misc, misc_arg);
}
/* TODO: Could we have just set misc->args directly as args? */
if (args->contents.number > 0 && arg_spec != LINE_skipline)
add_extra_misc_args (misc, "misc_args", args);
else
destroy_element_and_children (args);
}
else
{
destroy_element_and_children (misc);
destroy_element_and_children (args);
misc = 0;
}
}
if (cmd == CM_raisesections)
{
global_info.sections_level++;
}
else if (cmd == CM_lowersections)
{
global_info.sections_level--;
}
else if (cmd == CM_novalidate)
{
global_info.novalidate = 1;
}
if (misc)
register_global_command (misc);
if (arg_spec != LINE_special || !has_comment)
current = end_line (current);
if (cmd == CM_bye)
{
*status = FINISHED_TOTALLY;
goto funexit;
}
if (close_preformatted_command(cmd))
current = begin_preformatted (current);
*status = GET_A_NEW_LINE;
goto funexit;
}
else
{
ELEMENT *arg;
/* text, line, or a number.
(This includes handling of "@end", which is LINE_text.) */
if (cmd == CM_item_LINE || cmd == CM_itemx)
{
ELEMENT *parent;
if ((parent = item_line_parent (current)))
{
debug ("ITEM_LINE");
current = parent;
gather_previous_item (current, cmd);
}
else
{
line_error ("@%s outside of table or list",
cmd == CM_item_LINE ? "item" : "itemx");
current = begin_preformatted (current);
}
misc = new_element (ET_NONE);
misc->cmd = (cmd == CM_item_LINE) ? CM_item : CM_itemx;
misc->line_nr = line_nr;
add_to_element_contents (current, misc);
}
else
{
/* Add to contents */
misc = new_element (ET_NONE);
misc->cmd = cmd;
misc->line_nr = line_nr;
if (cmd == CM_subentry)
{
long level = 1;
ELEMENT *parent = current->parent;
if (!(command_flags(parent) & CF_index_entry_command)
&& parent->cmd != CM_subentry)
{
line_warn ("@subentry should only occur in an index entry");
}
add_extra_element (parent, "subentry", misc);
if (parent->cmd == CM_subentry)
{
KEY_PAIR *k = lookup_extra (parent, "level");
if (k && k->value)
level = (long) k->value + 1;
}
add_extra_integer (misc, "level", level);
if (level > 2)
{
line_error
("no more than two levels of index subentry are allowed");
}
/* Do not make the @subentry element a child of the index
command. This means that spaces are preserved properly
when converting back to Texinfo. */
current = end_line (current);
}
add_to_element_contents (current, misc);
if (command_data(cmd).flags & CF_sectioning)
{
if (global_info.sections_level)
{
add_extra_integer (misc, "sections_level",
global_info.sections_level);
}
}
/* @def*x */
if (command_data(cmd).flags & CF_def)
{
enum command_id base_command;
char *base_name;
int base_len;
int after_paragraph;
/* Find the command with "x" stripped from the end, e.g.
deffnx -> deffn. */
base_name = command_name(cmd);
add_extra_string_dup (misc, "original_def_cmdname", base_name);
base_name = strdup (base_name);
base_len = strlen (base_name);
if (base_name[base_len - 1] != 'x')
fatal ("no x at end of def command name");
base_name[base_len - 1] = '\0';
base_command = lookup_command (base_name);
if (base_command == CM_NONE)
fatal ("no def base command");
add_extra_string (misc, "def_command", base_name);
after_paragraph = check_no_text (current);
push_context (ct_def);
misc->type = ET_def_line;
if (current->cmd == base_command)
{
ELEMENT *e = pop_element_from_contents (current);
/* e should be the same as misc */
/* Gather an "inter_def_item" element. */
gather_def_item (current, cmd);
add_to_element_contents (current, e);
}
if (current->cmd != base_command || after_paragraph)
{
// error - deffnx not after deffn
line_error ("must be after `@%s' to use `@%s'",
command_name(base_command),
command_name(cmd));
add_extra_integer (misc, "not_after_command", 1);
}
}
}
/* change 'current' to its last child. This is ELEMENT *misc above. */
current = last_contents_child (current);
arg = new_element (ET_line_arg);
add_to_element_args (current, arg);
if (cmd == CM_node)
{
/* At most three comma-separated arguments to @node. This
is the only (non-block) line command taking comma-separated
arguments. Its arguments will be gathered the same as
those of some block line commands and brace commands. */
counter_push (&count_remaining_args, current, 3);
}
else if (cmd == CM_author)
{
ELEMENT *parent = current;
int found = 0;
while (parent->parent)
{
parent = parent->parent;
if (parent->type == ET_brace_command_context)
break;
if (parent->cmd == CM_titlepage)
{
add_extra_element (current, "titlepage", parent);
found = 1; break;
}
else if (parent->cmd == CM_quotation
|| parent->cmd == CM_smallquotation)
{
KEY_PAIR *k; ELEMENT *e;
k = lookup_extra (parent, "authors");
if (k)
e = k->value;
else
{
e = new_element (ET_NONE);
add_extra_contents (parent, "authors", e);
}
add_to_contents_as_array (e, current);
add_extra_element (current, "quotation", parent);
found = 1; break;
}
}
if (!found)
line_warn ("@author not meaningful outside "
"`@titlepage' and `@quotation' environments");
}
else if (cmd == CM_dircategory && current_node)
line_warn ("@dircategory after first node");
else if (cmd == CM_printindex && current_node)
add_extra_integer (current_node, "isindex", 1);
current = last_args_child (current);
/* add 'line' to context_stack. This will be the
case while we read the argument on this line. */
if (!(command_data(cmd).flags & CF_def))
push_context (ct_line);
start_empty_line_after_command (current, &line, misc);
}
if (misc)
register_global_command (misc);
if (cmd == CM_dircategory)
add_to_contents_as_array (&global_info.dircategory_direntry, misc);
funexit:
*line_inout = line;
return current;
}
struct expanded_format {
char *format;
int expandedp;
};
static struct expanded_format expanded_formats[] = {
"html", 0,
"docbook", 0,
"plaintext", 1,
"tex", 0,
"xml", 0,
"info", 1,
};
void
clear_expanded_formats (void)
{
int i;
for (i = 0; i < sizeof (expanded_formats)/sizeof (*expanded_formats);
i++)
{
expanded_formats[i].expandedp = 0;
}
}
void
add_expanded_format (char *format)
{
int i;
for (i = 0; i < sizeof (expanded_formats)/sizeof (*expanded_formats);
i++)
{
if (!strcmp (format, expanded_formats[i].format))
{
expanded_formats[i].expandedp = 1;
break;
}
}
if (!strcmp (format, "plaintext"))
add_expanded_format ("info");
}
int
format_expanded_p (char *format)
{
int i;
for (i = 0; i < sizeof (expanded_formats)/sizeof (*expanded_formats);
i++)
{
if (!strcmp (format, expanded_formats[i].format))
return expanded_formats[i].expandedp;
}
return 0;
}
/* A command name has been read that starts a multiline block, which should
end in @end . The block will be processed until
"end_line_misc_line" in end_line.c processes the @end command. */
ELEMENT *
handle_block_command (ELEMENT *current, char **line_inout,
enum command_id cmd, int *get_new_line)
{
char *line = *line_inout;
unsigned long flags = command_data(cmd).flags;
/* New macro being defined. */
if (cmd == CM_macro || cmd == CM_rmacro)
{
ELEMENT *macro;
macro = parse_macro_command_line (cmd, &line, current);
add_to_element_contents (current, macro);
current = macro;
/* A new line should be read immediately after this. */
line = strchr (line, '\0');
*get_new_line = 1;
goto funexit;
}
else if (command_data(cmd).data == BLOCK_conditional)
{
int iftrue = 0; /* Whether the conditional is true. */
if (cmd == CM_ifclear || cmd == CM_ifset
|| cmd == CM_ifcommanddefined || cmd == CM_ifcommandnotdefined)
{
char *p = line;
p = line + strspn (line, whitespace_chars);
if (!*p)
line_error ("@%s requires a name", command_name(cmd));
else
{
char *flag = read_flag_name (&p);
if (!flag)
goto bad_value;
else
{
p += strspn (p, whitespace_chars);
/* Check for a comment at the end of the line. */
if (memcmp (p, "@c", 2) == 0)
{
p += 2;
if (memcmp (p, "omment", 6) == 0)
p += 7;
if (*p && *p != '@' && !strchr (whitespace_chars, *p))
goto bad_value; /* @c or @comment not terminated. */
}
else if (*p)
goto bad_value; /* Trailing characters on line. */
}
if (1)
{
if (cmd == CM_ifclear || cmd == CM_ifset)
{
char *val = fetch_value (flag);
if (val)
iftrue = 1;
if (cmd == CM_ifclear)
iftrue = !iftrue;
}
else /* cmd == CM_ifcommanddefined
|| cmd == CM_ifcommandnotdefined */
{
enum command_id c = lookup_command (flag);
if (c)
iftrue = 1;
if (cmd == CM_ifcommandnotdefined)
iftrue = !iftrue;
}
}
else if (0)
{
bad_value:
line_error ("bad name for @%s", command_name(cmd));
}
free (flag);
}
}
else if (!memcmp (command_name(cmd), "if", 2)) /* e.g. @ifhtml */
{
int i; char *p;
/* Handle @if* and @ifnot* */
p = command_name(cmd) + 2; /* After "if". */
if (!memcmp (p, "not", 3))
p += 3; /* After "not". */
for (i = 0; i < sizeof (expanded_formats)/sizeof (*expanded_formats);
i++)
{
if (!strcmp (p, expanded_formats[i].format))
{
iftrue = expanded_formats[i].expandedp;
break;
}
}
if (!memcmp (command_name(cmd), "ifnot", 5))
iftrue = !iftrue;
}
else
bug_message ("unknown conditional command @%s", command_name(cmd));
/* If conditional true, push onto conditional stack. Otherwise
open a new element (which we shall later remove, in
process_remaining_on_line ("CLOSED conditional")). */
debug ("CONDITIONAL %s %d", command_name(cmd), iftrue);
if (iftrue)
push_conditional_stack (cmd);
else
{
/* Ignored. */
ELEMENT *e;
e = new_element (ET_NONE);
e->cmd = cmd;
add_to_element_contents (current, e);
current = e;
}
line = strchr (line, '\0');
*get_new_line = 1;
goto funexit;
}
else
{
ELEMENT *block = 0;
if (flags & CF_menu
&& (current->type == ET_menu_comment
|| current->type == ET_menu_entry_description))
{
/* This is for @detailmenu within @menu */
ELEMENT *menu = current->parent;
if (current->contents.number == 0)
destroy_element (pop_element_from_contents (menu));
if (pop_context () != ct_preformatted)
fatal ("preformatted context expected");
if (menu->type == ET_menu_entry)
menu = menu->parent;
current = menu;
}
if (flags & CF_def)
{
ELEMENT *def_line;
push_context (ct_def);
block = new_element (ET_NONE);
block->cmd = cmd;
block->line_nr = line_nr;
add_to_element_contents (current, block);
current = block;
def_line = new_element (ET_def_line);
def_line->line_nr = line_nr;
add_to_element_contents (current, def_line);
current = def_line;
add_extra_string_dup (current, "def_command", command_name(cmd));
add_extra_string_dup (current, "original_def_cmdname",
command_name(cmd));
}
else
{
block = new_element (ET_NONE);
block->cmd = cmd;
add_to_element_contents (current, block);
current = block;
}
/* Check if 'block args command' */
if (command_data(cmd).data != BLOCK_raw)
{
if (command_data(cmd).flags & CF_preformatted)
push_context (ct_preformatted);
else if (cmd == CM_displaymath)
push_context (ct_math);
else if (command_data(cmd).flags & CF_format_raw)
{
push_context (ct_rawpreformatted);
if (!format_expanded_p (command_name(cmd)))
{
ELEMENT *e;
enum command_id dummy;
char *line_dummy;
e = new_element (ET_elided_block);
add_to_element_contents (current, e);
line_dummy = line;
while (!is_end_current_command (current,
&line_dummy, &dummy))
{
line = new_line ();
if (!line)
{
line = "";
break;
}
line_dummy = line;
}
e = new_element (ET_empty_line_after_command);
text_append_n (&e->text, "\n", 1);
add_to_element_contents (current, e);
e = new_element (ET_empty_line);
text_append (&e->text, "");
add_to_element_contents (current, e);
goto funexit;
}
}
else if (command_data(cmd).data == BLOCK_region)
{
if (current_region_cmd ())
{
line_error ("region %s inside region %s is not allowed",
command_name(cmd),
command_name(current_region_cmd ()));
}
push_region (block);
}
if (command_data(cmd).flags & CF_menu)
{
if (current_context () == ct_preformatted)
push_context (ct_preformatted);
else
push_context (ct_menu);
if (cmd == CM_direntry)
add_to_contents_as_array (&global_info.dircategory_direntry,
block);
if (current_node)
{
if (cmd == CM_direntry && conf.show_menu)
{
line_warn ("@direntry after first node");
}
else if (cmd == CM_menu)
{
if (!(command_flags(current->parent) & CF_root))
line_warn ("@menu in invalid context");
/* Add to array of menus for current node. Currently
done in Perl code. */
}
}
}
if (cmd == CM_itemize || cmd == CM_enumerate)
counter_push (&count_items, current, 0);
/* Note that no equivalent thing is done in the Perl code, because
'item_count' is assumed to start at 0. */
{
ELEMENT *bla = new_element (ET_block_line_arg);
add_to_element_args (current, bla);
if (command_data (current->cmd).data > 1)
{
counter_push (&count_remaining_args,
current,
command_data (current->cmd).data - 1);
}
else if (command_data (current->cmd).data == BLOCK_variadic)
{
/* Unlimited args */
counter_push (&count_remaining_args, current,
COUNTER_VARIADIC);
}
current = bla;
if (!(command_data(cmd).flags & CF_def))
push_context (ct_line);
/* Note that an ET_empty_line_after_command gets reparented in the
contents in 'end_line'. */
}
}
block->line_nr = line_nr;
register_global_command (block);
start_empty_line_after_command (current, &line, block);
}
funexit:
*line_inout = line;
return current;
}
ELEMENT *
handle_brace_command (ELEMENT *current, char **line_inout, enum command_id cmd)
{
char *line = *line_inout;
ELEMENT *e;
e = new_element (ET_NONE);
e->cmd = cmd;
/* The line number information is only ever used for brace commands
if the command is given with braces, but it's easier just to always
store the information. */
e->line_nr = line_nr;
add_to_element_contents (current, e);
if (cmd == CM_sortas)
{
if (!(command_flags(current->parent) & CF_index_entry_command)
&& current->parent->cmd != CM_subentry)
{
line_warn ("@%s should only appear in an index entry",
command_name(cmd));
}
}
current = e;
if (cmd == CM_click)
{
add_extra_string_dup (e, "clickstyle", global_clickstyle);
}
else if (cmd == CM_kbd)
{
if (current_context () == ct_preformatted
&& global_kbdinputstyle != kbd_distinct
|| global_kbdinputstyle == kbd_code)
{
add_extra_integer (e, "code", 1);
}
else if (global_kbdinputstyle == kbd_example)
{
/* TODO: Understand what is going on here. */
ELEMENT *tmp = current->parent;
while (tmp->parent
&& (command_flags(tmp->parent) & CF_brace)
&& command_data(tmp->parent->cmd).data != BRACE_context)
{
if (command_flags(tmp->parent) & CF_code_style)
{
add_extra_integer (e, "code", 1);
break;
}
tmp = tmp->parent->parent;
}
}
}
else if (command_data(cmd).flags & CF_INFOENCLOSE)
{
INFO_ENCLOSE *ie = lookup_infoenclose (cmd);
if (ie)
{
add_extra_string_dup (e, "begin", ie->begin);
add_extra_string_dup (e, "end", ie->end);
}
e->type = ET_definfoenclose_command;
}
*line_inout = line;
return current;
}