/* $NetBSD: insertion.c,v 1.1.1.2 2003/01/17 14:54:35 wiz Exp $ */ /* insertion.c -- insertions for Texinfo. Id: insertion.c,v 1.12 2002/11/11 12:37:34 feloy Exp Copyright (C) 1998, 1999, 2000, 2001, 2002 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 2, 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "system.h" #include "cmds.h" #include "defun.h" #include "insertion.h" #include "macro.h" #include "makeinfo.h" #include "xml.h" /* Must match list in insertion.h. */ static char *insertion_type_names[] = { "cartouche", "copying", "defcv", "deffn", "defivar", "defmac", "defmethod", "defop", "defopt", "defspec", "deftp", "deftypefn", "deftypefun", "deftypeivar", "deftypemethod", "deftypeop", "deftypevar", "deftypevr", "defun", "defvar", "defvr", "detailmenu", "direntry", "display", "documentdescription", "enumerate", "example", "flushleft", "flushright", "format", "ftable", "group", "ifclear", "ifhtml", "ifinfo", "ifnothtml", "ifnotinfo", "ifnotplaintext", "ifnottex", "ifnotxml", "ifplaintext", "ifset", "iftex", "ifxml", "itemize", "lisp", "menu", "multitable", "quotation", "rawhtml", "rawtex", "smalldisplay", "smallexample", "smallformat", "smalllisp", "verbatim", "table", "tex", "vtable", "bad_type" }; /* All nested environments. */ INSERTION_ELT *insertion_stack = NULL; /* How deeply we're nested. */ int insertion_level = 0; /* Set to 1 if we've processed (commentary) text in a @menu that wasn't part of a menu item. */ int had_menu_commentary; /* How to examine menu lines. */ int in_detailmenu = 0; /* Whether to examine menu lines. */ int in_menu = 0; /* Set to 1 if
is written in normal context. Used for menu and itemize. */ int in_paragraph = 0; static const char dl_tag[] = "
\n"); else { /* with close_single_paragraph, we get no blank line above within @copying. */ close_paragraph (); last_char_was_newline = no_indent = 0; indented_fill = filling_enabled = 1; inhibit_paragraph_indentation = 1; } current_indent += default_indentation_increment; break; case display: case smalldisplay: case example: case smallexample: case lisp: case smalllisp: /* Like @display but without indentation. */ case smallformat: case format: close_single_paragraph (); inhibit_paragraph_indentation = 1; in_fixed_width_font++; filling_enabled = 0; last_char_was_newline = 0; if (html) /* Kludge alert: ifis followed by a newline, IE3 renders an extra blank line before the pre-formatted block. Other browsers seem to not mind one way or the other. */ add_word_args ("", command); if (type != format && type != smallformat) { current_indent += default_indentation_increment; if (html) { /* Since we didn't put \n after, we need to insert the indentation by hand. */ int i; for (i = current_indent; i > 0; i--) add_char (' '); } } break; case multitable: do_multitable (); break; case table: case ftable: case vtable: case itemize: close_single_paragraph (); current_indent += default_indentation_increment; filling_enabled = indented_fill = 1; #if defined (INDENT_PARAGRAPHS_IN_TABLE) inhibit_paragraph_indentation = 0; #else inhibit_paragraph_indentation = 1; #endif /* !INDENT_PARAGRAPHS_IN_TABLE */ /* Make things work for losers who forget the itemize syntax. */ if (type == itemize) { if (!(*insertion_stack->item_function)) { free (insertion_stack->item_function); insertion_stack->item_function = xstrdup ("@bullet"); } } if (!*insertion_stack->item_function) { line_error (_("%s requires an argument: the formatter for %citem"), insertion_type_pname (type), COMMAND_PREFIX); } if (html) { if (type == itemize) { add_word ("\n"); in_paragraph = 0; } else add_word (dl_tag); } if (xml) xml_begin_table (type, insertion_stack->item_function); break; case enumerate: close_single_paragraph (); no_indent = 0; #if defined (INDENT_PARAGRAPHS_IN_TABLE) inhibit_paragraph_indentation = 0; #else inhibit_paragraph_indentation = 1; #endif /* !INDENT_PARAGRAPHS_IN_TABLE */ current_indent += default_indentation_increment; filling_enabled = indented_fill = 1; if (html) { enum_html (); in_paragraph = 0; } if (xml) xml_begin_enumerate (enumeration_arg); if (isdigit (*enumeration_arg)) start_enumerating (atoi (enumeration_arg), ENUM_DIGITS); else start_enumerating (*enumeration_arg, ENUM_ALPHA); break; /* @group does nothing special in makeinfo. */ case group: /* Only close the paragraph if we are not inside of an @example-like environment. */ if (xml) xml_insert_element (GROUP, START); else if (!insertion_stack->next || (insertion_stack->next->insertion != display && insertion_stack->next->insertion != smalldisplay && insertion_stack->next->insertion != example && insertion_stack->next->insertion != smallexample && insertion_stack->next->insertion != lisp && insertion_stack->next->insertion != smalllisp && insertion_stack->next->insertion != format && insertion_stack->next->insertion != smallformat && insertion_stack->next->insertion != flushleft && insertion_stack->next->insertion != flushright)) close_single_paragraph (); break; /* Insertions that are no-ops in info, but do something in TeX. */ case cartouche: case ifclear: case ifhtml: case ifinfo: case ifnothtml: case ifnotinfo: case ifnotplaintext: case ifnottex: case ifnotxml: case ifplaintext: case ifset: case iftex: case ifxml: case rawtex: if (in_menu) no_discard++; break; case rawhtml: escape_html = 0; break; case defcv: case deffn: case defivar: case defmac: case defmethod: case defop: case defopt: case defspec: case deftp: case deftypefn: case deftypefun: case deftypeivar: case deftypemethod: case deftypeop: case deftypevar: case deftypevr: case defun: case defvar: case defvr: inhibit_paragraph_indentation = 1; filling_enabled = indented_fill = 1; current_indent += default_indentation_increment; no_indent = 0; break; case flushleft: close_single_paragraph (); inhibit_paragraph_indentation = 1; filling_enabled = indented_fill = no_indent = 0; if (html) add_word ("
"); break; case flushright: close_single_paragraph (); filling_enabled = indented_fill = no_indent = 0; inhibit_paragraph_indentation = 1; force_flush_right++; if (html) add_word ("\n"); close_insertion_paragraph (); break; /* Handle the @defun insertions with this default clause. */ default: { enum insertion_type base_type; if (type < defcv || type > defvr) line_error ("end_insertion internal error: type=%d", type); base_type = get_base_type (type); switch (base_type) { case deffn: case defvr: case deftp: case deftypefn: case deftypevr: case defcv: case defop: case deftypemethod: case deftypeop: case deftypeivar: if (html) /* close the tables which has been opened in defun.c */ add_word ("\n\n"); break; } /* switch (base_type)... */ current_indent -= default_indentation_increment; close_insertion_paragraph (); } break; } if (current_indent < 0) line_error ("end_insertion internal error: current indent=%d", current_indent); } /* Insertions cannot cross certain boundaries, such as node beginnings. In code that creates such boundaries, you should call `discard_insertions' before doing anything else. It prints the errors for you, and cleans up the insertion stack. With nonzero SPECIALS_OK argument, allows unmatched @if... conditionals, otherwise not. This is because conditionals can cross node boundaries. Always happens with the @top node, for example. */ void discard_insertions (specials_ok) int specials_ok; { int real_line_number = line_number; while (insertion_stack) { if (specials_ok && ((ifclear <= insertion_stack->insertion && insertion_stack->insertion <= iftex) || insertion_stack->insertion == rawhtml || insertion_stack->insertion == rawtex)) break; else { char *offender = insertion_type_pname (insertion_stack->insertion); file_line_error (insertion_stack->filename, insertion_stack->line_number, _("No matching `%cend %s'"), COMMAND_PREFIX, offender); pop_insertion (); } } line_number = real_line_number; } /* Insertion (environment) commands. */ void cm_quotation () { if (xml) xml_insert_element (QUOTATION, START); begin_insertion (quotation); } void cm_example () { if (xml) xml_insert_element (EXAMPLE, START); begin_insertion (example); } void cm_smallexample () { if (xml) xml_insert_element (SMALLEXAMPLE, START); begin_insertion (smallexample); } void cm_lisp () { if (xml) xml_insert_element (LISP, START); begin_insertion (lisp); } void cm_smalllisp () { if (xml) xml_insert_element (SMALLLISP, START); begin_insertion (smalllisp); } void cm_cartouche () { if (xml) xml_insert_element (CARTOUCHE, START); begin_insertion (cartouche); } void cm_copying () { if (xml) xml_insert_element (COPYING, START); begin_insertion (copying); } /* Not an insertion, despite the name, but it goes with cm_copying. */ void cm_insert_copying () { if (copying_text) { /* insert_string rather than add_word because we've already done full expansion on copying_text when we saved it. */ insert_string (copying_text); insert ('\n'); } } void cm_format () { if (xml) { if (docbook && xml_in_bookinfo) { xml_insert_element (ABSTRACT, START); xml_in_abstract = 1; } else xml_insert_element (FORMAT, START); } begin_insertion (format); } void cm_smallformat () { if (xml) xml_insert_element (SMALLFORMAT, START); begin_insertion (smallformat); } void cm_display () { if (xml) xml_insert_element (DISPLAY, START); begin_insertion (display); } void cm_smalldisplay () { if (xml) xml_insert_element (SMALLDISPLAY, START); begin_insertion (smalldisplay); } void cm_direntry () { if (html || xml) command_name_condition (); else begin_insertion (direntry); } void cm_documentdescription () { if (html || xml) begin_insertion (documentdescription); else command_name_condition (); } void cm_itemize () { begin_insertion (itemize); } /* Start an enumeration insertion of type TYPE. If the user supplied no argument on the line, then use DEFAULT_STRING as the initial string. */ static void do_enumeration (type, default_string) int type; char *default_string; { get_until_in_line (0, ".", &enumeration_arg); canon_white (enumeration_arg); if (!*enumeration_arg) { free (enumeration_arg); enumeration_arg = xstrdup (default_string); } if (!isdigit (*enumeration_arg) && !isletter (*enumeration_arg)) { warning (_("%s requires letter or digit"), insertion_type_pname (type)); switch (type) { case enumerate: default_string = "1"; break; } enumeration_arg = xstrdup (default_string); } begin_insertion (type); } void cm_enumerate () { do_enumeration (enumerate, "1"); } /* Handle verbatim environment: find_end_verbatim == 0: process until end of file find_end_verbatim != 0: process until 'COMMAND_PREFIXend verbatim' or end of file We cannot simply copy input stream onto output stream; as the verbatim environment may be encapsulated in an @example environment, for example. */ void handle_verbatim_environment (find_end_verbatim) int find_end_verbatim; { int character; int seen_end = 0; int save_filling_enabled = filling_enabled; int save_inhibit_paragraph_indentation = inhibit_paragraph_indentation; close_single_paragraph (); inhibit_paragraph_indentation = 1; filling_enabled = 0; in_fixed_width_font++; last_char_was_newline = 0; /* No indentation: this is verbatim after all If you want indent, enclose @verbatim in @example current_indent += default_indentation_increment; */ if (html) add_word (""); break; default: line_error ("begin_insertion internal error: type=%d", type); } if (!no_discard) discard_until ("\n"); } /* Try to end the insertion with the specified TYPE. With a value of `bad_type', TYPE gets translated to match the value currently on top of the stack. Otherwise, if TYPE doesn't match the top of the insertion stack, give error. */ void end_insertion (type) enum insertion_type type; { enum insertion_type temp_type; if (!insertion_level) return; temp_type = current_insertion_type (); if (type == bad_type) type = temp_type; if (type != temp_type) { line_error (_("`@end' expected `%s', but saw `%s'"), insertion_type_pname (temp_type), insertion_type_pname (type)); return; } pop_insertion (); if (xml) { switch (type) { case ifinfo: case documentdescription: break; case copying: xml_insert_element (COPYING, END); break; case quotation: xml_insert_element (QUOTATION, END); break; case example: xml_insert_element (EXAMPLE, END); break; case smallexample: xml_insert_element (SMALLEXAMPLE, END); break; case lisp: xml_insert_element (LISP, END); break; case smalllisp: xml_insert_element (SMALLLISP, END); break; case cartouche: xml_insert_element (CARTOUCHE, END); break; case format: if (docbook && xml_in_bookinfo && xml_in_abstract) { xml_insert_element (ABSTRACT, END); xml_in_abstract = 0; } else xml_insert_element (FORMAT, END); break; case smallformat: xml_insert_element (SMALLFORMAT, END); break; case display: xml_insert_element (DISPLAY, END); break; case smalldisplay: xml_insert_element (SMALLDISPLAY, END); break; case table: case ftable: case vtable: case itemize: xml_end_table (type); break; case enumerate: xml_end_enumerate (type); break; case group: xml_insert_element (GROUP, END); break; } } switch (type) { /* Insertions which have no effect on paragraph formatting. */ case copying: case documentdescription: case ifclear: case ifinfo: case ifhtml: case ifnothtml: case ifnotinfo: case ifnotplaintext: case ifnottex: case ifnotxml: case ifplaintext: case ifset: case iftex: case ifxml: case rawtex: break; case rawhtml: escape_html = 1; break; case detailmenu: in_detailmenu--; /* No longer hacking menus. */ if (!in_menu) { if (!no_headers) close_insertion_paragraph (); } break; case direntry: /* Eaten if html. */ insert_string ("END-INFO-DIR-ENTRY\n\n"); close_insertion_paragraph (); break; case menu: in_menu--; /* No longer hacking menus. */ if (html) add_word ("\n"); else if (!no_headers) close_insertion_paragraph (); break; case multitable: end_multitable (); break; case enumerate: stop_enumerating (); close_insertion_paragraph (); current_indent -= default_indentation_increment; if (html) add_word ("\n"); break; case flushleft: if (html) add_word ("\n"); close_insertion_paragraph (); break; case group: case cartouche: close_insertion_paragraph (); break; case format: case smallformat: case display: case smalldisplay: case example: case smallexample: case lisp: case smalllisp: case quotation: /* @format and @smallformat are the only fixed_width insertion without a change in indentation. */ if (type != format && type != smallformat) current_indent -= default_indentation_increment; if (html) add_word (type == quotation ? "\n" : "\n"); /* The ending of one of these insertions always marks the start of a new paragraph. */ close_insertion_paragraph (); break; case table: case ftable: case vtable: current_indent -= default_indentation_increment; if (html) add_word ("\n"); break; case itemize: current_indent -= default_indentation_increment; if (html) add_word ("\n"); close_insertion_paragraph (); break; case flushright: force_flush_right--; if (html) add_word (""); while (input_text_offset < input_text_length) { character = curchar (); if (character == '\n') line_number++; /* Assume no newlines in END_VERBATIM */ else if (find_end_verbatim && (character == COMMAND_PREFIX) /* @ */ && (input_text_length - input_text_offset > sizeof (END_VERBATIM)) && !strncmp (&input_text[input_text_offset+1], END_VERBATIM, sizeof (END_VERBATIM)-1)) { input_text_offset += sizeof (END_VERBATIM); seen_end = 1; break; } if (html && character == '&' && escape_html) add_word ("&"); else if (html && character == '<' && escape_html) add_word ("<"); else add_char (character); input_text_offset++; } if (find_end_verbatim && !seen_end) warning (_("end of file inside verbatim block")); if (html) add_word (""); in_fixed_width_font--; filling_enabled = save_filling_enabled; inhibit_paragraph_indentation = save_inhibit_paragraph_indentation; } void cm_verbatim () { handle_verbatim_environment (1); } void cm_table () { begin_insertion (table); } void cm_multitable () { begin_insertion (multitable); /* @@ */ } void cm_ftable () { begin_insertion (ftable); } void cm_vtable () { begin_insertion (vtable); } void cm_group () { begin_insertion (group); } /* Insert raw HTML (no escaping of `<' etc.). */ void cm_html () { if (process_html || process_xml) begin_insertion (rawhtml); else command_name_condition (); } void cm_ifhtml () { if (process_html) begin_insertion (ifhtml); else command_name_condition (); } void cm_ifnothtml () { if (!process_html) begin_insertion (ifnothtml); else command_name_condition (); } void cm_ifinfo () { if (process_info) begin_insertion (ifinfo); else command_name_condition (); } void cm_ifnotinfo () { if (!process_info) begin_insertion (ifnotinfo); else command_name_condition (); } void cm_ifplaintext () { if (process_plaintext) begin_insertion (ifplaintext); else command_name_condition (); } void cm_ifnotplaintext () { if (!process_plaintext) begin_insertion (ifnotplaintext); else command_name_condition (); } void cm_tex () { if (process_tex) begin_insertion (rawtex); else command_name_condition (); } void cm_iftex () { if (process_tex) begin_insertion (iftex); else command_name_condition (); } void cm_ifnottex () { if (!process_tex) begin_insertion (ifnottex); else command_name_condition (); } void cm_ifxml () { if (process_xml) begin_insertion (ifxml); else command_name_condition (); } void cm_ifnotxml () { if (!process_xml) begin_insertion (ifnotxml); else command_name_condition (); } /* Begin an insertion where the lines are not filled or indented. */ void cm_flushleft () { begin_insertion (flushleft); } /* Begin an insertion where the lines are not filled, and each line is forced to the right-hand side of the page. */ void cm_flushright () { begin_insertion (flushright); } void cm_menu () { if (current_node == NULL) { warning (_("@menu seen before first @node, creating `Top' node")); warning (_("perhaps your @top node should be wrapped in @ifnottex rather than @ifinfo?")); /* Include @top command so we can construct the implicit node tree. */ execute_string ("@node top\n@top Top\n"); } begin_insertion (menu); } void cm_detailmenu () { if (current_node == NULL) { /* Problems anyway, @detailmenu should always be inside @menu. */ warning (_("@detailmenu seen before first node, creating `Top' node")); execute_string ("@node top\n@top Top\n"); } begin_insertion (detailmenu); } /* End existing insertion block. */ void cm_end () { char *temp; enum insertion_type type; if (!insertion_level) { line_error (_("Unmatched `%c%s'"), COMMAND_PREFIX, command); return; } get_rest_of_line (0, &temp); if (temp[0] == 0) line_error (_("`%c%s' needs something after it"), COMMAND_PREFIX, command); type = find_type_from_name (temp); if (type == bad_type) { line_error (_("Bad argument to `%s', `%s', using `%s'"), command, temp, insertion_type_pname (current_insertion_type ())); } if (xml && type == menu) /* fixme */ { xml_end_menu (); } end_insertion (type); free (temp); } /* @itemx, @item. */ static int itemx_flag = 0; /* Return whether CMD takes a brace-delimited {arg}. */ /*static */int command_needs_braces (cmd) char *cmd; { int i; for (i = 0; command_table[i].name; i++) { if (STREQ (command_table[i].name, cmd)) return command_table[i].argument_in_braces == BRACE_ARGS; } return 0; /* macro or alias */ } void cm_item () { char *rest_of_line, *item_func; /* Can only hack "@item" while inside of an insertion. */ if (insertion_level) { INSERTION_ELT *stack = insertion_stack; int original_input_text_offset; skip_whitespace (); original_input_text_offset = input_text_offset; get_rest_of_line (0, &rest_of_line); item_func = current_item_function (); /* Do the right thing depending on which insertion function is active. */ switch_top: switch (stack->insertion) { case multitable: multitable_item (); /* Support text directly after the @item. */ if (*rest_of_line) { line_number--; input_text_offset = original_input_text_offset; } break; case ifclear: case ifhtml: case ifinfo: case ifnothtml: case ifnotinfo: case ifnotplaintext: case ifnottex: case ifnotxml: case ifplaintext: case ifset: case iftex: case ifxml: case rawhtml: case rawtex: case tex: case cartouche: stack = stack->next; if (!stack) goto no_insertion; else goto switch_top; break; case menu: case quotation: case example: case smallexample: case lisp: case smalllisp: case format: case smallformat: case display: case smalldisplay: case group: line_error (_("@%s not meaningful inside `@%s' block"), command, insertion_type_pname (current_insertion_type ())); break; case itemize: case enumerate: if (itemx_flag) { line_error (_("@itemx not meaningful inside `%s' block"), insertion_type_pname (current_insertion_type ())); } else { if (html) { if (in_paragraph) { add_word (""); in_paragraph = 0; } add_word ("- "); } else if (xml) xml_begin_item (); else { start_paragraph (); kill_self_indent (-1); filling_enabled = indented_fill = 1; if (current_item_function ()) { output_column = current_indent - 2; indent (output_column); /* The item marker can be given with or without braces -- @bullet and @bullet{} are both ok. Or it might be something that doesn't take braces at all, such as "o" or "#" or "@ ". Thus, only supply braces if the item marker is a command, they haven't supplied braces themselves, and we know it needs them. */ if (item_func && *item_func) { if (*item_func == COMMAND_PREFIX && item_func[strlen (item_func) - 1] != '}' && command_needs_braces (item_func + 1)) execute_string ("%s{}", item_func); else execute_string ("%s", item_func); } insert (' '); output_column++; } else enumerate_item (); /* Special hack. This makes `close_paragraph' a no-op until `start_paragraph' has been called. */ must_start_paragraph = 1; } /* Handle text directly after the @item. */ if (*rest_of_line) { line_number--; input_text_offset = original_input_text_offset; } } break; case table: case ftable: case vtable: if (html) { static int last_html_output_position = 0; /* If nothing has been output since the last
- , remove the empty
- element. Some browsers render an extra empty line for
- , which makes @itemx conversion look ugly. */ if (last_html_output_position == output_position && strncmp ((char *) output_paragraph, "
- ", output_paragraph_offset) == 0) output_paragraph_offset = 0; /* Force the browser to render one blank line before each new @item in a table. But don't do that if this is the first
- after the
, or if we are converting @itemx. Note that there are some browsers which ignore
in this context, but I cannot find any way to force them all render exactly one blank line. */ if (!itemx_flag && strncmp ((char *) output_paragraph + output_paragraph_offset - sizeof (dl_tag) + 1, dl_tag, sizeof (dl_tag) - 1) != 0) add_word ("
"); add_word ("- "); if (item_func && *item_func) execute_string ("%s{%s}", item_func, rest_of_line); else execute_string ("%s", rest_of_line); if (current_insertion_type () == ftable) execute_string ("%cfindex %s\n", COMMAND_PREFIX, rest_of_line); if (current_insertion_type () == vtable) execute_string ("%cvindex %s\n", COMMAND_PREFIX, rest_of_line); /* Make sure output_position is updated, so we could remember it. */ close_single_paragraph (); last_html_output_position = output_position; add_word ("
- "); } else if (xml) /* && docbook)*/ /* 05-08 */ { xml_begin_table_item (); if (item_func && *item_func) execute_string ("%s{%s}", item_func, rest_of_line); else execute_string ("%s", rest_of_line); xml_continue_table_item (); } else { /* We need this to determine if we have two @item's in a row (see test just below). */ static int last_item_output_position = 0; /* Get rid of extra characters. */ kill_self_indent (-1); /* If we have one @item followed directly by another @item, we need to insert a blank line. This is not true for @itemx, though. */ if (!itemx_flag && last_item_output_position == output_position) insert ('\n'); /* `close_paragraph' almost does what we want. The problem is when paragraph_is_open, and last_char_was_newline, and the last newline has been turned into a space, because filling_enabled. I handle it here. */ if (last_char_was_newline && filling_enabled && paragraph_is_open) insert ('\n'); close_paragraph (); #if defined (INDENT_PARAGRAPHS_IN_TABLE) /* Indent on a new line, but back up one indentation level. */ { int save = inhibit_paragraph_indentation; inhibit_paragraph_indentation = 1; /* At this point, inserting any non-whitespace character will force the existing indentation to be output. */ add_char ('i'); inhibit_paragraph_indentation = save; } #else /* !INDENT_PARAGRAPHS_IN_TABLE */ add_char ('i'); #endif /* !INDENT_PARAGRAPHS_IN_TABLE */ output_paragraph_offset--; kill_self_indent (default_indentation_increment + 1); /* Add item's argument to the line. */ filling_enabled = 0; if (item_func && *item_func) execute_string ("%s{%s}", item_func, rest_of_line); else execute_string ("%s", rest_of_line); if (current_insertion_type () == ftable) execute_string ("%cfindex %s\n", COMMAND_PREFIX, rest_of_line); else if (current_insertion_type () == vtable) execute_string ("%cvindex %s\n", COMMAND_PREFIX, rest_of_line); /* Start a new line, and let start_paragraph () do the indenting of it for you. */ close_single_paragraph (); indented_fill = filling_enabled = 1; last_item_output_position = output_position; } } free (rest_of_line); } else { no_insertion: line_error (_("%c%s found outside of an insertion block"), COMMAND_PREFIX, command); } } void cm_itemx () { itemx_flag++; cm_item (); itemx_flag--; }