From 05f7b4b96773cc26ff78d5e68babd66861aa07a8 Mon Sep 17 00:00:00 2001 From: ru Date: Mon, 17 Jan 2000 10:39:58 +0000 Subject: Virgin import of GNU texinfo 4.0 --- contrib/texinfo/makeinfo/insertion.c | 1368 ++++++++++++++++++++++++++++++++++ 1 file changed, 1368 insertions(+) create mode 100644 contrib/texinfo/makeinfo/insertion.c (limited to 'contrib/texinfo/makeinfo/insertion.c') diff --git a/contrib/texinfo/makeinfo/insertion.c b/contrib/texinfo/makeinfo/insertion.c new file mode 100644 index 0000000..11b9089 --- /dev/null +++ b/contrib/texinfo/makeinfo/insertion.c @@ -0,0 +1,1368 @@ +/* insertion.c -- insertions for Texinfo. + $Id: insertion.c,v 1.27 1999/07/06 23:12:53 karl Exp $ + + Copyright (C) 1998, 99 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" + +/* Must match list in insertion.h. */ +static char *insertion_type_names[] = +{ + "cartouche", "defcv", "deffn", "defivar", "defmac", "defmethod", + "defop", "defopt", "defspec", "deftp", "deftypefn", "deftypefun", + "deftypeivar", "deftypemethod", "deftypeop", "deftypevar", + "deftypevr", "defun", "defvar", "defvr", "detailmenu", "direntry", + "display", "enumerate", "example", "flushleft", "flushright", + "format", "ftable", "group", "ifclear", "ifhtml", "ifinfo", + "ifnothtml", "ifnotinfo", "ifnottex", "ifset", "iftex", "itemize", + "lisp", "menu", "multitable", "quotation", "rawhtml", "rawtex", + "smalldisplay", "smallexample", "smallformat", "smalllisp", "table", + "tex", "vtable", "bad_type" +}; + +/* All nested environments. */ +INSERTION_ELT *insertion_stack = NULL; + +/* How deeply we're nested. */ +int insertion_level = 0; + +/* Whether to examine menu lines. */ +int in_menu = 0; + +/* How to examine menu lines. */ +int in_detailmenu = 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; + +/* Set to 1 if

is written in normal context. + Used for menu and itemize. */ +int in_paragraph = 0; + +static const char dl_tag[] = "

\n"; + +void +init_insertion_stack () +{ + insertion_stack = NULL; +} + +/* Return the type of the current insertion. */ +static enum insertion_type +current_insertion_type () +{ + return insertion_level ? insertion_stack->insertion : bad_type; +} + +/* Return the string which is the function to wrap around items, or NULL + if we're not in an environment where @item is ok. */ +static char * +current_item_function () +{ + int done = 0; + INSERTION_ELT *elt = insertion_stack; + + /* Skip down through the stack until we find an insertion with an + itemize function defined, i.e., skip conditionals, @cartouche, etc. */ + while (!done && elt) + { + switch (elt->insertion) + { + /* This list should match the one in cm_item. */ + case ifclear: + case ifhtml: + case ifinfo: + case ifnothtml: + case ifnotinfo: + case ifnottex: + case ifset: + case iftex: + case rawhtml: + case rawtex: + case tex: + case cartouche: + elt = elt->next; + break; + + default: + done = 1; + } + } + + /* item_function usually gets assigned the empty string. */ + return done && (*elt->item_function) ? elt->item_function : NULL; +} + +/* Parse the item marker function off the input. If result is just "@", + change it to "@ ", since "@" by itself is not a command. This makes + "@ ", "@\t", and "@\n" all the same, but their default meanings are + the same anyway, and let's not worry about supporting redefining them. */ +char * +get_item_function () +{ + char *item_function; + get_rest_of_line (0, &item_function); + + /* If we hit the end of text in get_rest_of_line, backing up + input pointer will cause the last character of the last line + be pushed back onto the input, which is wrong. */ + if (input_text_offset < input_text_length) + backup_input_pointer (); + + if (STREQ (item_function, "@")) + { + free (item_function); + item_function = xstrdup ("@ "); + } + + return item_function; +} + + /* Push the state of the current insertion on the stack. */ +void +push_insertion (type, item_function) + enum insertion_type type; + char *item_function; +{ + INSERTION_ELT *new = xmalloc (sizeof (INSERTION_ELT)); + + new->item_function = item_function; + new->filling_enabled = filling_enabled; + new->indented_fill = indented_fill; + new->insertion = type; + new->line_number = line_number; + new->filename = xstrdup (input_filename); + new->inhibited = inhibit_paragraph_indentation; + new->in_fixed_width_font = in_fixed_width_font; + new->next = insertion_stack; + insertion_stack = new; + insertion_level++; +} + + /* Pop the value on top of the insertion stack into the + global variables. */ +void +pop_insertion () +{ + INSERTION_ELT *temp = insertion_stack; + + if (temp == NULL) + return; + + in_fixed_width_font = temp->in_fixed_width_font; + inhibit_paragraph_indentation = temp->inhibited; + filling_enabled = temp->filling_enabled; + indented_fill = temp->indented_fill; + free_and_clear (&(temp->item_function)); + free_and_clear (&(temp->filename)); + insertion_stack = insertion_stack->next; + free (temp); + insertion_level--; +} + + /* Return a pointer to the print name of this + enumerated type. */ +char * +insertion_type_pname (type) + enum insertion_type type; +{ + if ((int) type < (int) bad_type) + return insertion_type_names[(int) type]; + else + return _("Broken-Type in insertion_type_pname"); +} + +/* Return the insertion_type associated with NAME. + If the type is not one of the known ones, return BAD_TYPE. */ +enum insertion_type +find_type_from_name (name) + char *name; +{ + int index = 0; + while (index < (int) bad_type) + { + if (STREQ (name, insertion_type_names[index])) + return (enum insertion_type) index; + if (index == rawhtml && STREQ (name, "html")) + return rawhtml; + if (index == rawtex && STREQ (name, "tex")) + return rawtex; + index++; + } + return bad_type; +} + +int +defun_insertion (type) + enum insertion_type type; +{ + return 0 + || (type == defcv) + || (type == deffn) + || (type == defivar) + || (type == defmac) + || (type == defmethod) + || (type == defop) + || (type == defopt) + || (type == defspec) + || (type == deftp) + || (type == deftypefn) + || (type == deftypefun) + || (type == deftypeivar) + || (type == deftypemethod) + || (type == deftypeop) + || (type == deftypevar) + || (type == deftypevr) + || (type == defun) + || (type == defvar) + || (type == defvr) + ; +} + +/* MAX_NS is the maximum nesting level for enumerations. I picked 100 + which seemed reasonable. This doesn't control the number of items, + just the number of nested lists. */ +#define max_stack_depth 100 +#define ENUM_DIGITS 1 +#define ENUM_ALPHA 2 +typedef struct { + int enumtype; + int enumval; +} DIGIT_ALPHA; + +DIGIT_ALPHA enumstack[max_stack_depth]; +int enumstack_offset = 0; +int current_enumval = 1; +int current_enumtype = ENUM_DIGITS; +char *enumeration_arg = NULL; + +void +start_enumerating (at, type) + int at, type; +{ + if ((enumstack_offset + 1) == max_stack_depth) + { + line_error (_("Enumeration stack overflow")); + return; + } + enumstack[enumstack_offset].enumtype = current_enumtype; + enumstack[enumstack_offset].enumval = current_enumval; + enumstack_offset++; + current_enumval = at; + current_enumtype = type; +} + +void +stop_enumerating () +{ + --enumstack_offset; + if (enumstack_offset < 0) + enumstack_offset = 0; + + current_enumval = enumstack[enumstack_offset].enumval; + current_enumtype = enumstack[enumstack_offset].enumtype; +} + +/* Place a letter or digits into the output stream. */ +void +enumerate_item () +{ + char temp[10]; + + if (current_enumtype == ENUM_ALPHA) + { + if (current_enumval == ('z' + 1) || current_enumval == ('Z' + 1)) + { + current_enumval = ((current_enumval - 1) == 'z' ? 'a' : 'A'); + warning (_("lettering overflow, restarting at %c"), current_enumval); + } + sprintf (temp, "%c. ", current_enumval); + } + else + sprintf (temp, "%d. ", current_enumval); + + indent (output_column += (current_indent - strlen (temp))); + add_word (temp); + current_enumval++; +} + +static void +enum_html () +{ + char type; + int start; + + if (isdigit (*enumeration_arg)) + { + type = '1'; + start = atoi (enumeration_arg); + } + else if (isupper (*enumeration_arg)) + { + type = 'A'; + start = *enumeration_arg - 'A' + 1; + } + else + { + type = 'a'; + start = *enumeration_arg - 'a' + 1; + } + + add_word_args ("
    \n", type, start); +} + +/* Conditionally parse based on the current command name. */ +void +command_name_condition () +{ + char *discarder = xmalloc (8 + strlen (command)); + + sprintf (discarder, "\n%cend %s", COMMAND_PREFIX, command); + discard_until (discarder); + discard_until ("\n"); + + free (discarder); +} + +/* This is where the work for all the "insertion" style + commands is done. A huge switch statement handles the + various setups, and generic code is on both sides. */ +void +begin_insertion (type) + enum insertion_type type; +{ + int no_discard = 0; + + if (defun_insertion (type)) + { + push_insertion (type, xstrdup ("")); + no_discard++; + } + else + push_insertion (type, get_item_function ()); + + switch (type) + { + case menu: + if (!no_headers) + close_paragraph (); + + filling_enabled = no_indent = 0; + inhibit_paragraph_indentation = 1; + + if (html) + { + had_menu_commentary = 1; + } + else if (!no_headers) + add_word ("* Menu:\n"); + + in_menu++; + in_fixed_width_font++; + no_discard++; + break; + + case detailmenu: + if (!in_menu) + { + if (!no_headers) + close_paragraph (); + + filling_enabled = no_indent = 0; + inhibit_paragraph_indentation = 1; + + no_discard++; + } + + in_fixed_width_font++; + in_detailmenu++; + break; + + case direntry: + if (html) + command_name_condition (); + else + { + close_single_paragraph (); + filling_enabled = no_indent = 0; + inhibit_paragraph_indentation = 1; + insert_string ("START-INFO-DIR-ENTRY\n"); + } + break; + + case quotation: + /* @quotation does filling (@display doesn't). */ + if (html) + add_word ("
    \n"); + else + { + close_single_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: if
     is 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 ("
    ");
    +
    +      if (type != format && type != smallformat)
    +        current_indent += default_indentation_increment;
    +      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); + } + 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 (); + + 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 (!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 ifnottex: + case ifset: + case iftex: + 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; + break; + + case flushright: + close_single_paragraph (); + filling_enabled = indented_fill = no_indent = 0; + inhibit_paragraph_indentation = 1; + force_flush_right++; + 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 (); + + switch (type) + { + /* Insertions which have no effect on paragraph formatting. */ + case ifclear: + case ifhtml: + case ifinfo: + case ifnothtml: + case ifnotinfo: + case ifnottex: + case ifset: + case iftex: + case rawtex: + break; + + case rawhtml: + escape_html = 1; + break; + + case direntry: /* Eaten if html. */ + insert_string ("END-INFO-DIR-ENTRY\n\n"); + close_insertion_paragraph (); + break; + + case detailmenu: + in_detailmenu--; /* No longer hacking menus. */ + if (!in_menu) + { + if (!no_headers) + 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: + 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--; + 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); + char *current_filename = input_filename; + + input_filename = insertion_stack->filename; + line_number = insertion_stack->line_number; + line_error (_("No matching `%cend %s'"), COMMAND_PREFIX, offender); + input_filename = current_filename; + pop_insertion (); + } + } + line_number = real_line_number; +} + +/* Insertion (environment) commands. */ + +void +cm_quotation () +{ + begin_insertion (quotation); +} + +void +cm_example () +{ + begin_insertion (example); +} + +void +cm_smallexample () +{ + begin_insertion (smallexample); +} + +void +cm_lisp () +{ + begin_insertion (lisp); +} + +void +cm_smalllisp () +{ + begin_insertion (smalllisp); +} + +/* @cartouche/@end cartouche draws box with rounded corners in + TeX output. Right now, just a no-op insertion. */ +void +cm_cartouche () +{ + begin_insertion (cartouche); +} + +void +cm_format () +{ + begin_insertion (format); +} + +void +cm_smallformat () +{ + begin_insertion (smallformat); +} + +void +cm_display () +{ + begin_insertion (display); +} + +void +cm_smalldisplay () +{ + begin_insertion (smalldisplay); +} + +void +cm_direntry () +{ + if (no_headers || html) + command_name_condition (); + else + begin_insertion (direntry); +} + +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"); +} + +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); +} + +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 (); +} + + +/* Insert raw HTML (no escaping of `<' etc.). */ +void +cm_html () +{ + if (process_html) + 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_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 (); +} + +/* 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 ())); + } + 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 ifnottex: + case ifset: + case iftex: + 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 + { + 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 unless + 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 + { + /* 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--; +} -- cgit v1.1