From 980e0006ad5e821ea7a78d7be62d2337a03b3c12 Mon Sep 17 00:00:00 2001 From: Ruslan Ermilov Date: Mon, 17 Jan 2000 10:45:43 +0000 Subject: [PATCH] This is the stock GNU texinfo 4.0 file --- contrib/texinfo/info/doc.c | 21 +- contrib/texinfo/info/funs.h | 13 + contrib/texinfo/info/infomap.c | 363 +- contrib/texinfo/info/nodemenu.c | 10 +- contrib/texinfo/info/session.c | 1180 +++- contrib/texinfo/info/terminal.c | 109 +- contrib/texinfo/makeinfo/makeinfo.c | 8657 +++++---------------------- 7 files changed, 2757 insertions(+), 7596 deletions(-) diff --git a/contrib/texinfo/info/doc.c b/contrib/texinfo/info/doc.c index 673839601123..e9756e4e8b70 100644 --- a/contrib/texinfo/info/doc.c +++ b/contrib/texinfo/info/doc.c @@ -35,21 +35,28 @@ FUNCTION_DOC function_doc_array[] = { { info_global_next_node, "global-next-node", "Move forwards or down through node structure" }, { info_global_prev_node, "global-prev-node", "Move backwards or up through node structure" }, { info_scroll_forward, "scroll-forward", "Scroll forward in this window" }, + { info_scroll_forward_set_window, "scroll-forward-set-window", "Scroll forward in this window and set default window size" }, { info_scroll_backward, "scroll-backward", "Scroll backward in this window" }, + { info_scroll_backward_set_window, "scroll-backward-set-window", "Scroll backward in this window and set default window size" }, { info_beginning_of_node, "beginning-of-node", "Move to the start of this node" }, { info_end_of_node, "end-of-node", "Move to the end of this node" }, + { info_down_line, "down-line", "Scroll down by lines" }, + { info_up_line, "up-line", "Scroll up by lines" }, + { info_scroll_half_screen_down, "scroll-half-screen-down", "Scroll down by half screen size" }, + { info_scroll_half_screen_up, "scroll-half-screen-up", "Scroll up by half screen size" }, { info_next_window, "next-window", "Select the next window" }, { info_prev_window, "prev-window", "Select the previous window" }, { info_split_window, "split-window", "Split the current window" }, { info_delete_window, "delete-window", "Delete the current window" }, { info_keep_one_window, "keep-one-window", "Delete all other windows" }, { info_scroll_other_window, "scroll-other-window", "Scroll the other window" }, + { info_scroll_other_window_backward, "scroll-other-window-backward", "Scroll the other window backward" }, { info_grow_window, "grow-window", "Grow (or shrink) this window" }, { info_tile_windows, "tile-windows", "Divide the available screen space among the visible windows" }, { info_toggle_wrap, "toggle-wrap", "Toggle the state of line wrapping in the current window" }, - { info_next_node, "next-node", "Select the `Next' node" }, - { info_prev_node, "prev-node", "Select the `Prev' node" }, - { info_up_node, "up-node", "Select the `Up' node" }, + { info_next_node, "next-node", "Select the Next node" }, + { info_prev_node, "prev-node", "Select the Prev node" }, + { info_up_node, "up-node", "Select the Up node" }, { info_last_node, "last-node", "Select the last node in this file" }, { info_first_node, "first-node", "Select the first node in this file" }, { info_last_menu_item, "last-menu-item", "Select the last item in this node's menu" }, @@ -59,6 +66,8 @@ FUNCTION_DOC function_doc_array[] = { { info_find_menu, "find-menu", "Move to the start of this node's menu" }, { info_visit_menu, "visit-menu", "Visit as many menu items at once as possible" }, { info_goto_node, "goto-node", "Read a node name and select it" }, + { info_menu_sequence, "menu-sequence", "Read a list of menus starting from dir and follow them" }, + { info_goto_invocation_node, "goto-invocation-node", "Find the node describing program invocation" }, { info_man, "man", "Read a manpage reference and select it" }, { info_top_node, "top-node", "Select the node `Top' in this file" }, { info_dir_node, "dir-node", "Select the node `(dir)'" }, @@ -66,14 +75,18 @@ FUNCTION_DOC function_doc_array[] = { { info_kill_node, "kill-node", "Kill this node" }, { info_view_file, "view-file", "Read the name of a file and select it" }, { info_print_node, "print-node", "Pipe the contents of this node through INFO_PRINT_COMMAND" }, + { info_search_case_sensitively, "search-case-sensitively", "Read a string and search for it case-sensitively" }, { info_search, "search", "Read a string and search for it" }, + { info_search_backward, "search-backward", "Read a string and search backward for it" }, + { info_search_next, "search-next", "Repeat last search in the same direction" }, + { info_search_previous, "search-previous", "Repeat last search in the reverse direction" }, { isearch_forward, "isearch-forward", "Search interactively for a string as you type it" }, { isearch_backward, "isearch-backward", "Search interactively for a string as you type it" }, { info_move_to_prev_xref, "move-to-prev-xref", "Move to the previous cross reference" }, { info_move_to_next_xref, "move-to-next-xref", "Move to the next cross reference" }, { info_select_reference_this_line, "select-reference-this-line", "Select reference or menu item appearing on this line" }, { info_abort_key, "abort-key", "Cancel current operation" }, - { info_move_to_window_line, "move-to-window-line", "Move to the cursor to a specific line of the window" }, + { info_move_to_window_line, "move-to-window-line", "Move the cursor to a specific line of the window" }, { info_redraw_display, "redraw-display", "Redraw the display" }, { info_quit, "quit", "Quit using Info" }, { info_do_lowercase_version, "do-lowercase-version", "" }, diff --git a/contrib/texinfo/info/funs.h b/contrib/texinfo/info/funs.h index 32d80d51c037..f5c4d33d14cb 100644 --- a/contrib/texinfo/info/funs.h +++ b/contrib/texinfo/info/funs.h @@ -12,15 +12,22 @@ extern void info_backward_word (); extern void info_global_next_node (); extern void info_global_prev_node (); extern void info_scroll_forward (); +extern void info_scroll_forward_set_window (); extern void info_scroll_backward (); +extern void info_scroll_backward_set_window (); extern void info_beginning_of_node (); extern void info_end_of_node (); +extern void info_down_line (); +extern void info_up_line (); +extern void info_scroll_half_screen_down (); +extern void info_scroll_half_screen_up (); extern void info_next_window (); extern void info_prev_window (); extern void info_split_window (); extern void info_delete_window (); extern void info_keep_one_window (); extern void info_scroll_other_window (); +extern void info_scroll_other_window_backward (); extern void info_grow_window (); extern void info_tile_windows (); extern void info_toggle_wrap (); @@ -36,6 +43,8 @@ extern void info_xref_item (); extern void info_find_menu (); extern void info_visit_menu (); extern void info_goto_node (); +extern void info_menu_sequence (); +extern void info_goto_invocation_node (); extern void info_man (); extern void info_top_node (); extern void info_dir_node (); @@ -43,7 +52,11 @@ extern void info_history_node (); extern void info_kill_node (); extern void info_view_file (); extern void info_print_node (); +extern void info_search_case_sensitively (); extern void info_search (); +extern void info_search_backward (); +extern void info_search_next (); +extern void info_search_previous (); extern void isearch_forward (); extern void isearch_backward (); extern void info_move_to_prev_xref (); diff --git a/contrib/texinfo/info/infomap.c b/contrib/texinfo/info/infomap.c index 7591283dc592..932435e8d845 100644 --- a/contrib/texinfo/info/infomap.c +++ b/contrib/texinfo/info/infomap.c @@ -1,7 +1,7 @@ /* infomap.c -- Keymaps for Info. - $Id: infomap.c,v 1.7 1997/07/31 20:37:32 karl Exp $ + $Id: infomap.c,v 1.20 1999/06/25 21:57:40 karl Exp $ - Copyright (C) 1993, 97 Free Software Foundation, Inc. + Copyright (C) 1993, 97, 98, 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 @@ -67,7 +67,7 @@ keymap_copy_keymap (map) return (keymap); } -/* Free the keymap and it's descendents. */ +/* Free the keymap and its descendants. */ void keymap_discard_keymap (map) Keymap (map); @@ -141,49 +141,54 @@ keymap_bind_keyseq (map, keyseq, keyentry) /* Initialize the standard info keymaps. */ -Keymap info_keymap = (Keymap)NULL; -Keymap echo_area_keymap = (Keymap)NULL; +Keymap info_keymap = NULL; +Keymap echo_area_keymap = NULL; -void -initialize_info_keymaps () +static void +initialize_emacs_like_keymaps () { - register int i; + int i; Keymap map; if (!info_keymap) { info_keymap = keymap_make_keymap (); - info_keymap[ESC].type = ISKMAP; - info_keymap[ESC].function = (VFunction *)keymap_make_keymap (); - info_keymap[Control ('x')].type = ISKMAP; - info_keymap[Control ('x')].function = (VFunction *)keymap_make_keymap (); echo_area_keymap = keymap_make_keymap (); - echo_area_keymap[ESC].type = ISKMAP; - echo_area_keymap[ESC].function = (VFunction *)keymap_make_keymap (); - echo_area_keymap[Control ('x')].type = ISKMAP; - echo_area_keymap[Control ('x')].function = - (VFunction *)keymap_make_keymap (); } + info_keymap[ESC].type = ISKMAP; + info_keymap[ESC].function = (VFunction *)keymap_make_keymap (); + info_keymap[Control ('x')].type = ISKMAP; + info_keymap[Control ('x')].function = (VFunction *)keymap_make_keymap (); + + /* Bind the echo area insert routines. Let's make all characters + insertable by default, regardless of which character set we might + be using. */ + for (i = 0; i < 256; i++) + echo_area_keymap[i].function = ea_insert; + + echo_area_keymap[ESC].type = ISKMAP; + echo_area_keymap[ESC].function = (VFunction *) keymap_make_keymap (); + echo_area_keymap[Control ('x')].type = ISKMAP; + echo_area_keymap[Control ('x')].function + = (VFunction *) keymap_make_keymap (); + /* Bind numeric arg functions for both echo area and info window maps. */ for (i = '0'; i < '9' + 1; i++) { - ((Keymap) info_keymap[ESC].function)[i].function = - ((Keymap) echo_area_keymap[ESC].function)[i].function = - info_add_digit_to_numeric_arg; + ((Keymap) info_keymap[ESC].function)[i].function + = ((Keymap) echo_area_keymap[ESC].function)[i].function + = info_add_digit_to_numeric_arg; } ((Keymap) info_keymap[ESC].function)['-'].function = ((Keymap) echo_area_keymap[ESC].function)['-'].function = info_add_digit_to_numeric_arg; + info_keymap['-'].function = info_add_digit_to_numeric_arg; + /* Bind the echo area routines. */ map = echo_area_keymap; - /* Bind the echo area insert routines. */ - for (i = 0; i < 160; i++) - if (isprint (i)) - map[i].function = ea_insert; - map[Control ('a')].function = ea_beg_of_line; map[Control ('b')].function = ea_backward; map[Control ('d')].function = ea_delete; @@ -203,7 +208,12 @@ initialize_info_keymaps () map[SPC].function = ea_complete; map[TAB].function = ea_complete; map['?'].function = ea_possible_completions; +#ifdef __MSDOS__ + /* PC users will lynch me if I don't give them their usual DEL effect... */ + map[DEL].function = ea_delete; +#else map[DEL].function = ea_rubout; +#endif /* Bind the echo area ESC keymap. */ map = (Keymap)echo_area_keymap[ESC].function; @@ -272,6 +282,7 @@ initialize_info_keymaps () map[Control ('u')].function = info_universal_argument; map[Control ('v')].function = info_scroll_forward; map[','].function = info_next_index_match; + map['/'].function = info_search; for (i = '1'; i < '9' + 1; i++) map[i].function = info_menu_digit; @@ -288,15 +299,19 @@ initialize_info_keymaps () map['e'].function = info_end_of_node; map['f'].function = info_xref_item; map['g'].function = info_goto_node; + map['G'].function = info_menu_sequence; map['h'].function = info_get_info_help_node; map['i'].function = info_index_search; + map['I'].function = info_goto_invocation_node; map['l'].function = info_history_node; map['m'].function = info_menu_item; map['n'].function = info_next_node; + map['O'].function = info_goto_invocation_node; map['p'].function = info_prev_node; map['q'].function = info_quit; map['r'].function = info_xref_item; map['s'].function = info_search; + map['S'].function = info_search_case_sensitively; map['t'].function = info_top_node; map['u'].function = info_up_node; map[DEL].function = info_scroll_backward; @@ -316,6 +331,7 @@ initialize_info_keymaps () #if defined (NAMED_FUNCTIONS) map['x'].function = info_execute_command; #endif /* NAMED_FUNCTIONS */ + map[DEL].function = info_scroll_other_window_backward; /* Bind members in the Control-X map for Info windows. */ map = (Keymap)info_keymap[Control ('x')].function; @@ -331,6 +347,8 @@ initialize_info_keymaps () map['^'].function = info_grow_window; map['b'].function = select_visited_node; map['k'].function = info_kill_node; + map['n'].function = info_search_next; + map['N'].function = info_search_previous; map['o'].function = info_next_window; map['t'].function = info_tile_windows; map['w'].function = info_toggle_wrap; @@ -359,6 +377,7 @@ initialize_info_keymaps () keymap_bind_keyseq (map, "\033OB", &map['f']); keymap_bind_keyseq (map, "\033[B", &map['f']); keymap_bind_keyseq (map, term_kN, &map[Control ('v')]); /* pagedown */ + keymap_bind_keyseq (map, term_kP, &map[DEL]); /* pageup */ /* The alternative to this definition of a `main map' key in the `ESC map' section, is something like: @@ -366,3 +385,297 @@ initialize_info_keymaps () */ keymap_bind_keyseq (info_keymap/*sic*/, term_kP, &map['v']); /* pageup */ } + +static void +initialize_vi_like_keymaps () +{ + register int i; + Keymap map; + + if (!info_keymap) + { + info_keymap = keymap_make_keymap (); + echo_area_keymap = keymap_make_keymap (); + } + + info_keymap[ESC].type = ISKMAP; + info_keymap[ESC].function = (VFunction *)keymap_make_keymap (); + info_keymap[Control ('x')].type = ISKMAP; + info_keymap[Control ('x')].function = (VFunction *)keymap_make_keymap (); + + /* Bind the echo area insert routines. */ + for (i = 0; i < 256; i++) + echo_area_keymap[i].function = ea_insert; + + echo_area_keymap[ESC].type = ISKMAP; + echo_area_keymap[ESC].function = (VFunction *)keymap_make_keymap (); + echo_area_keymap[Control ('x')].type = ISKMAP; + echo_area_keymap[Control ('x')].function = + (VFunction *)keymap_make_keymap (); + + /* Bind numeric arg functions for both echo area and info window maps. */ + for (i = '0'; i < '9' + 1; i++) + { + info_keymap[i].function = + ((Keymap) echo_area_keymap[ESC].function)[i].function = + info_add_digit_to_numeric_arg; + } + info_keymap['-'].function = + ((Keymap) echo_area_keymap[ESC].function)['-'].function = + info_add_digit_to_numeric_arg; + + /* Bind the echo area routines. */ + map = echo_area_keymap; + + map[Control ('a')].function = ea_beg_of_line; + map[Control ('b')].function = ea_backward; + map[Control ('d')].function = ea_delete; + map[Control ('e')].function = ea_end_of_line; + map[Control ('f')].function = ea_forward; + map[Control ('g')].function = ea_abort; + map[Control ('h')].function = ea_rubout; + map[Control ('k')].function = ea_kill_line; + map[Control ('l')].function = info_redraw_display; + map[Control ('q')].function = ea_quoted_insert; + map[Control ('t')].function = ea_transpose_chars; + map[Control ('u')].function = ea_abort; + map[Control ('v')].function = ea_quoted_insert; + map[Control ('y')].function = ea_yank; + + map[LFD].function = ea_newline; + map[RET].function = ea_newline; + map[SPC].function = ea_complete; + map[TAB].function = ea_complete; + map['?'].function = ea_possible_completions; +#ifdef __MSDOS__ + /* PC users will lynch me if I don't give them their usual DEL effect... */ + map[DEL].function = ea_delete; +#else + map[DEL].function = ea_rubout; +#endif + + /* Bind the echo area ESC keymap. */ + map = (Keymap)echo_area_keymap[ESC].function; + + map[Control ('g')].function = ea_abort; + map[Control ('h')].function = ea_backward_kill_word; + map[Control ('v')].function = ea_scroll_completions_window; + map['0'].function = ea_beg_of_line; + map['$'].function = ea_end_of_line; + map['b'].function = ea_backward_word; + map['d'].function = ea_kill_word; + map['f'].function = ea_forward_word; + map['h'].function = ea_forward; + map['l'].function = ea_backward; + map['w'].function = ea_forward_word; + map['x'].function = ea_delete; + map['X'].function = ea_kill_word; + map['y'].function = ea_yank_pop; + map['?'].function = ea_possible_completions; + map[TAB].function = ea_tab_insert; + map[DEL].function = ea_kill_word; + + /* Bind the echo area Control-x keymap. */ + map = (Keymap)echo_area_keymap[Control ('x')].function; + + map['o'].function = info_next_window; + map[DEL].function = ea_backward_kill_line; + + /* Arrow key bindings for echo area keymaps. It seems that some + terminals do not match their termcap entries, so it's best to just + define everything with both of the usual prefixes. */ + map = echo_area_keymap; + keymap_bind_keyseq (map, term_ku, &map[Control ('p')]); /* up */ + keymap_bind_keyseq (map, "\033OA", &map[Control ('p')]); + keymap_bind_keyseq (map, "\033[A", &map[Control ('p')]); + keymap_bind_keyseq (map, term_kd, &map[Control ('n')]); /* down */ + keymap_bind_keyseq (map, "\033OB", &map[Control ('n')]); + keymap_bind_keyseq (map, "\033[B", &map[Control ('n')]); + keymap_bind_keyseq (map, term_kr, &map[Control ('f')]); /* right */ + keymap_bind_keyseq (map, "\033OC", &map[Control ('f')]); + keymap_bind_keyseq (map, "\033[C", &map[Control ('f')]); + keymap_bind_keyseq (map, term_kl, &map[Control ('b')]); /* left */ + keymap_bind_keyseq (map, "\033OD", &map[Control ('b')]); + keymap_bind_keyseq (map, "\033[D", &map[Control ('b')]); + + map = (Keymap)echo_area_keymap[ESC].function; + keymap_bind_keyseq (map, term_kl, &map['b']); /* left */ + keymap_bind_keyseq (map, "\033OA", &map['b']); + keymap_bind_keyseq (map, "\033[A", &map['b']); + keymap_bind_keyseq (map, term_kr, &map['f']); /* right */ + keymap_bind_keyseq (map, "\033OB", &map['f']); + keymap_bind_keyseq (map, "\033[B", &map['f']); + + /* Bind commands for Info window keymaps. */ + map = info_keymap; + map[TAB].function = info_move_to_next_xref; + map[LFD].function = info_down_line; + map[RET].function = info_down_line; + map[SPC].function = info_scroll_forward; + map[Control ('a')].function = info_beginning_of_line; + map[Control ('b')].function = info_scroll_backward; + map[Control ('d')].function = info_scroll_half_screen_down; + map[Control ('e')].function = info_down_line; + map[Control ('f')].function = info_scroll_forward; + map[Control ('g')].function = info_abort_key; + map[Control ('k')].function = info_up_line; + map[Control ('l')].function = info_redraw_display; + map[Control ('n')].function = info_down_line; + map[Control ('p')].function = info_up_line; + map[Control ('r')].function = info_redraw_display; + map[Control ('s')].function = isearch_forward; + map[Control ('u')].function = info_scroll_half_screen_up; + map[Control ('v')].function = info_scroll_forward; + map[Control ('y')].function = info_up_line; + map[','].function = info_next_index_match; + map['/'].function = info_search; + + for (i = '1'; i < '9' + 1; i++) + ((Keymap) info_keymap[ESC].function)[i].function = info_menu_digit; + ((Keymap) info_keymap[ESC].function)['0'].function = info_last_menu_item; + + map['<'].function = info_first_node; + map['>'].function = info_last_node; + map['?'].function = info_search_backward; + map['['].function = info_global_prev_node; + map[']'].function = info_global_next_node; + map['\''].function = info_history_node; + + map['b'].function = info_scroll_backward; + map['d'].function = info_scroll_half_screen_down; + map['e'].function = info_down_line; + map['E'].function = info_view_file; + map['f'].function = info_scroll_forward; + map['F'].function = info_scroll_forward; + map['g'].function = info_first_node; + map['G'].function = info_last_node; + map['h'].function = info_get_help_window; + map['H'].function = info_get_help_window; + map['i'].function = info_index_search; + map['I'].function = info_goto_invocation_node; + map['j'].function = info_down_line; + map['k'].function = info_up_line; + map['l'].function = info_history_node; + map['m'].function = info_menu_item; + map['n'].function = info_search_next; + map['N'].function = info_search_previous; + map['O'].function = info_goto_invocation_node; + map['p'].function = info_prev_node; + map['q'].function = info_quit; + map['Q'].function = info_quit; + map['r'].function = info_redraw_display; + map['R'].function = info_redraw_display; + map['s'].function = info_search; + map['S'].function = info_search_case_sensitively; + map['t'].function = info_top_node; + map['u'].function = info_scroll_half_screen_up; + map['w'].function = info_scroll_backward_set_window; + map['y'].function = info_up_line; + map['z'].function = info_scroll_forward_set_window; + map['Z'].function = NULL; /* unbind, so it works to bind "ZZ" below */ + map[DEL].function = info_scroll_backward; + keymap_bind_keyseq (map, ":q", &map['q']); + keymap_bind_keyseq (map, ":Q", &map['q']); + keymap_bind_keyseq (map, "ZZ", &map['q']); + + /* Bind members in the ESC map for Info windows. */ + map = (Keymap)info_keymap[ESC].function; + map[Control ('f')].function = info_show_footnotes; + map[Control ('g')].function = info_abort_key; + map[TAB].function = info_move_to_prev_xref; + map[SPC].function = info_scroll_forward; + map[Control ('v')].function = info_scroll_other_window; + map['<'].function = info_beginning_of_node; + map['>'].function = info_end_of_node; + map['/'].function = info_search; + map['?'].function = info_search_backward; + map['b'].function = info_beginning_of_node; + map['d'].function = info_dir_node; + map['e'].function = info_end_of_node; + map['f'].function = info_xref_item; + map['g'].function = info_select_reference_this_line; + map['h'].function = info_get_info_help_node; + map['m'].function = info_menu_item; + map['n'].function = info_search; + map['N'].function = info_search_backward; + map['r'].function = isearch_backward; + map['s'].function = isearch_forward; + map['t'].function = info_top_node; + map['v'].function = info_scroll_backward; +#if defined (NAMED_FUNCTIONS) + map['x'].function = info_execute_command; +#endif /* NAMED_FUNCTIONS */ + map[DEL].function = info_scroll_other_window_backward; + + /* Bind members in the Control-X map for Info windows. */ + map = (Keymap)info_keymap[Control ('x')].function; + + map[Control ('b')].function = list_visited_nodes; + map[Control ('c')].function = info_quit; + map[Control ('f')].function = info_view_file; + map[Control ('g')].function = info_abort_key; + map[Control ('v')].function = info_view_file; + map[LFD].function = info_select_reference_this_line; + map[RET].function = info_select_reference_this_line; + map['0'].function = info_delete_window; + map['1'].function = info_keep_one_window; + map['2'].function = info_split_window; + map['^'].function = info_grow_window; + map['b'].function = select_visited_node; + map['g'].function = info_goto_node; + map['i'].function = info_index_search; + map['I'].function = info_goto_invocation_node; + map['k'].function = info_kill_node; + map['n'].function = info_next_node; + map['o'].function = info_next_window; + map['O'].function = info_goto_invocation_node; + map['p'].function = info_prev_node; + map['r'].function = info_xref_item; + map['t'].function = info_tile_windows; + map['u'].function = info_up_node; + map['w'].function = info_toggle_wrap; + map[','].function = info_next_index_match; + keymap_bind_keyseq (info_keymap, ":e", &map[Control ('v')]); + + /* Arrow key bindings for Info windows keymap. */ + map = info_keymap; + keymap_bind_keyseq (map, term_kN, &map[Control ('v')]); /* pagedown */ + keymap_bind_keyseq (map, term_ku, &map[Control ('p')]); /* up */ + keymap_bind_keyseq (map, "\033OA", &map[Control ('p')]); + keymap_bind_keyseq (map, "\033[A", &map[Control ('p')]); + keymap_bind_keyseq (map, term_kd, &map[Control ('n')]); /* down */ + keymap_bind_keyseq (map, "\033OB", &map[Control ('n')]); + keymap_bind_keyseq (map, "\033[B", &map[Control ('n')]); + keymap_bind_keyseq (map, term_kr, &map[Control ('f')]); /* right */ + keymap_bind_keyseq (map, "\033OC", &map[Control ('f')]); + keymap_bind_keyseq (map, "\033[C", &map[Control ('f')]); + keymap_bind_keyseq (map, term_kl, &map[Control ('b')]); /* left */ + keymap_bind_keyseq (map, "\033OD", &map[Control ('b')]); + keymap_bind_keyseq (map, "\033[D", &map[Control ('b')]); + + map = (Keymap)info_keymap[ESC].function; + keymap_bind_keyseq (map, term_kl, &map['b']); /* left */ + keymap_bind_keyseq (map, "\033OA", &map['b']); + keymap_bind_keyseq (map, "\033[A", &map['b']); + keymap_bind_keyseq (map, term_kr, &map['f']); /* right */ + keymap_bind_keyseq (map, "\033OB", &map['f']); + keymap_bind_keyseq (map, "\033[B", &map['f']); + keymap_bind_keyseq (map, term_kN, &map[Control ('v')]); /* pagedown */ + keymap_bind_keyseq (map, term_kP, &map[DEL]); /* pageup */ + + /* The alternative to this definition of a `main map' key in the + `ESC map' section, is something like: + keymap_bind_keyseq (map, term_kP, &((KeyMap)map[ESC].function).map['v']); + */ + keymap_bind_keyseq (info_keymap/*sic*/, term_kP, &map['v']); /* pageup */ +} + +void +initialize_info_keymaps () +{ + if (vi_keys_p) + initialize_vi_like_keymaps (); + else + initialize_emacs_like_keymaps (); +} + diff --git a/contrib/texinfo/info/nodemenu.c b/contrib/texinfo/info/nodemenu.c index 263265747427..b109057a347c 100644 --- a/contrib/texinfo/info/nodemenu.c +++ b/contrib/texinfo/info/nodemenu.c @@ -1,7 +1,7 @@ -/* nodemenu.c -- Produce a menu of all visited nodes. - $Id: nodemenu.c,v 1.7 1997/07/24 21:30:30 karl Exp $ +/* nodemenu.c -- produce a menu of all visited nodes. + $Id: nodemenu.c,v 1.8 1998/06/28 19:54:27 karl Exp $ - Copyright (C) 1993, 97 Free Software Foundation, Inc. + Copyright (C) 1993, 97, 98 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 @@ -168,7 +168,9 @@ get_visited_nodes (filter_func) /* Delete duplicates. */ for (i = 0, newlen = 1; i < lines_index - 1; i++) { - if (strcmp (lines[i], lines[i + 1]) == 0) + /* Use FILENAME_CMP here, since the most important piece + of info in each line is the file name of the node. */ + if (FILENAME_CMP (lines[i], lines[i + 1]) == 0) { free (lines[i]); lines[i] = (char *)NULL; diff --git a/contrib/texinfo/info/session.c b/contrib/texinfo/info/session.c index 63a6ecdd35f3..69b138d9cbba 100644 --- a/contrib/texinfo/info/session.c +++ b/contrib/texinfo/info/session.c @@ -1,7 +1,7 @@ -/* session.c -- The user windowing interface to Info. - $Id: session.c,v 1.13 1998/02/22 22:38:30 karl Exp $ +/* session.c -- user windowing interface to Info. + $Id: session.c,v 1.38 1999/09/25 16:10:04 karl Exp $ - Copyright (C) 1993, 96, 97 Free Software Foundation, Inc. + Copyright (C) 1993, 96, 97, 98, 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 @@ -31,6 +31,12 @@ # include "man.h" #endif +#ifdef M_XENIX +/* SCO 3.2v5.0.2 defines but does not correctly declare strncasecmp. + Since we use it as a symbol, have to get it right. --gildea, 1jul99. */ +extern int strncasecmp (const char *, const char *, size_t); +#endif + static void info_clear_pending_input (), info_set_pending_input (); static void info_handle_pointer (); @@ -94,7 +100,7 @@ begin_multiple_window_info_session (filename, nodenames) /* Find the largest window in WINDOWS, and make that be the active one. Then split it and add our window and node to the list of remembered windows and nodes. Then tile the windows. */ - register WINDOW *win, *largest = (WINDOW *)NULL; + WINDOW *win, *largest = NULL; int max_height = 0; for (win = windows; win; win = win->next) @@ -107,9 +113,9 @@ begin_multiple_window_info_session (filename, nodenames) if (!largest) { display_update_display (windows); - info_error (CANT_FIND_WIND); + info_error (msg_cant_find_window); info_session (); - exit (0); + xexit (0); } active_window = largest; @@ -122,9 +128,9 @@ begin_multiple_window_info_session (filename, nodenames) else { display_update_display (windows); - info_error (WIN_TOO_SMALL); + info_error (msg_win_too_small); info_session (); - exit (0); + xexit (0); } } } @@ -134,13 +140,14 @@ begin_multiple_window_info_session (filename, nodenames) /* Start an info session with INITIAL_NODE, and an error message in the echo area made from FORMAT and ARG. */ void -begin_info_session_with_error (initial_node, format, arg) +begin_info_session_with_error (initial_node, format, arg1, arg2) NODE *initial_node; char *format; - void *arg; + void *arg1; + void *arg2; { initialize_info_session (initial_node, 1); - info_error (format, arg, (void *)NULL); + info_error (format, arg1, arg2); info_session (); } @@ -159,9 +166,9 @@ display_startup_message_and_start () char *format; format = replace_in_documentation - (_("Welcome to Info version %s. \"\\[get-help-window]\" for help, \"\\[menu-item]\" for menu item.")); + (_("Welcome to Info version %s. Type \\[get-help-window] for help, \\[menu-item] for menu item.")); - window_message_in_echo_area (format, version_string ()); + window_message_in_echo_area (format, VERSION); info_session (); } @@ -267,8 +274,8 @@ initialize_info_session (node, clear_screen) if (!term_name) term_name = "dumb"; - info_error (TERM_TOO_DUMB, term_name); - exit (1); + info_error (msg_term_too_dumb, term_name); + xexit (1); } if (clear_screen) @@ -281,7 +288,7 @@ initialize_info_session (node, clear_screen) window_initialize_windows (screenwidth, screenheight); initialize_info_signal_handler (); display_initialize_display (screenwidth, screenheight); - info_set_node_of_window (active_window, node); + info_set_node_of_window (0, active_window, node); /* Tell the window system how to notify us when a window needs to be asynchronously deleted (e.g., user resizes window very small). */ @@ -291,7 +298,7 @@ initialize_info_session (node, clear_screen) standard input. */ if (!info_input_stream) { - setbuf(stdin, NULL); + setbuf (stdin, NULL); info_input_stream = stdin; } @@ -305,7 +312,8 @@ info_set_input_from_file (filename) { FILE *stream; - stream = fopen (filename, "r"); + /* Input may include binary characters. */ + stream = fopen (filename, FOPEN_RBIN); if (!stream) return; @@ -484,12 +492,17 @@ forget_window_and_nodes (window) /* Set WINDOW to show NODE. Remember the new window in our list of Info windows. If we are doing automatic footnote display, also try to display - the footnotes for this window. */ + the footnotes for this window. If REMEMBER is nonzero, first call + set_remembered_pagetop_and_point. */ void -info_set_node_of_window (window, node) +info_set_node_of_window (remember, window, node) + int remember; WINDOW *window; NODE *node; { + if (remember) + set_remembered_pagetop_and_point (window); + /* Put this node into the window. */ window_set_node_of_window (window, node); @@ -607,7 +620,7 @@ move_to_new_line (old, new, window) { if (old == -1) { - info_error (CANT_FIND_POINT); + info_error (msg_cant_find_point); } else { @@ -686,7 +699,7 @@ DECLARE_INFO_COMMAND (info_beginning_of_line, _("Move to the start of the line") for (; (point) && (buffer[point - 1] != '\n'); point--); - /* If at a line start alreay, do nothing. */ + /* If at a line start already, do nothing. */ if (point != window->point) { window->point = point; @@ -827,38 +840,6 @@ DECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word")) info_show_point (window); } -/* Here is a list of time counter names which correspond to ordinal numbers. - It is used to print "once" instead of "1". */ -static char *counter_names[] = { - "not at all", "once", "twice", "three", "four", "five", "six", - (char *)NULL -}; - -/* Buffer used to return values from times_description (). */ -static char td_buffer[50]; - -/* Function returns a static string fully describing the number of times - present in COUNT. */ -static char * -times_description (count) - int count; -{ - register int i; - - td_buffer[0] = '\0'; - - for (i = 0; counter_names[i]; i++) - if (count == i) - break; - - if (counter_names[i]) - sprintf (td_buffer, "%s%s", counter_names[i], count > 2 ? _(" times") : ""); - else - sprintf (td_buffer, _("%d times"), count); - - return (td_buffer); -} - /* Variable controlling the behaviour of default scrolling when you are already at the bottom of a node. Possible values are defined in session.h. The meanings are: @@ -877,6 +858,14 @@ char *info_scroll_choices[] = { "Continuous", "Next Only", "Page Only", (char *)NULL }; +/* Default window sizes for scrolling commands. */ +int default_window_size = -1; /* meaning 1 window-full */ +int default_scroll_size = -1; /* meaning half screen size */ + +#define INFO_LABEL_FOUND() \ + (info_parsed_nodename || (info_parsed_filename \ + && !is_dir_name (info_parsed_filename))) + /* Move to 1st menu item, Next, Up/Next, or error in this window. */ static void forward_move_node_structure (window, behaviour) @@ -886,17 +875,17 @@ forward_move_node_structure (window, behaviour) switch (behaviour) { case IS_PageOnly: - info_error (AT_NODE_BOTTOM); + info_error (msg_at_node_bottom); break; case IS_NextOnly: info_next_label_of_node (window->node); if (!info_parsed_nodename && !info_parsed_filename) - info_error (_("No \"Next\" pointer for this node.")); + info_error (msg_no_pointer, _("Next")); else { - window_message_in_echo_area (_("Following \"Next\" node...")); - info_handle_pointer (_("Next"), window); + window_message_in_echo_area (_("Following Next node...")); + info_handle_pointer ("Next", window); } break; @@ -921,10 +910,10 @@ forward_move_node_structure (window, behaviour) /* Okay, this node does not contain a menu. If it contains a "Next:" pointer, use that. */ info_next_label_of_node (window->node); - if (info_label_was_found) + if (INFO_LABEL_FOUND ()) { - window_message_in_echo_area (_("Selecting \"Next\" node...")); - info_handle_pointer (_("Next"), window); + window_message_in_echo_area (_("Selecting Next node...")); + info_handle_pointer ("Next", window); return; } @@ -945,9 +934,9 @@ forward_move_node_structure (window, behaviour) while (!info_error_was_printed) { info_up_label_of_node (window->node); - if (info_label_was_found) + if (INFO_LABEL_FOUND ()) { - info_handle_pointer (_("Up"), window); + info_handle_pointer ("Up", window); if (info_error_was_printed) continue; @@ -956,7 +945,7 @@ forward_move_node_structure (window, behaviour) info_next_label_of_node (window->node); /* If no "Next" pointer, keep backing up. */ - if (!info_label_was_found) + if (!INFO_LABEL_FOUND ()) continue; /* If this node's first menu item is the same as this node's @@ -993,10 +982,10 @@ forward_move_node_structure (window, behaviour) /* This node has a "Next" pointer, and it is not the same as the first menu item found in this node. */ window_message_in_echo_area - ("Moving \"Up\" %s, then \"Next\".", - times_description (up_counter)); + (_("Moving Up %d time(s), then Next."), + up_counter); - info_handle_pointer (_("Next"), window); + info_handle_pointer ("Next", window); return; } else @@ -1017,7 +1006,7 @@ forward_move_node_structure (window, behaviour) window->point = info_win->points[old_current]; recalculate_line_starts (window); window->flags |= W_UpdateWindow; - info_error (_("No more nodes.")); + info_error (_("No more nodes within this document.")); } } } @@ -1035,32 +1024,34 @@ backward_move_node_structure (window, behaviour) switch (behaviour) { case IS_PageOnly: - info_error (AT_NODE_TOP); + info_error (msg_at_node_top); break; case IS_NextOnly: info_prev_label_of_node (window->node); if (!info_parsed_nodename && !info_parsed_filename) - info_error (_("No \"Prev\" for this node.")); + info_error (_("No `Prev' for this node.")); else { - window_message_in_echo_area (_("Moving \"Prev\" in this window.")); - info_handle_pointer (_("Prev"), window); + window_message_in_echo_area (_("Moving Prev in this window.")); + info_handle_pointer ("Prev", window); } break; case IS_Continuous: info_prev_label_of_node (window->node); - if (!info_parsed_nodename && !info_parsed_filename) + if (!info_parsed_nodename && (!info_parsed_filename + || is_dir_name (info_parsed_filename))) { info_up_label_of_node (window->node); - if (!info_parsed_nodename && !info_parsed_filename) - info_error (_("No \"Prev\" or \"Up\" for this node.")); + if (!info_parsed_nodename && (!info_parsed_filename + || is_dir_name (info_parsed_filename))) + info_error (_("No `Prev' or `Up' for this node within this document.")); else { - window_message_in_echo_area (_("Moving \"Up\" in this window.")); - info_handle_pointer (_("Up"), window); + window_message_in_echo_area (_("Moving Up in this window.")); + info_handle_pointer ("Up", window); } } else @@ -1097,8 +1088,8 @@ backward_move_node_structure (window, behaviour) /* Move to the previous node. If this node now contains a menu, and we have not inhibited movement to it, move to the node corresponding to the last menu item. */ - window_message_in_echo_area (_("Moving \"Prev\" in this window.")); - info_handle_pointer (_("Prev"), window); + window_message_in_echo_area (_("Moving Prev in this window.")); + info_handle_pointer ("Prev", window); if (!inhibit_menu_traversing) { @@ -1107,7 +1098,7 @@ backward_move_node_structure (window, behaviour) { info_free_references (menu); window_message_in_echo_area - (_("Moving to \"Prev\"'s last menu item.")); + (_("Moving to `Prev's last menu item.")); info_menu_digit (window, 1, '0'); } } @@ -1161,7 +1152,9 @@ DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window")) lines to the top of this window, Or, if at bottom of window, and the user wishes to scroll through nodes get the "Next" node for this window. */ - if (!info_explicit_arg && count == 1) + if (default_window_size > 0) + desired_top = window->pagetop + default_window_size; + else if (!info_explicit_arg && count == 1) { desired_top = window->pagetop + (window->height - 2); @@ -1193,6 +1186,16 @@ DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window")) } } +/* Like info_scroll_forward, but sets default_window_size as a side + effect. */ +DECLARE_INFO_COMMAND (info_scroll_forward_set_window, + _("Scroll forward in this window and set default window size")) +{ + if (info_explicit_arg) + default_window_size = count; + info_scroll_forward (window, count, key); +} + /* Show the previous screen of WINDOW's node. */ DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window")) { @@ -1205,7 +1208,9 @@ DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window")) /* Without an explicit numeric argument, scroll the top two lines to the bottom of this window, or move to the previous, or Up'th node. */ - if (!info_explicit_arg && count == 1) + if (default_window_size > 0) + desired_top = window->pagetop - default_window_size; + else if (!info_explicit_arg && count == 1) { desired_top = window->pagetop - (window->height - 2); @@ -1233,6 +1238,16 @@ DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window")) } } +/* Like info_scroll_backward, but sets default_window_size as a side + effect. */ +DECLARE_INFO_COMMAND (info_scroll_backward_set_window, + _("Scroll backward in this window and set default window size")) +{ + if (info_explicit_arg) + default_window_size = count; + info_scroll_backward (window, count, key); +} + /* Move to the beginning of the node. */ DECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node")) { @@ -1246,6 +1261,90 @@ DECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node")) window->point = window->node->nodelen - 1; info_show_point (window); } + +/* Scroll the window forward by N lines. */ +DECLARE_INFO_COMMAND (info_down_line, _("Scroll down by lines")) +{ + if (count < 0) + info_up_line (window, -count, key); + else + { + int desired_top = window->pagetop + count; + + if (desired_top >= window->line_count) + desired_top = window->line_count - 2; + + if (window->pagetop <= desired_top) + set_window_pagetop (window, desired_top); + } +} + +/* Scroll the window backward by N lines. */ +DECLARE_INFO_COMMAND (info_up_line, _("Scroll up by lines")) +{ + if (count < 0) + info_down_line (window, -count, key); + else + { + int desired_top = window->pagetop - count; + + if (desired_top < 0) + desired_top = 0; + + set_window_pagetop (window, desired_top); + } +} + +/* Scroll the window forward by N lines and remember N as default for + subsequent commands. */ +DECLARE_INFO_COMMAND (info_scroll_half_screen_down, + _("Scroll down by half screen size")) +{ + if (count < 0) + info_scroll_half_screen_up (window -count, key); + else + { + int scroll_size = (the_screen->height + 1) / 2; + int desired_top; + + if (info_explicit_arg) + default_scroll_size = count; + if (default_scroll_size > 0) + scroll_size = default_scroll_size; + + desired_top = window->pagetop + scroll_size; + if (desired_top >= window->line_count) + desired_top = window->line_count - 2; + + if (window->pagetop <= desired_top) + set_window_pagetop (window, desired_top); + } +} + +/* Scroll the window backward by N lines and remember N as default for + subsequent commands. */ +DECLARE_INFO_COMMAND (info_scroll_half_screen_up, + _("Scroll up by half screen size")) +{ + if (count < 0) + info_scroll_half_screen_down (window -count, key); + else + { + int scroll_size = (the_screen->height + 1) / 2; + int desired_top; + + if (info_explicit_arg) + default_scroll_size = count; + if (default_scroll_size > 0) + scroll_size = default_scroll_size; + + desired_top = window->pagetop - scroll_size; + if (desired_top < 0) + desired_top = 0; + + set_window_pagetop (window, desired_top); + } +} /* **************************************************************** */ /* */ @@ -1265,7 +1364,7 @@ DECLARE_INFO_COMMAND (info_next_window, _("Select the next window")) /* If no other window, error now. */ if (!windows->next && !echo_area_is_active) { - info_error (ONE_WINDOW); + info_error (msg_one_window); return; } @@ -1305,7 +1404,7 @@ DECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window")) if (!windows->next && !echo_area_is_active) { - info_error (ONE_WINDOW); + info_error (msg_one_window); return; } @@ -1361,7 +1460,7 @@ DECLARE_INFO_COMMAND (info_split_window, _("Split the current window")) if (!split) { - info_error (WIN_TOO_SMALL); + info_error (msg_win_too_small); } else { @@ -1429,7 +1528,7 @@ DECLARE_INFO_COMMAND (info_delete_window, _("Delete the current window")) { if (!windows->next) { - info_error (CANT_KILL_LAST); + info_error (msg_cant_kill_last); } else if (window->flags & W_WindowIsPerm) { @@ -1519,7 +1618,7 @@ DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window")) /* If only one window, give up. */ if (!windows->next) { - info_error (ONE_WINDOW); + info_error (msg_one_window); return; } @@ -1531,6 +1630,13 @@ DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window")) info_scroll_forward (other, count, key); } +/* Scroll the "other" window of WINDOW. */ +DECLARE_INFO_COMMAND (info_scroll_other_window_backward, + _("Scroll the other window backward")) +{ + info_scroll_other_window (window, -count, key); +} + /* Change the size of WINDOW by AMOUNT. */ DECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window")) { @@ -1561,6 +1667,28 @@ DECLARE_INFO_COMMAND (info_toggle_wrap, /* */ /* **************************************************************** */ +/* Return (FILENAME)NODENAME for NODE, or just NODENAME if NODE's + filename is not set. */ +char * +node_printed_rep (node) + NODE *node; +{ + char *rep; + + if (node->filename) + { + char *filename + = filename_non_directory (node->parent ? node->parent : node->filename); + rep = xmalloc (1 + strlen (filename) + 1 + strlen (node->nodename) + 1); + sprintf (rep, "(%s)%s", filename, node->nodename); + } + else + rep = node->nodename; + + return rep; +} + + /* Using WINDOW for various defaults, select the node referenced by ENTRY in it. If the node is selected, the window and node are remembered. */ void @@ -1613,7 +1741,7 @@ info_select_reference (window, entry) if (file_system_error) info_error (file_system_error); else - info_error (CANT_FIND_NODE, nodename); + info_error (msg_cant_find_node, nodename); } maybe_free (file_system_error); @@ -1621,10 +1749,7 @@ info_select_reference (window, entry) maybe_free (nodename); if (node) - { - set_remembered_pagetop_and_point (window); - info_set_node_of_window (window, node); - } + info_set_node_of_window (1, window, node); } /* Parse the node specification in LINE using WINDOW to default the filename. @@ -1689,15 +1814,14 @@ info_handle_pointer (label, window) info_win->pagetops[info_win->current] = window->pagetop; info_win->points[info_win->current] = window->point; } - set_remembered_pagetop_and_point (window); - info_set_node_of_window (window, node); + info_set_node_of_window (1, window, node); } else { if (info_recent_file_error) info_error (info_recent_file_error); else - info_error (CANT_FILE_NODE, filename, nodename); + info_error (msg_cant_file_node, filename, nodename); } free (filename); @@ -1705,32 +1829,32 @@ info_handle_pointer (label, window) } else { - info_error (NO_POINTER, label); + info_error (msg_no_pointer, label); } } /* Make WINDOW display the "Next:" node of the node currently being displayed. */ -DECLARE_INFO_COMMAND (info_next_node, _("Select the `Next' node")) +DECLARE_INFO_COMMAND (info_next_node, _("Select the Next node")) { info_next_label_of_node (window->node); - info_handle_pointer (_("Next"), window); + info_handle_pointer ("Next", window); } /* Make WINDOW display the "Prev:" node of the node currently being displayed. */ -DECLARE_INFO_COMMAND (info_prev_node, _("Select the `Prev' node")) +DECLARE_INFO_COMMAND (info_prev_node, _("Select the Prev node")) { info_prev_label_of_node (window->node); - info_handle_pointer (_("Prev"), window); + info_handle_pointer ("Prev", window); } /* Make WINDOW display the "Up:" node of the node currently being displayed. */ -DECLARE_INFO_COMMAND (info_up_node, _("Select the `Up' node")) +DECLARE_INFO_COMMAND (info_up_node, _("Select the Up node")) { info_up_label_of_node (window->node); - info_handle_pointer (_("Up"), window); + info_handle_pointer ("Up", window); } /* Make WINDOW display the last node of this info file. */ @@ -1742,17 +1866,28 @@ DECLARE_INFO_COMMAND (info_last_node, _("Select the last node in this file")) if (fb && fb->tags) { - for (i = 0; fb->tags[i]; i++); - node = info_get_node (fb->filename, fb->tags[i - 1]->nodename); + int last_node_tag_idx = -1; + + /* If no explicit argument, or argument of zero, default to the + last node. */ + if (count == 0 || (count == 1 && !info_explicit_arg)) + count = -1; + for (i = 0; count && fb->tags[i]; i++) + if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */ + { + count--; + last_node_tag_idx = i; + } + if (count > 0) + i = last_node_tag_idx + 1; + if (i > 0) + node = info_get_node (fb->filename, fb->tags[i - 1]->nodename); } if (!node) info_error (_("This window has no additional nodes")); else - { - set_remembered_pagetop_and_point (window); - info_set_node_of_window (window, node); - } + info_set_node_of_window (1, window, node); } /* Make WINDOW display the first node of this info file. */ @@ -1761,16 +1896,31 @@ DECLARE_INFO_COMMAND (info_first_node, _("Select the first node in this file")) FILE_BUFFER *fb = file_buffer_of_window (window); NODE *node = (NODE *)NULL; + /* If no explicit argument, or argument of zero, default to the + first node. */ + if (count == 0) + count = 1; if (fb && fb->tags) - node = info_get_node (fb->filename, fb->tags[0]->nodename); + { + register int i; + int last_node_tag_idx = -1; + + for (i = 0; count && fb->tags[i]; i++) + if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */ + { + count--; + last_node_tag_idx = i; + } + if (count > 0) + i = last_node_tag_idx + 1; + if (i > 0) + node = info_get_node (fb->filename, fb->tags[i - 1]->nodename); + } if (!node) info_error (_("This window has no additional nodes")); else - { - set_remembered_pagetop_and_point (window); - info_set_node_of_window (window, node); - } + info_set_node_of_window (1, window, node); } /* Select the last menu item in WINDOW->node. */ @@ -1790,7 +1940,7 @@ DECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item")) if (!menu) { - info_error (NO_MENU_NODE); + info_error (msg_no_menu_node); return; } @@ -1837,9 +1987,9 @@ info_menu_or_ref_item (window, count, key, builder, ask_p) if (!menu) { if (builder == info_menu_of_node) - info_error (NO_MENU_NODE); + info_error (msg_no_menu_node); else - info_error (NO_XREF_NODE); + info_error (msg_no_xref_node); return; } @@ -1996,12 +2146,11 @@ info_menu_or_ref_item (window, count, key, builder, ask_p) info_error (_("The reference disappeared! (%s)."), line); else { - NODE *orig; - - orig = window->node; + NODE *orig = window->node; info_select_reference (window, entry); - if ((builder == info_xrefs_of_node) && (window->node != orig)) - { + if (builder == info_xrefs_of_node && window->node != orig + && !(window->node->flags & N_FromAnchor)) + { /* Search for this reference in the node. */ long offset; long start; @@ -2066,7 +2215,7 @@ DECLARE_INFO_COMMAND (info_find_menu, _("Move to the start of this node's menu") position = search (INFO_MENU_LABEL, &binding); if (position == -1) - info_error (NO_MENU_NODE); + info_error (msg_no_menu_node); else { window->point = position; @@ -2085,7 +2234,7 @@ DECLARE_INFO_COMMAND (info_visit_menu, menu = info_menu_of_node (window->node); if (!menu) - info_error (NO_MENU_NODE); + info_error (msg_no_menu_node); for (i = 0; (!info_error_was_printed) && (entry = menu[i]); i++) { @@ -2095,7 +2244,7 @@ DECLARE_INFO_COMMAND (info_visit_menu, window_tile_windows (TILE_INTERNALS); if (!new) - info_error (WIN_TOO_SMALL); + info_error (msg_win_too_small); else { active_window = new; @@ -2144,33 +2293,27 @@ DECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it")) { entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); entry->filename = entry->nodename = (char *)NULL; - entry->label = (char *) xmalloc - (4 + strlen (fb->filename) + strlen (fb->tags[i]->nodename)); - sprintf (entry->label, "(%s)%s", - fb->filename, fb->tags[i]->nodename); + if (this_is_the_current_fb) + entry->label = xstrdup (fb->tags[i]->nodename); + else + { + entry->label = (char *) xmalloc + (4 + strlen (fb->filename) + + strlen (fb->tags[i]->nodename)); + sprintf (entry->label, "(%s)%s", + fb->filename, fb->tags[i]->nodename); + } add_pointer_to_array (entry, items_index, items, items_slots, 100, REFERENCE *); - } - - if (this_is_the_current_fb) - { - for (i = 0; fb->tags[i]; i++) - { - entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); - entry->filename = entry->nodename = (char *)NULL; - entry->label = xstrdup (fb->tags[i]->nodename); - add_pointer_to_array (entry, items_index, items, - items_slots, 100, REFERENCE *); - } } } } - line = info_read_maybe_completing (window, _("Goto Node: "), items); + line = info_read_maybe_completing (window, _("Goto node: "), items); info_free_references (items); } #else /* !GOTO_COMPLETES */ - line = info_read_in_echo_area (window, _("Goto Node: ")); + line = info_read_in_echo_area (window, _("Goto node: ")); #endif /* !GOTO_COMPLETES */ /* If the user aborted, quit now. */ @@ -2189,7 +2332,413 @@ DECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it")) if (!info_error_was_printed) window_clear_echo_area (); } + +/* Follow the menu list in MENUS (list of strings terminated by a NULL + entry) from INITIAL_NODE. If can't continue at any point (no menu or + no menu entry for the next item), return the node so far -- that + might be INITIAL_NODE itself. If error, *ERRSTR and *ERRARG[12] will + be set to the error message and argument for message, otherwise they + will be NULL. */ +NODE * +info_follow_menus (initial_node, menus, errstr, errarg1, errarg2) + NODE *initial_node; + char **menus; + char **errstr, **errarg1, **errarg2; +{ + NODE *node = NULL; + *errstr = *errarg1 = *errarg2 = NULL; + + for (; *menus; menus++) + { + static char *first_arg = NULL; + REFERENCE **menu; + REFERENCE *entry; + char *arg = *menus; /* Remember the name of the menu entry we want. */ + + /* A leading space is certainly NOT part of a node name. Most + probably, they typed a space after the separating comma. The + strings in menus[] have their whitespace canonicalized, so + there's at most one space to ignore. */ + if (*arg == ' ') + arg++; + if (!first_arg) + first_arg = arg; + + /* Build and return a list of the menu items in this node. */ + menu = info_menu_of_node (initial_node); + + /* If no menu item in this node, stop here, but let the user + continue to use Info. Perhaps they wanted this node and didn't + realize it. */ + if (!menu) + { + if (arg == first_arg) + { + node = make_manpage_node (first_arg); + if (node) + goto maybe_got_node; + } + *errstr = _("No menu in node `%s'."); + *errarg1 = node_printed_rep (initial_node); + return initial_node; + } + + /* Find the specified menu item. */ + entry = info_get_labeled_reference (arg, menu); + + /* If the item wasn't found, search the list sloppily. Perhaps this + user typed "buffer" when they really meant "Buffers". */ + if (!entry) + { + int i; + int best_guess = -1; + + for (i = 0; (entry = menu[i]); i++) + { + if (strcasecmp (entry->label, arg) == 0) + break; + else + if (strncasecmp (entry->label, arg, strlen (arg)) == 0) + best_guess = i; + } + + if (!entry && best_guess != -1) + entry = menu[best_guess]; + } + + /* If we still failed to find the reference, start Info with the current + node anyway. It is probably a misspelling. */ + if (!entry) + { + if (arg == first_arg) + { + node = make_manpage_node (first_arg); + if (node) + goto maybe_got_node; + } + + info_free_references (menu); + *errstr = _("No menu item `%s' in node `%s'."); + *errarg1 = arg; + *errarg2 = node_printed_rep (initial_node); + return initial_node; + } + + /* We have found the reference that the user specified. If no + filename in this reference, define it. */ + if (!entry->filename) + entry->filename = xstrdup (initial_node->parent ? initial_node->parent + : initial_node->filename); + + /* Try to find this node. */ + node = info_get_node (entry->filename, entry->nodename); + if (!node && arg == first_arg) + { + node = make_manpage_node (first_arg); + if (node) + goto maybe_got_node; + } + + /* Since we cannot find it, try using the label of the entry as a + file, i.e., "(LABEL)Top". */ + if (!node && entry->nodename + && strcmp (entry->label, entry->nodename) == 0) + node = info_get_node (entry->label, "Top"); + + maybe_got_node: + if (!node) + { + *errstr = _("Unable to find node referenced by `%s' in `%s'."); + *errarg1 = xstrdup (entry->label); + *errarg2 = node_printed_rep (initial_node); + info_free_references (menu); + return initial_node; + } + + info_free_references (menu); + + /* Success. Go round the loop again. */ + free (initial_node); + initial_node = node; + } + + return initial_node; +} + +/* Split STR into individual node names by writing null bytes in wherever + there are commas and constructing a list of the resulting pointers. + (We can do this since STR has had canonicalize_whitespace called on it.) + Return array terminated with NULL. */ + +static char ** +split_list_of_nodenames (str) + char *str; +{ + unsigned len = 2; + char **nodes = xmalloc (len * sizeof (char *)); + + nodes[len - 2] = str; + + while (*str++) + { + if (*str == ',') + { + *str++ = 0; /* get past the null byte */ + len++; + nodes = xrealloc (nodes, len * sizeof (char *)); + nodes[len - 2] = str; + } + } + + nodes[len - 1] = NULL; + + return nodes; +} + + +/* Read a line of input which is a sequence of menus (starting from + dir), and follow them. */ +DECLARE_INFO_COMMAND (info_menu_sequence, + _("Read a list of menus starting from dir and follow them")) +{ + char *line = info_read_in_echo_area (window, _("Follow menus: ")); + + /* If the user aborted, quit now. */ + if (!line) + { + info_abort_key (window, 0, 0); + return; + } + + canonicalize_whitespace (line); + + if (*line) + { + char *errstr, *errarg1, *errarg2; + NODE *dir_node = info_get_node (NULL, NULL); + char **nodes = split_list_of_nodenames (line); + NODE *node; + + /* If DIR_NODE is NULL, they might be reading a file directly, + like in "info -d . -f ./foo". Try using "Top" instead. */ + if (!dir_node) + { + char *file_name = window->node->parent; + + if (!file_name) + file_name = window->node->filename; + dir_node = info_get_node (file_name, NULL); + } + + /* If we still cannot find the starting point, give up. + We cannot allow a NULL pointer inside info_follow_menus. */ + if (!dir_node) + info_error (msg_cant_find_node, "Top"); + else + node + = info_follow_menus (dir_node, nodes, &errstr, &errarg1, &errarg2); + + free (nodes); + if (!errstr) + info_set_node_of_window (1, window, node); + else + info_error (errstr, errarg1, errarg2); + } + + free (line); + if (!info_error_was_printed) + window_clear_echo_area (); +} + +/* Search the menu MENU for a (possibly mis-spelled) entry ARG. + Return the menu entry, or the best guess for what they meant by ARG, + or NULL if there's nothing in this menu seems to fit the bill. + If EXACT is non-zero, allow only exact matches. */ +static REFERENCE * +entry_in_menu (arg, menu, exact) + char *arg; + REFERENCE **menu; + int exact; +{ + REFERENCE *entry; + + /* First, try to find the specified menu item verbatim. */ + entry = info_get_labeled_reference (arg, menu); + + /* If the item wasn't found, search the list sloppily. Perhaps we + have "Option Summary", but ARG is "option". */ + if (!entry && !exact) + { + int i; + int best_guess = -1; + + for (i = 0; (entry = menu[i]); i++) + { + if (strcasecmp (entry->label, arg) == 0) + break; + else + if (strncasecmp (entry->label, arg, strlen (arg)) == 0) + best_guess = i; + } + + if (!entry && best_guess != -1) + entry = menu[best_guess]; + } + + return entry; +} + +/* Find the node that is the best candidate to list the PROGRAM's + invocation info and its command-line options, by looking for menu + items and chains of menu items with characteristic names. */ +void +info_intuit_options_node (window, initial_node, program) + WINDOW *window; + NODE *initial_node; + char *program; +{ + /* The list of node names typical for GNU manuals where the program + usage and specifically the command-line arguments are described. + This is pure heuristics. I gathered these node names by looking + at all the Info files I could put my hands on. If you are + looking for evidence to complain to the GNU project about + non-uniform style of documentation, here you have your case! */ + static const char *invocation_nodes[] = { + "%s invocation", + "Invoking %s", + "Preliminaries", /* m4 has Invoking under Preliminaries! */ + "Invocation", + "Command Arguments",/* Emacs */ + "Invoking `%s'", + "%s options", + "Options", + "Option ", /* e.g. "Option Summary" */ + "Invoking", + "All options", /* tar, paxutils */ + "Arguments", + "%s cmdline", /* ar */ + "%s", /* last resort */ + (const char *)0 + }; + NODE *node = NULL; + REFERENCE **menu; + const char **try_node; + + /* We keep looking deeper and deeper in the menu structure until + there are no more menus or no menu items from the above list. + Some manuals have the invocation node sitting 3 or 4 levels deep + in the menu hierarchy... */ + for (node = initial_node; node; initial_node = node) + { + REFERENCE *entry; + + /* Build and return a list of the menu items in this node. */ + menu = info_menu_of_node (initial_node); + + /* If no menu item in this node, stop here. Perhaps this node + is the one they need. */ + if (!menu) + break; + + /* Look for node names typical for usage nodes in this menu. */ + for (try_node = invocation_nodes; *try_node; try_node++) + { + char nodename[200]; + + sprintf (nodename, *try_node, program); + /* The last resort "%s" is dangerous, so we restrict it + to exact matches here. */ + entry = entry_in_menu (nodename, menu, + strcmp (*try_node, "%s") == 0); + if (entry) + break; + } + + if (!entry) + break; + + if (!entry->filename) + entry->filename = xstrdup (initial_node->parent ? initial_node->parent + : initial_node->filename); + /* Try to find this node. */ + node = info_get_node (entry->filename, entry->nodename); + info_free_references (menu); + if (!node) + break; + } + + /* We've got our best shot at the invocation node. Now select it. */ + if (initial_node) + info_set_node_of_window (1, window, initial_node); + if (!info_error_was_printed) + window_clear_echo_area (); +} + +/* Given a name of an Info file, find the name of the package it + describes by removing the leading directories and extensions. */ +char * +program_name_from_file_name (file_name) + char *file_name; +{ + int i; + char *program_name = xstrdup (filename_non_directory (file_name)); + + for (i = strlen (program_name) - 1; i > 0; i--) + if (program_name[i] == '.' + && (FILENAME_CMPN (program_name + i, ".info", 5) == 0 + || FILENAME_CMPN (program_name + i, ".inf", 4) == 0 +#ifdef __MSDOS__ + || FILENAME_CMPN (program_name + i, ".i", 2) == 0 +#endif + || isdigit (program_name[i + 1]))) /* a man page foo.1 */ + { + program_name[i] = 0; + break; + } + return program_name; +} + +DECLARE_INFO_COMMAND (info_goto_invocation_node, + _("Find the node describing program invocation")) +{ + char *invocation_prompt = _("Find Invocation node of [%s]: "); + char *program_name, *line; + char *default_program_name, *prompt, *file_name; + NODE *top_node; + + /* Intuit the name of the program they are likely to want. + We use the file name of the current Info file as a hint. */ + file_name = window->node->parent ? window->node->parent + : window->node->filename; + default_program_name = program_name_from_file_name (file_name); + + prompt = (char *)xmalloc (strlen (default_program_name) + + strlen (invocation_prompt)); + sprintf (prompt, invocation_prompt, default_program_name); + line = info_read_in_echo_area (window, prompt); + free (prompt); + if (!line) + { + info_abort_key (); + return; + } + if (*line) + program_name = line; + else + program_name = default_program_name; + + /* In interactive usage they'd probably expect us to begin looking + from the Top node. */ + top_node = info_get_node (file_name, NULL); + if (!top_node) + info_error (msg_cant_find_node, "Top"); + + info_intuit_options_node (window, top_node, program_name); + free (line); + free (default_program_name); +} + #if defined (HANDLE_MAN_PAGES) DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it")) { @@ -2227,7 +2776,7 @@ DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it")) /* Move to the "Top" node in this file. */ DECLARE_INFO_COMMAND (info_top_node, _("Select the node `Top' in this file")) { - info_parse_and_select (_("Top"), window); + info_parse_and_select ("Top", window); } /* Move to the node "(dir)Top". */ @@ -2288,7 +2837,7 @@ kill_node (window, nodename) int iw, i; INFO_WINDOW *info_win; NODE *temp; - + /* If there is no nodename to kill, quit now. */ if (!nodename) { @@ -2298,7 +2847,8 @@ kill_node (window, nodename) /* If there is a nodename, find it in our window list. */ for (iw = 0; (info_win = info_windows[iw]); iw++) - if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0) + if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0 + && info_win->window == window) break; if (!info_win) @@ -2321,7 +2871,7 @@ kill_node (window, nodename) /* INFO_WIN contains the node that the user wants to stop viewing. Delete this node from the list of nodes previously shown in this window. */ for (i = info_win->current; i < info_win->nodes_index; i++) - info_win->nodes[i] = info_win->nodes[i++]; + info_win->nodes[i] = info_win->nodes[i + 1]; /* There is one less node in this window's history list. */ info_win->nodes_index--; @@ -2359,7 +2909,7 @@ kill_node (window, nodename) /* Copy this node. */ { NODE *copy = xmalloc (sizeof (NODE)); - + temp = stealer->nodes[which]; point = stealer->points[which]; pagetop = stealer->pagetops[which]; @@ -2370,6 +2920,7 @@ kill_node (window, nodename) copy->contents = temp->contents; copy->nodelen = temp->nodelen; copy->flags = temp->flags; + copy->display_pos = temp->display_pos; temp = copy; } @@ -2382,6 +2933,7 @@ kill_node (window, nodename) else { temp = info_win->nodes[info_win->current]; + temp->display_pos = info_win->points[info_win->current]; window_set_node_of_window (info_win->window, temp); } @@ -2433,13 +2985,11 @@ DECLARE_INFO_COMMAND (info_view_file, _("Read the name of a file and select it") if (info_recent_file_error) info_error (info_recent_file_error); else - info_error (_("Cannot find \"%s\"."), line); + info_error (_("Cannot find `%s'."), line); } else - { - set_remembered_pagetop_and_point (active_window); - info_set_node_of_window (window, node); - } + info_set_node_of_window (1, window, node); + free (line); } @@ -2480,7 +3030,7 @@ dump_nodes_to_file (filename, nodenames, output_filename, dump_subnodes) if (!output_stream) { - info_error (_("Could not create output file \"%s\"."), output_filename); + info_error (_("Could not create output file `%s'."), output_filename); return; } @@ -2529,10 +3079,10 @@ dump_node_to_stream (filename, nodename, stream, dump_subnodes) else { if (filename && *nodename != '(') - info_error - (CANT_FILE_NODE, filename_non_directory (filename), nodename); + info_error (msg_cant_file_node, filename_non_directory (filename), + nodename); else - info_error (CANT_FIND_NODE, nodename); + info_error (msg_cant_find_node, nodename); } return; } @@ -2549,11 +3099,7 @@ dump_node_to_stream (filename, nodename, stream, dump_subnodes) #if defined (VERBOSE_NODE_DUMPING) /* Maybe we should print some information about the node being output. */ - if (node->filename) - info_error (_("Writing node \"(%s)%s\"..."), - filename_non_directory (node->filename), node->nodename); - else - info_error (_("Writing node \"%s\"..."), node->nodename); + info_error (_("Writing node %s..."), node_printed_rep (node)); #endif /* VERBOSE_NODE_DUMPING */ write_node_to_stream (node, stream); @@ -2605,7 +3151,7 @@ dump_node_to_file (node, filename, dump_subnodes) if (!output_stream) { - info_error (_("Could not create output file \"%s\"."), filename); + info_error (_("Could not create output file `%s'."), filename); return; } @@ -2643,29 +3189,44 @@ print_node (node) { FILE *printer_pipe; char *print_command = getenv ("INFO_PRINT_COMMAND"); + int piping = 0; if (!print_command || !*print_command) print_command = DEFAULT_INFO_PRINT_COMMAND; - printer_pipe = popen (print_command, "w"); + /* Note that on MS-DOS/MS-Windows, this MUST open the pipe in the + (default) text mode, since the printer drivers there need to see + DOS-style CRLF pairs at the end of each line. + + FIXME: if we are to support Mac-style text files, we might need + to convert the text here. */ + + /* INFO_PRINT_COMMAND which says ">file" means write to that file. + Presumably, the name of the file is the local printer device. */ + if (*print_command == '>') + printer_pipe = fopen (++print_command, "w"); + else + { + printer_pipe = popen (print_command, "w"); + piping = 1; + } if (!printer_pipe) { - info_error (_("Cannot open pipe to \"%s\"."), print_command); + info_error (_("Cannot open pipe to `%s'."), print_command); return; } #if defined (VERBOSE_NODE_DUMPING) /* Maybe we should print some information about the node being output. */ - if (node->filename) - info_error (_("Printing node \"(%s)%s\"..."), - filename_non_directory (node->filename), node->nodename); - else - info_error (_("Printing node \"%s\"..."), node->nodename); + info_error (_("Printing node %s..."), node_printed_rep (node)); #endif /* VERBOSE_NODE_DUMPING */ write_node_to_stream (node, printer_pipe); - pclose (printer_pipe); + if (piping) + pclose (printer_pipe); + else + fclose (printer_pipe); #if defined (VERBOSE_NODE_DUMPING) info_error (_("Done.")); @@ -2693,12 +3254,16 @@ write_node_to_stream (node, stream) int gc_compressed_files = 0; static void info_gc_file_buffers (); +static void info_search_1 (); static char *search_string = (char *)NULL; static int search_string_index = 0; static int search_string_size = 0; static int isearch_is_active = 0; +static int last_search_direction = 0; +static int last_search_case_sensitive = 0; + /* Return the file buffer which belongs to WINDOW's node. */ FILE_BUFFER * file_buffer_of_window (window) @@ -2724,12 +3289,12 @@ file_buffer_of_window (window) DIR says which direction to search in. If it is positive, search forward, else backwards. */ long -info_search_in_node (string, node, start, window, dir) +info_search_in_node (string, node, start, window, dir, case_sensitive) char *string; NODE *node; long start; WINDOW *window; - int dir; + int dir, case_sensitive; { SEARCH_BINDING binding; long offset; @@ -2737,7 +3302,9 @@ info_search_in_node (string, node, start, window, dir) binding.buffer = node->contents; binding.start = start; binding.end = node->nodelen; - binding.flags = S_FoldCase; + binding.flags = 0; + if (!case_sensitive) + binding.flags |= S_FoldCase; if (dir < 0) { @@ -2786,7 +3353,7 @@ info_target_search_node (node, string, start) while (i) { target[i] = '\0'; - offset = info_search_in_node (target, node, start, (WINDOW *)NULL, 1); + offset = info_search_in_node (target, node, start, (WINDOW *)NULL, 1, 0); if (offset != -1) break; @@ -2803,12 +3370,11 @@ info_target_search_node (node, string, start) associated with WINDOW's node, and search through each node in that file. If the search fails, return non-zero, else zero. Side-effect window leaving the node and point where the string was found current. */ -static char *last_searched_for_string = (char *)NULL; static int -info_search_internal (string, window, dir) +info_search_internal (string, window, dir, case_sensitive) char *string; WINDOW *window; - int dir; + int dir, case_sensitive; { register int i; FILE_BUFFER *file_buffer; @@ -2818,21 +3384,14 @@ info_search_internal (string, window, dir) file_buffer = file_buffer_of_window (window); initial_nodename = window->node->nodename; - if ((info_last_executed_command == info_search) && - (last_searched_for_string) && - (strcmp (last_searched_for_string, string) == 0)) - { - ret = info_search_in_node - (string, window->node, window->point + dir, window, dir); - } - else - { - ret = info_search_in_node - (string, window->node, window->point, window, dir); - } - - maybe_free (last_searched_for_string); - last_searched_for_string = xstrdup (string); + /* This used to begin from window->point, unless this was a repeated + search command. But invoking search with an argument loses with + that logic, since info_last_executed_command is then set to + info_add_digit_to_numeric_arg. I think there's no sense in + ``finding'' a string that is already under the cursor, anyway. */ + ret = info_search_in_node + (string, window->node, window->point + dir, window, dir, + case_sensitive); if (ret != -1) { @@ -2881,19 +3440,28 @@ info_search_internal (string, window, dir) /* Allow C-g to quit the search, failing it if pressed. */ return_if_control_g (-1); - current_tag += dir; + /* Find the next tag that isn't an anchor. */ + for (i = current_tag + dir; i != current_tag; i += dir) + { + if (i < 0) + i = number_of_tags - 1; + else if (i == number_of_tags) + i = 0; - if (current_tag < 0) - current_tag = number_of_tags - 1; - else if (current_tag == number_of_tags) - current_tag = 0; + tag = file_buffer->tags[i]; + if (tag->nodelen != 0) + break; + } - tag = file_buffer->tags[current_tag]; + /* If we got past out starting point, bail out. */ + if (i == current_tag) + return (-1); + current_tag = i; if (!echo_area_is_active && (last_subfile != tag->filename)) { window_message_in_echo_area - (_("Searching subfile \"%s\"..."), + (_("Searching subfile %s ..."), filename_non_directory (tag->filename)); last_subfile = tag->filename; @@ -2909,7 +3477,7 @@ info_search_internal (string, window, dir) if (info_recent_file_error) info_error (info_recent_file_error); else - info_error (CANT_FILE_NODE, + info_error (msg_cant_file_node, filename_non_directory (file_buffer->filename), tag->nodename); } @@ -2920,7 +3488,8 @@ info_search_internal (string, window, dir) start = tag->nodelen; ret = - info_search_in_node (string, node, start, window, dir); + info_search_in_node (string, node, start, window, dir, + case_sensitive); /* Did we find the string in this node? */ if (ret != -1) @@ -2943,16 +3512,52 @@ info_search_internal (string, window, dir) return (-1); } +DECLARE_INFO_COMMAND (info_search_case_sensitively, + _("Read a string and search for it case-sensitively")) +{ + last_search_direction = count > 0 ? 1 : -1; + last_search_case_sensitive = 1; + info_search_1 (window, count, key, 1, 1); +} + DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it")) +{ + last_search_direction = count > 0 ? 1 : -1; + last_search_case_sensitive = 0; + info_search_1 (window, count, key, 0, 1); +} + +DECLARE_INFO_COMMAND (info_search_backward, + _("Read a string and search backward for it")) +{ + last_search_direction = count > 0 ? -1 : 1; + last_search_case_sensitive = 0; + info_search_1 (window, -count, key, 0, 1); +} + +static void +info_search_1 (window, count, key, case_sensitive, ask_for_string) + WINDOW *window; + int count; + unsigned char key; + int case_sensitive; + int ask_for_string; { char *line, *prompt; int result, old_pagetop; int direction; if (count < 0) - direction = -1; + { + direction = -1; + count = -count; + } else - direction = 1; + { + direction = 1; + if (count == 0) + count = 1; /* for backward compatibility */ + } /* Read a string from the user, defaulting the search to SEARCH_STRING. */ if (!search_string) @@ -2961,34 +3566,50 @@ DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it")) search_string[0] = '\0'; } - prompt = (char *)xmalloc (50 + strlen (search_string)); - - sprintf (prompt, _("%s for string [%s]: "), - direction < 0 ? _("Search backward") : _("Search"), - search_string); - - line = info_read_in_echo_area (window, prompt); - free (prompt); - - if (!line) + if (ask_for_string) { - info_abort_key (); - return; + prompt = (char *)xmalloc (50 + strlen (search_string)); + + sprintf (prompt, _("%s%sfor string [%s]: "), + direction < 0 ? _("Search backward") : _("Search"), + case_sensitive ? _(" case-sensitively ") : _(" "), + search_string); + + line = info_read_in_echo_area (window, prompt); + free (prompt); + + if (!line) + { + info_abort_key (); + return; + } + + if (*line) + { + if (strlen (line) + 1 > search_string_size) + search_string = (char *) xrealloc + (search_string, (search_string_size += 50 + strlen (line))); + + strcpy (search_string, line); + search_string_index = strlen (line); + free (line); + } } - if (*line) - { - if (strlen (line) + 1 > search_string_size) - search_string = (char *) - xrealloc (search_string, (search_string_size += 50 + strlen (line))); - - strcpy (search_string, line); - search_string_index = strlen (line); - free (line); - } + /* If the search string includes upper-case letters, make the search + case-sensitive. */ + if (case_sensitive == 0) + for (line = search_string; *line; line++) + if (isupper (*line)) + { + case_sensitive = 1; + break; + } old_pagetop = active_window->pagetop; - result = info_search_internal (search_string, active_window, direction); + for (result = 0; result == 0 && count--; ) + result = info_search_internal (search_string, + active_window, direction, case_sensitive); if (result != 0 && !info_error_was_printed) info_error (_("Search failed.")); @@ -3008,6 +3629,26 @@ DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it")) info_gc_file_buffers (); } +DECLARE_INFO_COMMAND (info_search_next, + _("Repeat last search in the same direction")) +{ + if (!last_search_direction) + info_error (_("No previous search string")); + else + info_search_1 (window, last_search_direction * count, + key, last_search_case_sensitive, 0); +} + +DECLARE_INFO_COMMAND (info_search_previous, + _("Repeat last search in the reverse direction")) +{ + if (!last_search_direction) + info_error (_("No previous search string")); + else + info_search_1 (window, -last_search_direction * count, + key, last_search_case_sensitive, 0); +} + /* **************************************************************** */ /* */ /* Incremental Searching */ @@ -3162,6 +3803,8 @@ incremental_search (window, count, ignore) unsigned char key; int last_search_result, search_result, dir; SEARCH_STATE mystate, orig_state; + char *p; + int case_sensitive = 0; if (count < 0) dir = -1; @@ -3228,7 +3871,7 @@ incremental_search (window, count, ignore) if (quoted) goto insert_and_search; - if (!Meta_p (key) || (ISO_Latin_p && key < 160)) + if (!Meta_p (key) || key > 32) { func = window->keymap[key].function; @@ -3320,7 +3963,16 @@ incremental_search (window, count, ignore) last_isearch_accepted = xstrdup (isearch_string); } - if (key != isearch_terminate_search_key) + /* If the key is the isearch_terminate_search_key, but some buffered + input is pending, it is almost invariably because the ESC key is + actually the beginning of an escape sequence, like in case they + pressed an arrow key. So don't gobble the ESC key, push it back + into pending input. */ + /* FIXME: this seems like a kludge! We need a more reliable + mechanism to know when ESC is a separate key and when it is + part of an escape sequence. */ + if (key != isearch_terminate_search_key || + info_any_buffered_input_p ()) info_set_pending_input (key); if (func == info_abort_key) @@ -3343,17 +3995,29 @@ incremental_search (window, count, ignore) search_now: show_isearch_prompt (dir, isearch_string, search_result); + /* If the search string includes upper-case letters, make the + search case-sensitive. */ + for (p = isearch_string; *p; p++) + if (isupper (*p)) + { + case_sensitive = 1; + break; + } + + if (search_result == 0) { /* Check to see if the current search string is right here. If we are looking at it, then don't bother calling the search function. */ if (((dir < 0) && - (strncasecmp (window->node->contents + window->point, + ((case_sensitive ? strncmp : strncasecmp) + (window->node->contents + window->point, isearch_string, isearch_string_index) == 0)) || ((dir > 0) && ((window->point - isearch_string_index) >= 0) && - (strncasecmp (window->node->contents + + ((case_sensitive ? strncmp : strncasecmp) + (window->node->contents + (window->point - (isearch_string_index - 1)), isearch_string, isearch_string_index) == 0))) { @@ -3361,7 +4025,8 @@ incremental_search (window, count, ignore) window->point++; } else - search_result = info_search_internal (isearch_string, window, dir); + search_result = info_search_internal (isearch_string, + window, dir, case_sensitive); } /* If this search failed, and we didn't already have a failed search, @@ -3437,8 +4102,8 @@ info_gc_file_buffers () { for (i = 0; iw->nodes && iw->nodes[i]; i++) { - if ((strcmp (fb->fullpath, iw->nodes[i]->filename) == 0) || - (strcmp (fb->filename, iw->nodes[i]->filename) == 0)) + if ((FILENAME_CMP (fb->fullpath, iw->nodes[i]->filename) == 0) || + (FILENAME_CMP (fb->filename, iw->nodes[i]->filename) == 0)) { fb_referenced_p = 1; break; @@ -3483,7 +4148,7 @@ info_move_to_xref (window, count, key, dir) found is moved to. */ firstmenu = info_search_in_node - (INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir); + (INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir, 0); /* FIRSTMENU may point directly to the line defining the menu. Skip that and go directly to the first item. */ @@ -3494,11 +4159,11 @@ info_move_to_xref (window, count, key, dir) if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0) firstmenu = info_search_in_node - (INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, (WINDOW *)NULL, dir); + (INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, (WINDOW *)NULL, dir, 0); } firstxref = - info_search_in_node (INFO_XREF_LABEL, node, start, (WINDOW *)NULL, dir); + info_search_in_node (INFO_XREF_LABEL, node, start, (WINDOW *)NULL, dir, 0); #if defined (HANDLE_MAN_PAGES) if ((firstxref == -1) && (node->flags & N_IsManPage)) @@ -3509,7 +4174,7 @@ info_move_to_xref (window, count, key, dir) if (firstmenu == -1 && firstxref == -1) { - info_error (_("No cross references in this node.")); + info_error (msg_no_xref_node); return; } @@ -3517,10 +4182,10 @@ info_move_to_xref (window, count, key, dir) Try hard to find the next available one. */ nextmenu = info_search_in_node - (INFO_MENU_ENTRY_LABEL, node, window->point + dir, (WINDOW *)NULL, dir); + (INFO_MENU_ENTRY_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0); nextxref = info_search_in_node - (INFO_XREF_LABEL, node, window->point + dir, (WINDOW *)NULL, dir); + (INFO_XREF_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0); #if defined (HANDLE_MAN_PAGES) if ((nextxref == -1) && (node->flags & N_IsManPage) && (firstxref != -1)) @@ -3534,7 +4199,7 @@ info_move_to_xref (window, count, key, dir) if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0) nextmenu = info_search_in_node - (INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, (WINDOW *)NULL, dir); + (INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, (WINDOW *)NULL, dir, 0); } /* If there is both a next menu entry, and a next xref entry, choose the @@ -3633,7 +4298,7 @@ DECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation")) /* Move the cursor to the desired line of the window. */ DECLARE_INFO_COMMAND (info_move_to_window_line, - _("Move to the cursor to a specific line of the window")) + _("Move the cursor to a specific line of the window")) { int line; @@ -3728,10 +4393,7 @@ dispatch_error (keyseq) info_error (_("Unknown command (%s)."), rep); else { - char *temp; - - temp = (char *)xmalloc (1 + strlen (rep) + strlen (_("\"\" is invalid"))); - + char *temp = xmalloc (1 + strlen (rep) + strlen (_("\"\" is invalid"))); sprintf (temp, _("\"%s\" is invalid"), rep); terminal_ring_bell (); inform_in_echo_area (temp); @@ -4172,6 +4834,31 @@ info_gather_typeahead () if (chars_avail == -1) chars_avail = 0; } +# else /* !O_NDELAY */ +# ifdef __DJGPP__ + { + extern long pc_term_chars_avail (void); + + if (isatty (tty)) + chars_avail = pc_term_chars_avail (); + else + { + /* We could be more accurate by calling ltell, but we have no idea + whether tty is buffered by stdio functions, and if so, how many + characters are already waiting in the buffer. So we punt. */ + struct stat st; + + if (fstat (tty, &st) < 0) + chars_avail = 1; + else + chars_avail = st.st_size; + } + if (chars_avail > space_avail) + chars_avail = space_avail; + if (chars_avail) + chars_avail = read (tty, &input[0], chars_avail); + } +# endif/* __DJGPP__ */ # endif /* O_NDELAY */ #endif /* !FIONREAD */ @@ -4229,6 +4916,7 @@ info_get_input_char () { fclose (info_input_stream); info_input_stream = stdin; + tty = fileno (info_input_stream); display_inhibited = 0; display_update_display (windows); display_cursor_at_point (active_window); @@ -4240,7 +4928,7 @@ info_get_input_char () { terminal_unprep_terminal (); close_dribble_file (); - exit (0); + xexit (0); } } } diff --git a/contrib/texinfo/info/terminal.c b/contrib/texinfo/info/terminal.c index f0e43bebfae7..9223d9532533 100644 --- a/contrib/texinfo/info/terminal.c +++ b/contrib/texinfo/info/terminal.c @@ -1,7 +1,7 @@ /* terminal.c -- How to handle the physical terminal for Info. - $Id: terminal.c,v 1.9 1998/02/22 00:05:15 karl Exp $ + $Id: terminal.c,v 1.19 1999/09/20 12:28:54 karl Exp $ - Copyright (C) 1988, 89, 90, 91, 92, 93, 96, 97, 98 + Copyright (C) 1988, 89, 90, 91, 92, 93, 96, 97, 98, 99 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify @@ -26,6 +26,7 @@ #include #include +#include /* TIOCGWINSZ on LynxOS, at least */ /* The Unix termcap interface code. */ #ifdef HAVE_NCURSES_TERMCAP_H @@ -76,9 +77,10 @@ VFunction *terminal_scroll_terminal_hook = (VFunction *)NULL; /* **************************************************************** */ /* A buffer which holds onto the current terminal description, and a pointer - used to float within it. */ -static char *term_buffer = (char *)NULL; -static char *term_string_buffer = (char *)NULL; + used to float within it. And the name of the terminal. */ +static char *term_buffer = NULL; +static char *term_string_buffer = NULL; +static char *term_name; /* Some strings to control terminal actions. These are output by tputs (). */ static char *term_goto, *term_clreol, *term_cr, *term_clrpag; @@ -146,12 +148,13 @@ terminal_begin_using_terminal () #endif send_to_terminal (term_begin_use); - /* Without this fflush and sleep, running info in a shelltool or - cmdtool (TERM=sun-cmd) with scrollbars loses -- the scrollbars are - not restored properly. - From: strube@physik3.gwdg.de (Hans Werner Strube). */ fflush (stdout); - sleep (1); + if (STREQ (term_name, "sun-cmd")) + /* Without this fflush and sleep, running info in a shelltool or + cmdtool (TERM=sun-cmd) with scrollbars loses -- the scrollbars are + not restored properly. + From: strube@physik3.gwdg.de (Hans Werner Strube). */ + sleep (1); #ifdef SIGWINCH signal (SIGWINCH, sigsave); @@ -177,7 +180,9 @@ terminal_end_using_terminal () send_to_terminal (term_end_use); fflush (stdout); - sleep (1); + if (STREQ (term_name, "sun-cmd")) + /* See comments at other sleep. */ + sleep (1); #ifdef SIGWINCH signal (SIGWINCH, sigsave); @@ -516,7 +521,7 @@ void terminal_initialize_terminal (terminal_name) char *terminal_name; { - char *term, *buffer; + char *buffer; terminal_is_dumb_p = 0; @@ -526,37 +531,46 @@ terminal_initialize_terminal (terminal_name) return; } - term = terminal_name ? terminal_name : getenv ("TERM"); + term_name = terminal_name ? terminal_name : getenv ("TERM"); + if (!term_name) + term_name = "dumb"; if (!term_string_buffer) - term_string_buffer = (char *)xmalloc (2048); + term_string_buffer = xmalloc (2048); if (!term_buffer) - term_buffer = (char *)xmalloc (2048); + term_buffer = xmalloc (2048); buffer = term_string_buffer; - term_clrpag = term_cr = term_clreol = (char *)NULL; + term_clrpag = term_cr = term_clreol = NULL; - if (!term) - term = "dumb"; - - if (tgetent (term_buffer, term) <= 0) + /* HP-UX 11.x returns 0 for OK --jeff.hull@state.co.us. */ + if (tgetent (term_buffer, term_name) < 0) { terminal_is_dumb_p = 1; screenwidth = 80; screenheight = 24; term_cr = "\r"; - term_up = term_dn = audible_bell = visible_bell = (char *)NULL; - term_ku = term_kd = term_kl = term_kr = (char *)NULL; - term_kP = term_kN = (char *)NULL; + term_up = term_dn = audible_bell = visible_bell = NULL; + term_ku = term_kd = term_kl = term_kr = NULL; + term_kP = term_kN = NULL; return; } BC = tgetstr ("pc", &buffer); PC = BC ? *BC : 0; -#if defined (TIOCGETP) +#if defined (HAVE_TERMIOS_H) + { + struct termios ti; + if (tcgetattr (fileno(stdout), &ti) != -1) + ospeed = cfgetospeed (&ti); + else + ospeed = B9600; + } +#else +# if defined (TIOCGETP) { struct sgttyb sg; @@ -565,16 +579,17 @@ terminal_initialize_terminal (terminal_name) else ospeed = B9600; } -#else +# else ospeed = B9600; -#endif /* !TIOCGETP */ +# endif /* !TIOCGETP */ +#endif term_cr = tgetstr ("cr", &buffer); term_clreol = tgetstr ("ce", &buffer); term_clrpag = tgetstr ("cl", &buffer); term_goto = tgetstr ("cm", &buffer); - /* Find out about this terminals scrolling capability. */ + /* Find out about this terminal's scrolling capability. */ term_AL = tgetstr ("AL", &buffer); term_DL = tgetstr ("DL", &buffer); term_al = tgetstr ("al", &buffer); @@ -633,23 +648,7 @@ terminal_initialize_terminal (terminal_name) terminal_is_dumb_p = 1; } -/* **************************************************************** */ -/* */ -/* How to Read Characters From the Terminal */ -/* */ -/* **************************************************************** */ - -#if defined (TIOCGETC) -/* A buffer containing the terminal interrupt characters upon entry - to Info. */ -struct tchars original_tchars; -#endif - -#if defined (TIOCGLTC) -/* A buffer containing the local terminal mode characters upon entry - to Info. */ -struct ltchars original_ltchars; -#endif +/* How to read characters from the terminal. */ #if defined (HAVE_TERMIOS_H) struct termios original_termios, ttybuff; @@ -662,6 +661,25 @@ struct termio original_termio, ttybuff; int original_tty_flags = 0; int original_lmode; struct sgttyb ttybuff; + +# if defined(TIOCGETC) && defined(M_XENIX) +/* SCO 3.2v5.0.2 defines but does not support TIOCGETC. Gak. Maybe + better fix would be to use Posix termios in preference. --gildea, + 1jul99. */ +# undef TIOCGETC +# endif + +# if defined (TIOCGETC) +/* A buffer containing the terminal interrupt characters upon entry + to Info. */ +struct tchars original_tchars; +# endif + +# if defined (TIOCGLTC) +/* A buffer containing the local terminal mode characters upon entry + to Info. */ +struct ltchars original_ltchars; +# endif # endif /* !HAVE_TERMIO_H */ #endif /* !HAVE_TERMIOS_H */ @@ -835,3 +853,6 @@ terminal_unprep_terminal () terminal_end_using_terminal (); } +#ifdef __MSDOS__ +# include "pcterm.c" +#endif diff --git a/contrib/texinfo/makeinfo/makeinfo.c b/contrib/texinfo/makeinfo/makeinfo.c index 241a068aa737..5f042bc6b146 100644 --- a/contrib/texinfo/makeinfo/makeinfo.c +++ b/contrib/texinfo/makeinfo/makeinfo.c @@ -1,7 +1,7 @@ -/* Makeinfo -- convert Texinfo source files into Info files. - $Id: makeinfo.c,v 1.60 1998/02/25 20:36:22 karl Exp $ +/* makeinfo -- convert Texinfo source into other formats. + $Id: makeinfo.c,v 1.171 1999/09/19 15:24:44 karl Exp $ - Copyright (C) 1987, 92, 93, 94, 95, 96, 97, 98 + Copyright (C) 1987, 92, 93, 94, 95, 96, 97, 98, 99 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify @@ -20,35 +20,20 @@ Makeinfo was authored by Brian Fox (bfox@ai.mit.edu). */ -/* Indent #pragma so that older Cpp's don't try to parse it. */ -#ifdef _AIX - #pragma alloca -#endif /* _AIX */ - -int major_version = 1; -int minor_version = 68; - #include "system.h" #include "getopt.h" -#ifdef TM_IN_SYS_TIME -#include -#else -#include -#endif /* !TM_IN_SYS_TIME */ - -#ifdef __GNUC__ -# undef alloca -# define alloca __builtin_alloca -#else -# ifdef HAVE_ALLOCA_H -# include -# else -# ifndef _AIX -char *alloca (); -# endif -# endif -#endif +#define COMPILING_MAKEINFO +#include "makeinfo.h" +#include "cmds.h" +#include "files.h" +#include "footnote.h" +#include "html.h" +#include "index.h" +#include "insertion.h" +#include "macro.h" +#include "node.h" +#include "toc.h" /* We'd like to take advantage of _doprnt if it's around, a la error.c, but then we'd have no VA_SPRINTF. */ @@ -69,6 +54,15 @@ char *alloca (); # define va_end(args) #endif +/* DJGPP supports /dev/null, which is okay for Unix aficionados, + shell scripts and Makefiles, but interactive DOS die-hards + would probably want to have NUL as well. */ +#ifdef __DJGPP__ +# define ALSO_NULL_DEVICE "NUL" +#else +# define ALSO_NULL_DEVICE "" +#endif + /* You can change some of the behavior of Makeinfo by changing the following defines: */ @@ -78,11 +72,6 @@ char *alloca (); no starting indentation. */ /* #define INDENT_PARAGRAPHS_IN_TABLE */ -/* Define DEFAULT_INDENTATION_INCREMENT as an integer which is the amount - that @example should increase indentation by. This incremement is used - for all insertions which indent the enclosed text. */ -#define DEFAULT_INDENTATION_INCREMENT 5 - /* Define PARAGRAPH_START_INDENT to be the amount of indentation that the first lines of paragraphs receive by default, where no other value has been specified. Users can change this value on the command @@ -95,103 +84,23 @@ char *alloca (); line between paragraphs. Paragraphs are defined by 2 or more consecutive newlines in the input file (i.e., one or more blank lines). */ #define DEFAULT_PARAGRAPH_SPACING 1 - -/* Define HAVE_MACROS to enable the macro facility of Texinfo. Using this - facility, users can create their own command procedures with - arguments. Must always be defined. */ -#define HAVE_MACROS - - -#define COMPILING_MAKEINFO -#include "makeinfo.h" - -/* Nonzero means that we are currently hacking the insides of an - insertion which would use a fixed width font. */ -static int in_fixed_width_font = 0; - -/* Nonzero means that start_paragraph () MUST be called before we pay - any attention to close_paragraph () calls. */ -int must_start_paragraph = 0; - -/* Nonzero means a string is in execution, as opposed to a file. */ -static int executing_string = 0; - -/* Nonzero means a macro string is in execution, as opposed to a file. */ -static int me_executing_string = 0; - -#if defined (HAVE_MACROS) -/* If non-NULL, this is an output stream to write the full macro expansion - of the input text to. The result is another texinfo file, but - missing @include, @infoinclude, @macro, and macro invocations. Instead, - all of the text is placed within the file. */ -FILE *macro_expansion_output_stream = (FILE *)NULL; -char *macro_expansion_filename; - -/* Here is a structure used to remember input text strings and offsets - within them. */ -typedef struct { - char *pointer; /* Pointer to the input text. */ - int offset; /* Offset of the last character output. */ -} ITEXT; - -static ITEXT **itext_info = (ITEXT **)NULL; -static int itext_size = 0; - -/* Nonzero means to inhibit writing macro expansions to the output - stream, because it has already been written. */ -int me_inhibit_expansion = 0; - -ITEXT *remember_itext (); -void forget_itext (), me_append_before_this_command (); -void append_to_expansion_output (), write_region_to_macro_output (); -void maybe_write_itext (), me_execute_string (); -#endif /* HAVE_MACROS */ - -/* **************************************************************** */ -/* */ -/* Global Variables */ -/* */ -/* **************************************************************** */ - -/* Global pointer to argv[0]. */ -char *progname; - -/* Return nonzero if STRING is the text at input_text + input_text_offset, - else zero. */ -#define looking_at(string) \ - (strncmp (input_text + input_text_offset, string, strlen (string)) == 0) - -/* And writing to the output. */ +/* Global variables. */ /* The output file name. */ -char *output_filename = (char *)NULL; -char *pretty_output_filename; +char *output_filename = NULL; /* Name of the output file that the user elected to pass on the command line. Such a name overrides any name found with the @setfilename command. */ -char *command_output_filename = (char *)NULL; +char *command_output_filename = NULL; -/* A colon separated list of directories to search for files included - with @include. This can be controlled with the `-I' option to makeinfo. */ -char *include_files_path = (char *)NULL; - -/* Position in the output file. */ -int output_position; +/* Flags which control initial output string for xrefs. */ +int px_ref_flag = 0; +int ref_flag = 0; #define INITIAL_PARAGRAPH_SPACE 5000 int paragraph_buffer_len = INITIAL_PARAGRAPH_SPACE; -/* Nonzero indicates that filling will take place on long lines. */ -int filling_enabled = 1; - -/* Nonzero means that words are not to be split, even in long lines. This - gets changed for cm_w (). */ -int non_splitting_words = 0; - -/* Nonzero indicates that filling a line also indents the new line. */ -int indented_fill = 0; - /* The amount of indentation to add at the starts of paragraphs. 0 means don't change existing indentation at paragraph starts. > 0 is amount to indent new paragraphs by. @@ -202,170 +111,26 @@ int indented_fill = 0; this is 3. */ int paragraph_start_indent = PARAGRAPH_START_INDENT; -/* Nonzero means that the use of paragraph_start_indent is inhibited. - @example uses this to line up the left columns of the example text. - A negative value for this variable is incremented each time it is used. - @noindent uses this to inhibit indentation for a single paragraph. */ -int inhibit_paragraph_indentation = 0; - /* Indentation that is pending insertion. We have this for hacking lines which look blank, but contain whitespace. We want to treat those as blank lines. */ int pending_indent = 0; -/* The amount that indentation increases/decreases by. */ -int default_indentation_increment = DEFAULT_INDENTATION_INCREMENT; - -/* Nonzero indicates that indentation is temporarily turned off. */ -int no_indent = 1; - -/* Nonzero means forcing output text to be flushright. */ -int force_flush_right = 0; - -/* Nonzero means that the footnote style for this document was set on - the command line, which overrides any other settings. */ -int footnote_style_preset = 0; - -/* Nonzero means that we automatically number footnotes that have no - specified marker. */ -int number_footnotes = 1; - -/* The current footnote number in this node. Each time a new node is - started this is reset to 1. */ -int current_footnote_number = 1; - -/* Command name in the process of being hacked. */ -char *command; - /* The index in our internal command table of the currently executing command. */ int command_index; -/* A search string which is used to find a line defining a node. */ -char node_search_string[] = - { '\n', COMMAND_PREFIX, 'n', 'o', 'd', 'e', ' ', 0 }; - -/* A search string which is used to find a line defining a menu. */ -char menu_search_string[] = - { '\n', COMMAND_PREFIX, 'm', 'e', 'n', 'u', 0 }; - /* A search string which is used to find the first @setfilename. */ char setfilename_search[] = { COMMAND_PREFIX, 's', 'e', 't', 'f', 'i', 'l', 'e', 'n', 'a', 'm', 'e', 0 }; -/* A stack of file information records. If a new file is read in with - "@input", we remember the old input file state on this stack. */ -typedef struct fstack -{ - struct fstack *next; - char *filename; - char *text; - int size; - int offset; - int line_number; -} FSTACK; - -FSTACK *filestack = (FSTACK *) NULL; - -/* Stuff for nodes. */ -/* The current nodes node name. */ -char *current_node = (char *)NULL; - -/* The current nodes section level. */ -int current_section = 0; - -/* The filename of the current input file. This is never freed. */ -char *node_filename = (char *)NULL; - -/* What we remember for each node. */ -typedef struct tentry -{ - struct tentry *next_ent; - char *node; /* name of this node. */ - char *prev; /* name of "Prev:" for this node. */ - char *next; /* name of "Next:" for this node. */ - char *up; /* name of "Up:" for this node. */ - int position; /* output file position of this node. */ - int line_no; /* defining line in source file. */ - char *filename; /* The file that this node was found in. */ - int touched; /* Nonzero means this node has been referenced. */ - int flags; /* Room for growth. Right now, contains 1 bit. */ -} TAG_ENTRY; - -/* If node-a has a "Next" for node-b, but node-b has no "Prev" for node-a, - we turn on this flag bit in node-b's tag entry. This means that when - it is time to validate node-b, we don't report an additional error - if there was no "Prev" field. */ -#define PREV_ERROR 0x1 -#define NEXT_ERROR 0x2 -#define UP_ERROR 0x4 -#define NO_WARN 0x8 -#define IS_TOP 0x10 - -TAG_ENTRY *tag_table = (TAG_ENTRY *) NULL; - /* Values for calling handle_variable_internal (). */ #define SET 1 #define CLEAR 2 #define IFSET 3 #define IFCLEAR 4 -#if defined (HAVE_MACROS) -#define ME_RECURSE 0x01 -#define ME_QUOTE_ARG 0x02 - -/* Macro definitions for user-defined commands. */ -typedef struct { - char *name; /* Name of the macro. */ - char **arglist; /* Args to replace when executing. */ - char *body; /* Macro body. */ - char *source_file; /* File where this macro is defined. */ - int source_lineno; /* Line number within FILENAME. */ - int inhibited; /* Nonzero means make find_macro () fail. */ - int flags; /* ME_RECURSE, ME_QUOTE_ARG, etc. */ -} MACRO_DEF; - -void add_macro (), execute_macro (); -MACRO_DEF *find_macro (), *delete_macro (); -#endif /* HAVE_MACROS */ - -/* Menu reference, *note reference, and validation hacking. */ - -/* The various references that we know about. */ -enum reftype -{ - menu_reference, followed_reference -}; - -/* A structure to remember references with. A reference to a node is - either an entry in a menu, or a cross-reference made with [px]ref. */ -typedef struct node_ref -{ - struct node_ref *next; - char *node; /* Name of node referred to. */ - char *containing_node; /* Name of node containing this reference. */ - int line_no; /* Line number where the reference occurs. */ - int section; /* Section level where the reference occurs. */ - char *filename; /* Name of file where the reference occurs. */ - enum reftype type; /* Type of reference, either menu or note. */ -} NODE_REF; - -/* The linked list of such structures. */ -NODE_REF *node_references = (NODE_REF *) NULL; - -/* Flag which tells us whether to examine menu lines or not. */ -int in_menu = 0; - -/* Flag which tells us how to examine menu lines. */ -int in_detailmenu = 0; - -/* Nonzero means that we have seen "@top" once already. */ -int top_node_seen = 0; - -/* Nonzero means that we have seen a non-"@top" node already. */ -int non_top_node_seen = 0; - /* Flags controlling the operation of the program. */ /* Default is to remove output if there were errors. */ @@ -374,81 +139,78 @@ int force = 0; /* Default is to notify users of bad choices. */ int print_warnings = 1; -/* Default is to check node references. */ -int validating = 1; - -/* Nonzero means do not output "Node: Foo" for node separations. */ -int no_headers = 0; - /* Number of errors that we tolerate on a given fileset. */ int max_error_level = 100; -/* Maximum number of references to a single node before complaining. */ -int reference_warning_limit = 1000; +/* The actual last inserted character. Note that this may be something + other than NEWLINE even if last_char_was_newline is 1. */ +int last_inserted_character = 0; -/* Nonzero means print out information about what is going on when it - is going on. */ -int verbose_mode = 0; +/* Nonzero means that a newline character has already been + inserted, so close_paragraph () should insert one less. */ +int line_already_broken = 0; -/* Nonzero means to be relaxed about the input file. This is useful when - we can successfully format the input, but it doesn't strictly match our - somewhat pedantic ideas of correctness. Right now, it affects what - @table and @itemize do without arguments. */ -int allow_lax_format = 0; +/* When nonzero we have finished an insertion (see end_insertion ()) and we + want to ignore false continued paragraph closings. */ +int insertion_paragraph_closed = 0; -/* The list of commands that we hack in texinfo. Each one - has an associated function. When the command is encountered in the - text, the associated function is called with START as the argument. - If the function expects arguments in braces, it remembers itself on - the stack. When the corresponding close brace is encountered, the - function is called with END as the argument. */ - -#define START 0 -#define END 1 +/* Nonzero means attempt to make all of the lines have fill_column width. */ +int do_justification = 0; typedef struct brace_element { struct brace_element *next; COMMAND_FUNCTION *proc; + char *command; int pos, line; int in_fixed_width_font; } BRACE_ELEMENT; -BRACE_ELEMENT *brace_stack = (BRACE_ELEMENT *) NULL; +BRACE_ELEMENT *brace_stack = NULL; -extern void do_multitable (); +extern void do_multitable (), end_multitable (); -void print_version_info (); -void usage (); void push_node_filename (), pop_node_filename (); -void remember_error (), flush_file_stack (); +void remember_error (); void convert_from_stream (), convert_from_file (), convert_from_loaded_file (); void init_internals (), init_paragraph (), init_brace_stack (); void init_insertion_stack (), init_indices (); void init_tag_table (), write_tag_table (), write_tag_table_internal (); void validate_file (), validate_other_references (), split_file (); -void free_node_references (), do_enumeration (), handle_variable (); +void free_node_references (), handle_variable (); void handle_variable_internal (); void normalize_node_name (); -void undefindex (), top_defindex (), gen_defindex (); -void define_user_command (); -void free_pending_notes (), output_pending_notes (); +void add_anchor_name (); +void free_node_node_references (), remember_node_node_reference (); char **get_brace_args (); -char *expansion (); int array_len (); void free_array (); static int end_of_sentence_p (); static void isolate_nodename (); -void reader_loop (), read_command (); +void reader_loop (); void remember_brace (), remember_brace_1 (); void pop_and_call_brace (), discard_braces (); void add_word (), add_char (), insert (), flush_output (); void insert_string (); -void close_paragraph_with_lines (), close_paragraph (); +void close_paragraph (); void ignore_blank_line (); void do_flush_right_indentation (), discard_insertions (); void start_paragraph (), indent (); +void inhibit_output_flushing (), uninhibit_output_flushing (); +int set_paragraph_indent (); +int self_delimiting (), search_forward (); +int multitable_item (), number_of_node (); +extern void add_link (), add_escaped_anchor_name (); + +void me_execute_string_keep_state (); +void maybe_update_execution_strings (); + +extern char *escape_string (); +extern void insert_html_tag (); +extern void sectioning_html (); +extern void add_link (); + #if defined (VA_FPRINTF) && __STDC__ /* Unfortunately we must use prototypes if we are to use . */ void add_word_args (char *, ...); @@ -456,356 +218,9 @@ void execute_string (char *, ...); #else void add_word_args (); void execute_string (); -#endif /* will not use prototypes */ +#endif /* no prototypes */ -void insert_self (), insert_space (), cm_ignore_line (); - -void - cm_TeX (), cm_asterisk (), cm_bullet (), cm_cite (), - cm_code (), cm_copyright (), cm_ctrl (), cm_dfn (), cm_dircategory (), - cm_direntry (), cm_dots (), cm_emph (), cm_enddots (), - cm_kbd (), cm_key (), cm_no_op (), cm_no_op_line_arg (), - cm_not_fixed_width (), cm_strong (), cm_var_sc (), cm_w (), cm_image (); - -/* Sectioning. */ -void - cm_chapter (), cm_unnumbered (), cm_appendix (), cm_top (), - cm_section (), cm_unnumberedsec (), cm_appendixsec (), - cm_subsection (), cm_unnumberedsubsec (), cm_appendixsubsec (), - cm_subsubsection (), cm_unnumberedsubsubsec (), cm_appendixsubsubsec (), - cm_heading (), cm_chapheading (), cm_subheading (), cm_subsubheading (), - cm_majorheading (), cm_raisesections (), cm_lowersections (); - -/* All @def... commands map to cm_defun, most accent commands map to - cm_accent, most non-English letters map to cm_special_char. */ -void cm_defun (), cm_accent (), cm_special_char (), cm_dotless (); - -void - cm_node (), cm_menu (), cm_xref (), cm_ftable (), cm_vtable (), cm_pxref (), - cm_inforef (), cm_uref (), cm_email (), cm_quotation (), - cm_display (), cm_itemize (), - cm_enumerate (), cm_tab (), cm_table (), cm_itemx (), cm_noindent (), - cm_setfilename (), cm_br (), cm_sp (), cm_page (), cm_group (), - cm_center (), cm_include (), cm_bye (), cm_item (), cm_end (), - cm_ifinfo (), cm_ifnothtml (), cm_ifnottex (), cm_kindex (), cm_cindex (), - cm_findex (), cm_pindex (), cm_vindex (), cm_tindex (), - cm_synindex (), cm_printindex (), cm_minus (), cm_footnote (), - cm_example (), cm_smallexample (), cm_lisp (), cm_format (), cm_exdent (), - cm_defindex (), cm_defcodeindex (), cm_result (), cm_expansion (), - cm_equiv (), cm_print (), cm_error (), cm_point (), cm_today (), - cm_flushleft (), cm_flushright (), cm_smalllisp (), cm_finalout (), - cm_cartouche (), cm_detailmenu (), cm_multitable (); - -/* Conditionals. */ -void cm_set (), cm_clear (), cm_ifset (), cm_ifclear (); -void cm_value (), cm_ifeq (); - -#if defined (HAVE_MACROS) -/* Define a user-defined command which is simple substitution. */ -void cm_macro (), cm_unmacro (); -#endif /* HAVE_MACROS */ - -/* Options. */ -void cm_paragraphindent (), cm_footnotestyle (); - -/* Internals. */ -void command_name_condition (), misplaced_brace (), cm_obsolete (), - cm_ideprecated (); - -typedef struct -{ - char *name; - COMMAND_FUNCTION *proc; - int argument_in_braces; -} COMMAND; - -/* Stuff for defining commands on the fly. */ -COMMAND **user_command_array = (COMMAND **) NULL; -int user_command_array_len = 0; - -#define NO_BRACE_ARGS 0 -#define BRACE_ARGS 1 - -static COMMAND command_table[] = { - { "\t", insert_space, NO_BRACE_ARGS }, - { "\n", insert_space, NO_BRACE_ARGS }, - { " ", insert_self, NO_BRACE_ARGS }, - { "!", insert_self, NO_BRACE_ARGS }, - { "\"", insert_self, NO_BRACE_ARGS }, - { "'", insert_self, NO_BRACE_ARGS }, - { "*", cm_asterisk, NO_BRACE_ARGS }, - { ",", cm_accent, BRACE_ARGS }, - { "-", cm_no_op, NO_BRACE_ARGS }, - { ".", insert_self, NO_BRACE_ARGS }, - { ":", cm_no_op, NO_BRACE_ARGS }, - { "=", insert_self, NO_BRACE_ARGS }, - { "?", insert_self, NO_BRACE_ARGS }, - { "@", insert_self, NO_BRACE_ARGS }, - { "^", insert_self, NO_BRACE_ARGS }, - { "`", insert_self, NO_BRACE_ARGS }, - { "{", insert_self, NO_BRACE_ARGS }, - { "|", cm_no_op, NO_BRACE_ARGS }, - { "}", insert_self, NO_BRACE_ARGS }, - { "~", insert_self, NO_BRACE_ARGS }, - { "AA", insert_self, BRACE_ARGS }, - { "AE", insert_self, BRACE_ARGS }, - { "H", cm_accent, BRACE_ARGS }, - { "L", cm_special_char, BRACE_ARGS }, - { "O", cm_special_char, BRACE_ARGS }, - { "OE", insert_self, BRACE_ARGS }, - { "TeX", cm_TeX, BRACE_ARGS }, - { "aa", insert_self, BRACE_ARGS }, - { "ae", insert_self, BRACE_ARGS }, - { "appendix", cm_appendix, NO_BRACE_ARGS }, - { "appendixsection", cm_appendixsec, NO_BRACE_ARGS }, - { "appendixsec", cm_appendixsec, NO_BRACE_ARGS }, - { "appendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS }, - { "appendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS }, - { "asis", cm_no_op, BRACE_ARGS }, - { "b", cm_not_fixed_width, BRACE_ARGS }, - { "bullet", cm_bullet, BRACE_ARGS }, - { "bye", cm_bye, NO_BRACE_ARGS }, - { "c", cm_ignore_line, NO_BRACE_ARGS }, - { "cartouche", cm_cartouche, NO_BRACE_ARGS }, - { "center", cm_center, NO_BRACE_ARGS }, - { "centerchap", cm_unnumbered, NO_BRACE_ARGS }, - { "chapheading", cm_chapheading, NO_BRACE_ARGS }, - { "chapter", cm_chapter, NO_BRACE_ARGS }, - { "cindex", cm_cindex, NO_BRACE_ARGS }, - { "cite", cm_cite, BRACE_ARGS }, - { "clear", cm_clear, NO_BRACE_ARGS }, - { "code", cm_code, BRACE_ARGS }, - { "comment", cm_ignore_line, NO_BRACE_ARGS }, - { "contents", cm_no_op, NO_BRACE_ARGS }, - { "copyright", cm_copyright, BRACE_ARGS }, - { "ctrl", cm_obsolete, BRACE_ARGS }, - { "defcodeindex", cm_defcodeindex, NO_BRACE_ARGS }, - { "defindex", cm_defindex, NO_BRACE_ARGS }, -/* The `def' commands. */ - { "defcv", cm_defun, NO_BRACE_ARGS }, - { "defcvx", cm_defun, NO_BRACE_ARGS }, - { "deffn", cm_defun, NO_BRACE_ARGS }, - { "deffnx", cm_defun, NO_BRACE_ARGS }, - { "defivar", cm_defun, NO_BRACE_ARGS }, - { "defivarx", cm_defun, NO_BRACE_ARGS }, - { "defmac", cm_defun, NO_BRACE_ARGS }, - { "defmacx", cm_defun, NO_BRACE_ARGS }, - { "defmethod", cm_defun, NO_BRACE_ARGS }, - { "defmethodx", cm_defun, NO_BRACE_ARGS }, - { "defop", cm_defun, NO_BRACE_ARGS }, - { "defopt", cm_defun, NO_BRACE_ARGS }, - { "defoptx", cm_defun, NO_BRACE_ARGS }, - { "defopx", cm_defun, NO_BRACE_ARGS }, - { "defspec", cm_defun, NO_BRACE_ARGS }, - { "defspecx", cm_defun, NO_BRACE_ARGS }, - { "deftp", cm_defun, NO_BRACE_ARGS }, - { "deftpx", cm_defun, NO_BRACE_ARGS }, - { "deftypefn", cm_defun, NO_BRACE_ARGS }, - { "deftypefnx", cm_defun, NO_BRACE_ARGS }, - { "deftypefun", cm_defun, NO_BRACE_ARGS }, - { "deftypefunx", cm_defun, NO_BRACE_ARGS }, - { "deftypemethod", cm_defun, NO_BRACE_ARGS }, - { "deftypemethodx", cm_defun, NO_BRACE_ARGS }, - { "deftypevar", cm_defun, NO_BRACE_ARGS }, - { "deftypevarx", cm_defun, NO_BRACE_ARGS }, - { "deftypevr", cm_defun, NO_BRACE_ARGS }, - { "deftypevrx", cm_defun, NO_BRACE_ARGS }, - { "defun", cm_defun, NO_BRACE_ARGS }, - { "defunx", cm_defun, NO_BRACE_ARGS }, - { "defvar", cm_defun, NO_BRACE_ARGS }, - { "defvarx", cm_defun, NO_BRACE_ARGS }, - { "defvr", cm_defun, NO_BRACE_ARGS }, - { "defvrx", cm_defun, NO_BRACE_ARGS }, -/* The end of the `def' commands. */ - { "detailmenu", cm_detailmenu, NO_BRACE_ARGS }, - { "dfn", cm_dfn, BRACE_ARGS }, - { "dircategory", cm_dircategory, NO_BRACE_ARGS }, - { "direntry", cm_direntry, NO_BRACE_ARGS }, - { "display", cm_display, NO_BRACE_ARGS }, - { "dmn", cm_no_op, BRACE_ARGS }, - { "dotaccent", cm_accent, BRACE_ARGS }, - { "dotless", cm_dotless, BRACE_ARGS }, - { "dots", cm_dots, BRACE_ARGS }, - { "email", cm_email, BRACE_ARGS }, - { "emph", cm_emph, BRACE_ARGS }, - { "end", cm_end, NO_BRACE_ARGS }, - { "enddots", cm_enddots, BRACE_ARGS }, - { "enumerate", cm_enumerate, NO_BRACE_ARGS }, - { "equiv", cm_equiv, BRACE_ARGS }, - { "error", cm_error, BRACE_ARGS }, - { "example", cm_example, NO_BRACE_ARGS }, - { "exclamdown", cm_special_char, BRACE_ARGS }, - { "exdent", cm_exdent, NO_BRACE_ARGS }, - { "expansion", cm_expansion, BRACE_ARGS }, - { "file", cm_code, BRACE_ARGS }, - { "finalout", cm_no_op, NO_BRACE_ARGS }, - { "findex", cm_findex, NO_BRACE_ARGS }, - { "flushleft", cm_flushleft, NO_BRACE_ARGS }, - { "flushright", cm_flushright, NO_BRACE_ARGS }, - { "footnote", cm_footnote, NO_BRACE_ARGS}, /* self-arg eater */ - { "footnotestyle", cm_footnotestyle, NO_BRACE_ARGS }, - { "format", cm_format, NO_BRACE_ARGS }, - { "ftable", cm_ftable, NO_BRACE_ARGS }, - { "group", cm_group, NO_BRACE_ARGS }, - { "heading", cm_heading, NO_BRACE_ARGS }, - { "headings", cm_ignore_line, NO_BRACE_ARGS }, - { "html", command_name_condition, NO_BRACE_ARGS }, - { "hyphenation", cm_no_op, BRACE_ARGS }, - { "i", cm_not_fixed_width, BRACE_ARGS }, - { "ifclear", cm_ifclear, NO_BRACE_ARGS }, - { "ifeq", cm_ifeq, NO_BRACE_ARGS }, - { "ifhtml", command_name_condition, NO_BRACE_ARGS }, - { "ifinfo", cm_ifinfo, NO_BRACE_ARGS }, - { "ifnothtml", cm_ifnothtml, NO_BRACE_ARGS }, - { "ifnotinfo", command_name_condition, NO_BRACE_ARGS }, - { "ifnottex", cm_ifnottex, NO_BRACE_ARGS }, - { "ifset", cm_ifset, NO_BRACE_ARGS }, - { "iftex", command_name_condition, NO_BRACE_ARGS }, - { "ignore", command_name_condition, NO_BRACE_ARGS }, - { "image", cm_image, BRACE_ARGS }, - { "include", cm_include, NO_BRACE_ARGS }, - { "inforef", cm_inforef, BRACE_ARGS }, - { "item", cm_item, NO_BRACE_ARGS }, - { "itemize", cm_itemize, NO_BRACE_ARGS }, - { "itemx", cm_itemx, NO_BRACE_ARGS }, - { "kbd", cm_kbd, BRACE_ARGS }, - { "kbdinputstyle", cm_no_op_line_arg, NO_BRACE_ARGS }, - { "key", cm_key, BRACE_ARGS }, - { "kindex", cm_kindex, NO_BRACE_ARGS }, - { "l", cm_special_char, BRACE_ARGS }, - { "lisp", cm_lisp, NO_BRACE_ARGS }, - { "lowersections", cm_lowersections, NO_BRACE_ARGS }, - { "macro", cm_macro, NO_BRACE_ARGS }, - { "majorheading", cm_majorheading, NO_BRACE_ARGS }, - { "math", cm_no_op, BRACE_ARGS }, - { "menu", cm_menu, NO_BRACE_ARGS }, - { "minus", cm_minus, BRACE_ARGS }, - { "multitable", cm_multitable, NO_BRACE_ARGS }, - { "need", cm_ignore_line, NO_BRACE_ARGS }, - { "node", cm_node, NO_BRACE_ARGS }, - { "noindent", cm_noindent, NO_BRACE_ARGS }, - { "nwnode", cm_node, NO_BRACE_ARGS }, - { "o", cm_special_char, BRACE_ARGS }, - { "oe", insert_self, BRACE_ARGS }, - { "page", cm_no_op, NO_BRACE_ARGS }, - { "paragraphindent", cm_paragraphindent, NO_BRACE_ARGS }, - { "pindex", cm_pindex, NO_BRACE_ARGS }, - { "point", cm_point, BRACE_ARGS }, - { "pounds", cm_special_char, BRACE_ARGS }, - { "print", cm_print, BRACE_ARGS }, - { "printindex", cm_printindex, NO_BRACE_ARGS }, - { "pxref", cm_pxref, BRACE_ARGS }, - { "questiondown", cm_special_char, BRACE_ARGS }, - { "quotation", cm_quotation, NO_BRACE_ARGS }, - { "r", cm_not_fixed_width, BRACE_ARGS }, - { "raisesections", cm_raisesections, NO_BRACE_ARGS }, - { "ref", cm_xref, BRACE_ARGS }, - { "refill", cm_no_op, NO_BRACE_ARGS }, - { "result", cm_result, BRACE_ARGS }, - { "ringaccent", cm_accent, BRACE_ARGS }, - { "samp", cm_code, BRACE_ARGS }, - { "sc", cm_var_sc, BRACE_ARGS }, - { "section", cm_section, NO_BRACE_ARGS }, - { "set", cm_set, NO_BRACE_ARGS }, - { "setchapternewpage", cm_ignore_line, NO_BRACE_ARGS }, - { "setchapterstyle", cm_obsolete, NO_BRACE_ARGS }, - { "setfilename", cm_setfilename, NO_BRACE_ARGS }, - { "settitle", cm_ignore_line, NO_BRACE_ARGS }, - { "shortcontents", cm_no_op, NO_BRACE_ARGS }, - { "shorttitlepage", cm_ignore_line, NO_BRACE_ARGS }, - { "smallbook", cm_ignore_line, NO_BRACE_ARGS }, - { "smallexample", cm_smallexample, NO_BRACE_ARGS }, - { "smalllisp", cm_smalllisp, NO_BRACE_ARGS }, - { "sp", cm_sp, NO_BRACE_ARGS }, - { "ss", insert_self, BRACE_ARGS }, - { "strong", cm_strong, BRACE_ARGS }, - { "subheading", cm_subheading, NO_BRACE_ARGS }, - { "subsection", cm_subsection, NO_BRACE_ARGS }, - { "subsubheading", cm_subsubheading, NO_BRACE_ARGS }, - { "subsubsection", cm_subsubsection, NO_BRACE_ARGS }, - { "summarycontents", cm_no_op, NO_BRACE_ARGS }, - { "syncodeindex", cm_synindex, NO_BRACE_ARGS }, - { "synindex", cm_synindex, NO_BRACE_ARGS }, - { "t", cm_no_op, BRACE_ARGS }, - { "tab", cm_tab, NO_BRACE_ARGS }, - { "table", cm_table, NO_BRACE_ARGS }, - { "tex", command_name_condition, NO_BRACE_ARGS }, - { "tieaccent", cm_accent, BRACE_ARGS }, - { "tindex", cm_tindex, NO_BRACE_ARGS }, - { "titlefont", cm_not_fixed_width, BRACE_ARGS }, - { "titlepage", command_name_condition, NO_BRACE_ARGS }, - { "today", cm_today, BRACE_ARGS }, - { "top", cm_top, NO_BRACE_ARGS }, - { "u", cm_accent, BRACE_ARGS }, - { "ubaraccent", cm_accent, BRACE_ARGS }, - { "udotaccent", cm_accent, BRACE_ARGS }, -#if defined (HAVE_MACROS) - { "unmacro", cm_unmacro, NO_BRACE_ARGS }, -#endif - { "unnumbered", cm_unnumbered, NO_BRACE_ARGS }, - { "unnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS }, - { "unnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS }, - { "unnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS }, - { "uref", cm_uref, BRACE_ARGS }, - { "url", cm_code, BRACE_ARGS }, - { "v", cm_accent, BRACE_ARGS }, - { "value", cm_value, BRACE_ARGS }, - { "var", cm_var_sc, BRACE_ARGS }, - { "vindex", cm_vindex, NO_BRACE_ARGS }, - { "vtable", cm_vtable, NO_BRACE_ARGS }, - { "w", cm_w, BRACE_ARGS }, - { "xref", cm_xref, BRACE_ARGS }, - - /* Deprecated commands. These used to be for italics. */ - { "iappendix", cm_ideprecated, NO_BRACE_ARGS }, - { "iappendixsec", cm_ideprecated, NO_BRACE_ARGS }, - { "iappendixsection", cm_ideprecated, NO_BRACE_ARGS }, - { "iappendixsubsec", cm_ideprecated, NO_BRACE_ARGS }, - { "iappendixsubsubsec", cm_ideprecated, NO_BRACE_ARGS }, - { "ichapter", cm_ideprecated, NO_BRACE_ARGS }, - { "isection", cm_ideprecated, NO_BRACE_ARGS }, - { "isubsection", cm_ideprecated, NO_BRACE_ARGS }, - { "isubsubsection", cm_ideprecated, NO_BRACE_ARGS }, - { "iunnumbered", cm_ideprecated, NO_BRACE_ARGS }, - { "iunnumberedsec", cm_ideprecated, NO_BRACE_ARGS }, - { "iunnumberedsubsec", cm_ideprecated, NO_BRACE_ARGS }, - { "iunnumberedsubsubsec", cm_ideprecated, NO_BRACE_ARGS }, - - /* Now @include does what this was used to. */ - { "infoinclude", cm_obsolete, NO_BRACE_ARGS }, - { "titlespec", cm_obsolete, NO_BRACE_ARGS }, - - { NULL, NULL, NO_BRACE_ARGS } -}; - -struct option long_options[] = -{ - { "error-limit", 1, 0, 'e' }, /* formerly -el */ - { "fill-column", 1, 0, 'f' }, /* formerly -fc */ - { "footnote-style", 1, 0, 's' }, /* formerly -ft */ - { "force", 0, 0, 'F' }, /* do not remove output */ - { "no-headers", 0, &no_headers, 1 }, /* do not output Node: foo */ - { "no-pointer-validate", 0, &validating, 0 }, /* formerly -nv */ - { "no-validate", 0, &validating, 0 }, /* formerly -nv */ - { "no-split", 0, &splitting, 0 }, /* formerly -ns */ - { "no-warn", 0, &print_warnings, 0 }, /* formerly -nw */ - { "macro-expand", 1, 0, 'E' }, - { "number-footnotes", 0, &number_footnotes, 1 }, - { "no-number-footnotes", 0, &number_footnotes, 0 }, - { "output", 1, 0, 'o' }, - { "paragraph-indent", 1, 0, 'p' }, /* formerly -pi */ - { "reference-limit", 1, 0, 'r' }, /* formerly -rl */ - { "verbose", 0, &verbose_mode, 1 }, /* formerly -verbose */ - { "help", 0, 0, 'h' }, - { "version", 0, 0, 'V' }, - {NULL, 0, NULL, 0} -}; - -/* **************************************************************** */ -/* */ -/* Error Handling */ -/* */ -/* **************************************************************** */ +/* Error handling. */ /* Number of errors encountered. */ int errors_printed = 0; @@ -817,7 +232,7 @@ fs_error (filename) { remember_error (); perror (filename); - return (0); + return 0; } /* Print an error message, and return false. */ @@ -916,15 +331,136 @@ remember_error () fprintf (stderr, _("Too many errors! Gave up.\n")); flush_file_stack (); cm_bye (); - exit (FATAL); + xexit (1); } } + +/* The other side of a malformed expression. */ +void +misplaced_brace () +{ + line_error (_("Misplaced %c"), '}'); +} -/* **************************************************************** */ -/* */ -/* Main () Start of code */ -/* */ -/* **************************************************************** */ +/* Main. */ + +/* Display the version info of this invocation of Makeinfo. */ +static void +print_version_info () +{ + printf ("makeinfo (GNU %s) %s\n", PACKAGE, VERSION); +} + +/* If EXIT_VALUE is zero, print the full usage message to stdout. + Otherwise, just say to use --help for more info. + Then exit with EXIT_VALUE. */ +static void +usage (exit_value) + int exit_value; +{ + if (exit_value != 0) + fprintf (stderr, _("Try `%s --help' for more information.\n"), progname); + else + { + printf (_("Usage: %s [OPTION]... TEXINFO-FILE...\n\ +\n\ +Translate Texinfo source documentation to various other formats:\n\ +Info files suitable for reading online with Emacs or standalone GNU Info\n\ +(by default); plain text (with --no-headers); or HTML (with --html).\n\ +\n\ +Options:\n\ + --commands-in-node-names allow @ commands in node names.\n\ + -D VAR define a variable, as with @set.\n\ + -E, --macro-expand FILE output macro-expanded source to FILE.\n\ + --error-limit=NUM quit after NUM errors (default %d).\n\ + --fill-column=NUM break Info lines at NUM characters (default %d).\n\ + --footnote-style=STYLE output footnotes according to STYLE:\n\ + `separate' to place footnotes in their own node,\n\ + `end' to place the footnotes at the end of the\n\ + node in which they are defined (the default).\n\ + --force preserve output even if errors.\n\ + --help display this help and exit.\n\ + --html output HTML rather than Info format;\n\ + -I DIR append DIR to the @include search path.\n\ + --ifhtml process @ifhtml and @html text even when not\n\ + generating HTML.\n\ + --ifinfo process @ifinfo text even when generating HTML.\n\ + --iftex process @iftex and @tex text.\n\ + implies --no-split.\n"), + progname, max_error_level, fill_column); + printf (_("\ + --no-headers suppress Info node separators and Node: lines and\n\ + write to standard output without --output.\n\ + --no-ifhtml do not process @ifhtml and @html text.\n\ + --no-ifinfo do not process @ifinfo text.\n\ + --no-iftex do not process @iftex and @tex text.\n\ + --no-split suppress splitting of large Info output files or\n\ + generation of one HTML file per node.\n\ + --no-validate suppress node cross-reference validation.\n\ + --no-warn suppress warnings (but not errors).\n\ + --number-sections include chapter, section, etc. numbers in output.\n\ + -o, --output=FILE output to FILE, ignoring any @setfilename.\n\ + -P DIR prepend DIR to the @include search path.\n\ + --paragraph-indent=VAL indent Info paragraphs by VAL spaces (default %d).\n\ + if VAL is `none', do not indent;\n\ + if VAL is `asis', preserve existing indentation.\n\ + --reference-limit=NUM warn about at most NUM references (default %d).\n\ + -U VAR undefine a variable, as with @clear.\n\ + -v, --verbose explain what is being done.\n\ + --version display version information and exit.\n\ +"), + paragraph_start_indent, reference_warning_limit); + } + + puts (_("\n\ +The defaults for the @if... conditionals depend on the output format:\n\ +if generating HTML, --ifhtml is on and the others are off;\n\ +if generating Info or plain text, --ifinfo is on and the others are off.\n\ +\n\ +Examples:\n\ + makeinfo foo.texi write Info to foo's @setfilename\n\ + makeinfo --html foo.texi write HTML to foo's @setfilename\n\ + makeinfo --no-headers -o - foo.texi write plain text to standard output\n\ + makeinfo --number-sections foo.texi write Info with numbered sections\n\ + makeinfo --no-split foo.texi write one Info file however big\n\ +\n\ +Email bug reports to bug-texinfo@gnu.org,\n\ +general questions and discussion to help-texinfo@gnu.org.")); + xexit (exit_value); +} + +struct option long_options[] = +{ + { "commands-in-node-names", 0, &expensive_validation, 1 }, + { "error-limit", 1, 0, 'e' }, + { "fill-column", 1, 0, 'f' }, + { "footnote-style", 1, 0, 's' }, + { "force", 0, &force, 1 }, + { "help", 0, 0, 'h' }, + { "html", 0, 0, 'w' }, + { "ifhtml", 0, &process_html, 1 }, + { "ifinfo", 0, &process_info, 1 }, + { "iftex", 0, &process_tex, 1 }, + { "macro-expand", 1, 0, 'E' }, + { "no-headers", 0, &no_headers, 1 }, + { "no-ifhtml", 0, &process_html, 0 }, + { "no-ifinfo", 0, &process_info, 0 }, + { "no-iftex", 0, &process_tex, 0 }, + { "no-number-footnotes", 0, &number_footnotes, 0 }, + { "no-number-sections", 0, &number_sections, 0 }, + { "no-pointer-validate", 0, &validating, 0 }, + { "no-split", 0, &splitting, 0 }, + { "no-validate", 0, &validating, 0 }, + { "no-warn", 0, &print_warnings, 0 }, + { "number-footnotes", 0, &number_footnotes, 1 }, + { "number-sections", 0, &number_sections, 1 }, + { "output", 1, 0, 'o' }, + { "paragraph-indent", 1, 0, 'p' }, + { "reference-limit", 1, 0, 'r' }, + { "verbose", 0, &verbose_mode, 1 }, + { "version", 0, 0, 'V' }, + {NULL, 0, NULL, 0} +}; /* For each file mentioned in the command line, process it, turning Texinfo commands into wonderfully formatted output text. */ @@ -934,18 +470,16 @@ main (argc, argv) char **argv; { extern int errors_printed; - char *filename_part (); int c, ind; int reading_from_stdin = 0; - /* The name of this program is the last filename in argv[0]. */ - progname = filename_part (argv[0]); - #ifdef HAVE_SETLOCALE /* Do not use LC_ALL, because LC_NUMERIC screws up the scanf parsing of the argument to @multicolumn. */ setlocale (LC_TIME, ""); setlocale (LC_MESSAGES, ""); + setlocale (LC_CTYPE, ""); + setlocale (LC_COLLATE, ""); #endif /* Set the text message domain. */ @@ -953,7 +487,7 @@ main (argc, argv) textdomain (PACKAGE); /* Parse argument flags from the input line. */ - while ((c = getopt_long (argc, argv, "D:e:E:f:I:o:p:P:r:s:U:V", + while ((c = getopt_long (argc, argv, "D:e:E:f:hI:o:p:P:r:s:U:vV:w", long_options, &ind)) != EOF) { if (c == 0 && long_options[ind].flag == 0) @@ -967,19 +501,17 @@ main (argc, argv) handle_variable_internal ((c == 'D') ? SET : CLEAR, optarg); break; - case 'e': - /* User specified error level. */ + case 'e': /* --error-limit */ if (sscanf (optarg, "%d", &max_error_level) != 1) { fprintf (stderr, _("%s: %s arg must be numeric, not `%s'.\n"), "--error-limit", progname, optarg); - usage (stderr, FATAL); + usage (stderr, 1); } break; - case 'E': - /* User specified a macro expansion output file. */ + case 'E': /* --macro-expand */ if (!macro_expansion_output_stream) { macro_expansion_filename = optarg; @@ -992,23 +524,18 @@ main (argc, argv) error (_("Cannot specify more than one macro expansion output")); break; - case 'f': - /* User specified fill_column. */ + case 'f': /* --fill-column */ if (sscanf (optarg, "%d", &fill_column) != 1) { fprintf (stderr, - _("%s: %s arg must be numeric, not `%s'.\n"), + _("%s: %s arg must be numeric, not `%s'.\n"), "--fill-column", progname, optarg); - usage (FATAL); + usage (1); } break; - case 'F': - force++; /* Do not remove erroneous output. */ - break; - - case 'h': - usage (NO_ERROR); + case 'h': /* --help */ + usage (0); break; case 'I': @@ -1019,23 +546,21 @@ main (argc, argv) include_files_path = (char *) xrealloc (include_files_path, 2 + strlen (include_files_path) + strlen (optarg)); - strcat (include_files_path, ":"); + strcat (include_files_path, PATH_SEP); strcat (include_files_path, optarg); break; - case 'o': - /* User specified output file. */ + case 'o': /* --output */ command_output_filename = xstrdup (optarg); break; - case 'p': - /* User specified paragraph indent (paragraph_start_index). */ + case 'p': /* --paragraph-indent */ if (set_paragraph_indent (optarg) < 0) { fprintf (stderr, - _("%s: --paragraph-indent arg must be numeric/`none'/`asis', not `%s'.\n"), + _("%s: --paragraph-indent arg must be numeric/`none'/`asis', not `%s'.\n"), progname, optarg); - usage (FATAL); + usage (1); } break; @@ -1044,14 +569,14 @@ main (argc, argv) if (!include_files_path) { include_files_path = xstrdup (optarg); - include_files_path = (char *) xrealloc (include_files_path, + include_files_path = xrealloc (include_files_path, strlen (include_files_path) + 3); /* 3 for ":.\0" */ - strcat (include_files_path, ":."); + strcat (strcat (include_files_path, PATH_SEP), "."); } else { char *tmp = xstrdup (include_files_path); - include_files_path = (char *) xrealloc (include_files_path, + include_files_path = xrealloc (include_files_path, strlen (include_files_path) + strlen (optarg) + 2); /* 2 for ":\0" */ strcpy (include_files_path, optarg); strcat (include_files_path, ":"); @@ -1059,47 +584,59 @@ main (argc, argv) free (tmp); } break; - - case 'r': - /* User specified reference warning limit. */ + + case 'r': /* --reference-limit */ if (sscanf (optarg, "%d", &reference_warning_limit) != 1) { fprintf (stderr, - _("%s: %s arg must be numeric, not `%s'.\n"), + _("%s: %s arg must be numeric, not `%s'.\n"), "--reference-limit", progname, optarg); - usage (FATAL); + usage (1); } break; - case 's': - /* User specified footnote style. */ + case 's': /* --footnote-style */ if (set_footnote_style (optarg) < 0) { fprintf (stderr, - _("%s: --footnote-style arg must be `separate' or `end', not `%s'.\n"), + _("%s: --footnote-style arg must be `separate' or `end', not `%s'.\n"), progname, optarg); - usage (FATAL); + usage (1); } footnote_style_preset = 1; break; - case 'V': - /* User requested version info. */ + case 'v': + verbose_mode++; + break; + + case 'V': /* --version */ print_version_info (); - printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\ + puts (""); + printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\ There is NO warranty. You may redistribute this software\n\ under the terms of the GNU General Public License.\n\ For more information about these matters, see the files named COPYING.\n"), - "1998"); - exit (NO_ERROR); + "1999"); + exit (0); + break; + + case 'w': /* --html */ + html = 1; + process_html = 1; + process_info = 0; + splitting = 0; /* too complicated for now */ break; case '?': - usage (FATAL); + usage (1); break; } } + if (!validating) + expensive_validation = 0; + if (optind == argc) { /* Check to see if input is a file. If so, process that. */ @@ -1108,19 +645,24 @@ For more information about these matters, see the files named COPYING.\n"), else { fprintf (stderr, _("%s: missing file argument.\n"), progname); - usage (FATAL); + usage (1); } } - /* If the user has specified --no-headers, this should imply --no-split. - Do that here. I think it might also imply that we should ignore the - setfilename at the top of the file, but this might break some FSF things, - so I will hold off on that. */ if (no_headers) { + if (html && splitting) + { /* --no-headers --no-split --html indicates confusion. */ + fprintf (stderr, + "%s: --no-headers conflicts with --no-split for --html.\n", + progname); + usage (1); + } + + /* --no-headers implies --no-split. */ splitting = 0; - /* If the user has not specified an output file, use stdout. */ + /* If the user did not specify an output file, use stdout. */ if (!command_output_filename) command_output_filename = xstrdup ("-"); } @@ -1138,491 +680,11 @@ For more information about these matters, see the files named COPYING.\n"), else convert_from_stream (stdin, "stdin"); - if (errors_printed) - return (SYNTAX); - else - return (NO_ERROR); + return errors_printed ? 2 : 0; } -/* Display the version info of this invocation of Makeinfo. */ -void -print_version_info () -{ - printf ("makeinfo (GNU %s %s) %d.%d\n", PACKAGE, VERSION, - major_version, minor_version); -} - -/* If EXIT_VALUE is zero, print the full usage message to stdout. - Otherwise, just say to use --help for more info. - Then exit with EXIT_VALUE. */ -void -usage (exit_value) - int exit_value; -{ - if (exit_value != 0) - fprintf (stderr, _("Try `%s --help' for more information.\n"), progname); - else - printf (_("Usage: %s [OPTION]... TEXINFO-FILE...\n\ -\n\ -Translate Texinfo source documentation to a format suitable for reading\n\ -with GNU Info.\n\ -\n\ -Options:\n\ --D VAR define a variable, as with @set.\n\ --E MACRO-OFILE process macros only, output texinfo source.\n\ --I DIR append DIR to the @include directory search path.\n\ --P DIR prepend DIR to the @include directory search path.\n\ --U VAR undefine a variable, as with @clear.\n\ ---error-limit NUM quit after NUM errors (default %d).\n\ ---fill-column NUM break lines at NUM characters (default %d).\n\ ---footnote-style STYLE output footnotes according to STYLE:\n\ - `separate' to place footnotes in their own node,\n\ - `end' to place the footnotes at the end of\n\ - the node in which they are defined (the default).\n\ ---force preserve output even if errors.\n\ ---help display this help and exit.\n\ ---no-validate suppress node cross-reference validation.\n\ ---no-warn suppress warnings (but not errors).\n\ ---no-split suppress splitting of large files.\n\ ---no-headers suppress node separators and Node: Foo headers.\n\ ---output FILE, -o FILE output to FILE, and ignore any @setfilename.\n\ ---paragraph-indent VAL indent paragraphs with VAL spaces (default %d).\n\ - if VAL is `none', do not indent; if VAL is `asis',\n\ - preserve any existing indentation.\n\ ---reference-limit NUM complain about at most NUM references (default %d).\n\ ---verbose report about what is being done.\n\ ---version display version information and exit.\n\ -\n\ -Email bug reports to bug-texinfo@gnu.org.\n\ -"), - progname, max_error_level, fill_column, - paragraph_start_indent, reference_warning_limit); - exit (exit_value); -} -/* Manipulating Lists */ - -typedef struct generic_list { - struct generic_list *next; -} GENERIC_LIST; - -/* Reverse the chain of structures in LIST. Output the new head - of the chain. You should always assign the output value of this - function to something, or you will lose the chain. */ -GENERIC_LIST * -reverse_list (list) - register GENERIC_LIST *list; -{ - register GENERIC_LIST *next; - register GENERIC_LIST *prev = (GENERIC_LIST *) NULL; - - while (list) - { - next = list->next; - list->next = prev; - prev = list; - list = next; - } - return (prev); -} - -/* Pushing and Popping Files */ - -/* Find and load the file named FILENAME. Return a pointer to - the loaded file, or NULL if it can't be loaded. */ -char * -find_and_load (filename) - char *filename; -{ - struct stat fileinfo; - long file_size; - int file = -1, count = 0; - char *fullpath, *result, *get_file_info_in_path (); - - result = fullpath = (char *)NULL; - - fullpath = get_file_info_in_path (filename, include_files_path, &fileinfo); - - if (!fullpath) - goto error_exit; - - filename = fullpath; - file_size = (long) fileinfo.st_size; - - file = open (filename, O_RDONLY); - if (file < 0) - goto error_exit; - - /* Load the file, with enough room for a newline and a null. */ - result = xmalloc (file_size + 2); - - /* VMS stat lies about the st_size value. The actual number of - readable bytes is always less than this value. The arcane - mysteries of VMS/RMS are too much to probe, so this hack - suffices to make things work. */ -#if defined (VMS) || defined (WIN32) -#ifdef VMS - while ((n = read (file, result + count, file_size)) > 0) -#else /* WIN32 */ - while ((n = read (file, result + count, 1)) > 0) -#endif /* WIN32 */ - count += n; - if (n == -1) -#else /* !VMS && !WIN32 */ - count = file_size; - if (read (file, result, file_size) != file_size) -#endif /* !VMS && !WIN32 */ - error_exit: - { - if (result) - free (result); - - if (fullpath) - free (fullpath); - - if (file != -1) - close (file); - - return ((char *) NULL); - } - close (file); - - /* Set the globals to the new file. */ - input_text = result; - size_of_input_text = count; - input_filename = fullpath; - node_filename = xstrdup (fullpath); - input_text_offset = 0; - line_number = 1; - /* Not strictly necessary. This magic prevents read_token () from doing - extra unnecessary work each time it is called (that is a lot of times). - SIZE_OF_INPUT_TEXT is one past the actual end of the text. */ - input_text[size_of_input_text] = '\n'; - /* This, on the other hand, is always necessary. */ - input_text[size_of_input_text+1] = 0; - return (result); -} - -/* Save the state of the current input file. */ -void -pushfile () -{ - FSTACK *newstack = (FSTACK *) xmalloc (sizeof (FSTACK)); - newstack->filename = input_filename; - newstack->text = input_text; - newstack->size = size_of_input_text; - newstack->offset = input_text_offset; - newstack->line_number = line_number; - newstack->next = filestack; - - filestack = newstack; - push_node_filename (); -} - -/* Make the current file globals be what is on top of the file stack. */ -void -popfile () -{ - FSTACK *tos = filestack; - - if (!tos) - abort (); /* My fault. I wonder what I did? */ - -#if defined (HAVE_MACROS) - if (macro_expansion_output_stream) - { - maybe_write_itext (input_text, input_text_offset); - forget_itext (input_text); - } -#endif /* HAVE_MACROS */ - - /* Pop the stack. */ - filestack = filestack->next; - - /* Make sure that commands with braces have been satisfied. */ - if (!executing_string && !me_executing_string) - discard_braces (); - - /* Get the top of the stack into the globals. */ - input_filename = tos->filename; - input_text = tos->text; - size_of_input_text = tos->size; - input_text_offset = tos->offset; - line_number = tos->line_number; - free (tos); - - /* Go back to the (now) current node. */ - pop_node_filename (); -} - -/* Flush all open files on the file stack. */ -void -flush_file_stack () -{ - while (filestack) - { - char *fname = input_filename; - char *text = input_text; - popfile (); - free (fname); - free (text); - } -} - -int node_filename_stack_index = 0; -int node_filename_stack_size = 0; -char **node_filename_stack = (char **)NULL; - -void -push_node_filename () -{ - if (node_filename_stack_index + 1 > node_filename_stack_size) - node_filename_stack = (char **)xrealloc - (node_filename_stack, (node_filename_stack_size += 10) * sizeof (char *)); - - node_filename_stack[node_filename_stack_index] = node_filename; - node_filename_stack_index++; -} - -void -pop_node_filename () -{ - node_filename = node_filename_stack[--node_filename_stack_index]; -} - -/* Return just the simple part of the filename; i.e. the - filename without the path information, or extensions. - This conses up a new string. */ -char * -filename_part (filename) - char *filename; -{ - char *basename; - - basename = strrchr (filename, '/'); - if (!basename) - basename = filename; - else - basename++; - - basename = xstrdup (basename); -#if defined (REMOVE_OUTPUT_EXTENSIONS) - - /* See if there is an extension to remove. If so, remove it. */ - { - char *temp; - - temp = strrchr (basename, '.'); - if (temp) - *temp = 0; - } -#endif /* REMOVE_OUTPUT_EXTENSIONS */ - return (basename); -} - -/* Return the pathname part of filename. This can be NULL. */ -char * -pathname_part (filename) - char *filename; -{ - char *expand_filename (); - char *result = (char *) NULL; - register int i; - - filename = expand_filename (filename, ""); - - i = strlen (filename) - 1; - - while (i && filename[i] != '/') - i--; - if (filename[i] == '/') - i++; - - if (i) - { - result = (char *)xmalloc (1 + i); - strncpy (result, filename, i); - result[i] = 0; - } - free (filename); - return (result); -} - -char * -filename_non_directory (name) - char *name; -{ - register int i; - - for (i = strlen (name) - 1; i; i--) - if (name[i] == '/') - return (xstrdup (name + i + 1)); - - return (xstrdup (name)); -} - -/* Return the expansion of FILENAME. */ -char * -expand_filename (filename, input_name) - char *filename, *input_name; -{ - register int i; - char *full_pathname (); - - if (filename) - filename = full_pathname (filename); - else - { - filename = filename_non_directory (input_name); - - if (!*filename) - { - free (filename); - filename = xstrdup ("noname.texi"); - } - - for (i = strlen (filename) - 1; i; i--) - if (filename[i] == '.') - break; - - if (!i) - i = strlen (filename); - - if (i + 6 > (strlen (filename))) - filename = (char *)xrealloc (filename, i + 6); - strcpy (filename + i, ".info"); - return (filename); - } - - if (filename[0] == '.' || filename[0] == '/') - return (filename); - - if (filename[0] != '/' && input_name[0] == '/') - { - /* Make it so that relative names work. */ - char *result; - - i = strlen (input_name) - 1; - - result = (char *)xmalloc (1 + strlen (input_name) + strlen (filename)); - strcpy (result, input_name); - - while (result[i] != '/' && i) - i--; - - if (result[i] == '/') - i++; - - strcpy (&result[i], filename); - free (filename); - return (result); - } - return (filename); -} - -/* Return the full path to FILENAME. */ -char * -full_pathname (filename) - char *filename; -{ - int initial_character; - char *result; - - /* No filename given? */ - if (!filename || !(initial_character = *filename)) - return (xstrdup ("")); - - /* Already absolute? */ - if ((initial_character == '/') || - ((strncmp (filename, "./", 2) == 0) || - (strncmp (filename, "../", 3) == 0))) - return (xstrdup (filename)); - - if (initial_character != '~') - { - char *localdir; - - localdir = (char *)xmalloc (1025); -#if defined (HAVE_GETCWD) - if (!getcwd (localdir, 1024)) -#else /* !HAVE_GETCWD */ - if (!getwd (localdir)) -#endif /* !HAVE_GETCWD */ - { - fprintf (stderr, _("%s: getwd: %s, %s\n"), - progname, filename, localdir); - exit (1); - } - - strcat (localdir, "/"); - strcat (localdir, filename); - result = xstrdup (localdir); - free (localdir); - } - else - { -#ifndef WIN32 - if (filename[1] == '/') - { - /* Return the concatenation of the environment variable HOME - and the rest of the string. */ - char *temp_home; - - temp_home = (char *) getenv ("HOME"); - result = (char *)xmalloc (strlen (&filename[1]) - + 1 - + temp_home ? strlen (temp_home) - : 0); - *result = 0; - - if (temp_home) - strcpy (result, temp_home); - - strcat (result, &filename[1]); - } - else - { - struct passwd *user_entry; - int i, c; - char *username = (char *)xmalloc (257); - - for (i = 1; (c = filename[i]); i++) - { - if (c == '/') - break; - else - username[i - 1] = c; - } - if (c) - username[i - 1] = 0; - - user_entry = getpwnam (username); - - if (!user_entry) - return (xstrdup (filename)); - - result = (char *)xmalloc (1 + strlen (user_entry->pw_dir) - + strlen (&filename[i])); - strcpy (result, user_entry->pw_dir); - strcat (result, &filename[i]); - } - } -#endif /* not WIN32 */ - return (result); -} - -char * -output_name_from_input_name (name) - char *name; -{ - return (expand_filename ((char *)NULL, name)); -} - -/* **************************************************************** */ -/* */ -/* Hacking Tokens and Strings */ -/* */ -/* **************************************************************** */ +/* Hacking tokens and strings. */ /* Return the next token as a string pointer. We cons the string. */ char * @@ -1643,17 +705,17 @@ read_token () result = xstrdup (" "); *result = character; - return (result); + return result; } - for (i = 0; ((input_text_offset != size_of_input_text) + for (i = 0; ((input_text_offset != input_text_length) && (character = curchar ()) && command_char (character)); i++, input_text_offset++); - result = (char *)xmalloc (i + 1); + result = xmalloc (i + 1); memcpy (result, &input_text[input_text_offset - i], i); result[i] = 0; - return (result); + return result; } /* Return nonzero if CHARACTER is self-delimiting. */ @@ -1663,7 +725,7 @@ self_delimiting (character) { /* @; and @\ are not Texinfo commands, but they are listed here anyway. I don't know why. --karl, 10aug96. */ - return member (character, "~{|}`^\\@?=;:.-,*\'\" !\n\t"); + return strchr ("~{|}`^\\@?=;:.-,*\'\" !\n\t", character) != NULL; } /* Clear whitespace from the front and end of string. */ @@ -1698,7 +760,7 @@ void fix_whitespace (string) char *string; { - char *temp = (char *)xmalloc (strlen (string) + 1); + char *temp = xmalloc (strlen (string) + 1); int string_index = 0; int temp_index = 0; int c; @@ -1731,7 +793,7 @@ discard_until (string) { int temp = search_forward (string, input_text_offset); - int tt = (temp < 0) ? size_of_input_text : temp + strlen (string); + int tt = (temp < 0) ? input_text_length : temp + strlen (string); int from = input_text_offset; /* Find out what line we are on. */ @@ -1741,7 +803,7 @@ discard_until (string) if (temp < 0) { - input_text_offset = size_of_input_text - strlen (string); + input_text_offset = input_text_length - strlen (string); if (strcmp (string, "\n") != 0) { @@ -1769,7 +831,7 @@ get_until (match, string) new_point = search_forward (match, input_text_offset); if (new_point < 0) - new_point = size_of_input_text; + new_point = input_text_length; len = new_point - current_point; /* Keep track of which line number we are at. */ @@ -1778,7 +840,7 @@ get_until (match, string) if (input_text[x++] == '\n') line_number++; - *string = (char *)xmalloc (len + 1); + *string = xmalloc (len + 1); memcpy (*string, &input_text[current_point], len); (*string)[len] = 0; @@ -1786,86 +848,144 @@ get_until (match, string) /* Now leave input_text_offset in a consistent state. */ input_text_offset = tem; - if (input_text_offset > size_of_input_text) - input_text_offset = size_of_input_text; + if (input_text_offset > input_text_length) + input_text_offset = input_text_length; - return (new_point); + return new_point; +} + +/* Replace input_text[FROM .. TO] with its expansion. */ +void +replace_with_expansion (from, to) + int from, *to; +{ + char *xp; + unsigned xp_len, new_len; + char *old_input = input_text; + unsigned raw_len = *to - from; + char *str; + + /* The rest of the code here moves large buffers, so let's + not waste time if the input cannot possibly expand + into anything. Unfortunately, we cannot avoid expansion + when we see things like @code etc., even if they only + asked for expansion of macros, since any Texinfo command + can be potentially redefined with a macro. */ + if (only_macro_expansion && + memchr (input_text + from, COMMAND_PREFIX, raw_len) == 0) + return; + + /* Get original string from input. */ + str = xmalloc (raw_len + 1); + memcpy (str, input_text + from, raw_len); + str[raw_len] = 0; + + /* We are going to relocate input_text, so we had better output + pending portion of input_text now, before the pointer changes. */ + if (macro_expansion_output_stream && !executing_string + && !me_inhibit_expansion) + append_to_expansion_output (from); + + /* Expand it. */ + xp = expansion (str, 0); + xp_len = strlen (xp); + free (str); + + /* Plunk the expansion into the middle of `input_text' -- + which is terminated by a newline, not a null. Avoid + expensive move of the rest of the input if the expansion + has the same length as the original string. */ + if (xp_len != raw_len) + { + new_len = from + xp_len + input_text_length - *to + 1; + if (executing_string) + { /* If we are in execute_string, we might need to update + the relevant element in the execution_strings[] array, + since it could have to be relocated from under our + feet. (input_text is reallocated here as well, if needed.) */ + maybe_update_execution_strings (&input_text, new_len); + } + else if (new_len > input_text_length + 1) + /* Don't bother to realloc if we have enough space. */ + input_text = xrealloc (input_text, new_len); + + memmove (input_text + from + xp_len, + input_text + *to, input_text_length - *to + 1); + + *to += xp_len - raw_len; + /* Since we change input_text_length here, the comparison above + isn't really valid, but it seems the worst that might happen is + an extra xrealloc or two, so let's not worry. */ + input_text_length += xp_len - raw_len; + } + memcpy (input_text + from, xp, xp_len); + free (xp); + + /* Synchronize the macro-expansion pointers with our new input_text. */ + if (input_text != old_input) + forget_itext (old_input); + if (macro_expansion_output_stream && !executing_string) + remember_itext (input_text, from); } /* Read characters from the file until we are at MATCH or end of line. - Place the characters read into STRING. */ + Place the characters read into STRING. If EXPAND is nonzero, + expand the text before looking for MATCH for those cases where + MATCH might be produced by some macro. */ void get_until_in_line (expand, match, string) int expand; char *match, **string; { - int real_bottom = size_of_input_text; + int real_bottom = input_text_length; int limit = search_forward ("\n", input_text_offset); if (limit < 0) - limit = size_of_input_text; + limit = input_text_length; - /* Replace input_text[input_text_offset .. limit-1] with its macro - expansion (actually, we expand all commands). This allows the node - names themselves to be constructed via a macro, as in: + /* Replace input_text[input_text_offset .. limit-1] with its expansion. + This allows the node names and menu entries themselves to be + constructed via a macro, as in: @macro foo{p, q} Together: \p\ & \q\. @end macro @node @foo{A,B}, next, prev, top - + Otherwise, the `,' separating the macro args A and B is taken as the node argument separator, so the node name is `@foo{A'. This expansion is only necessary on the first call, since we expand the - whole line then. - - Furthermore, if we're executing a string, don't do it -- we'll end - up shrinking the execution string which is currently aliased to - `input_text', so it might get moved, and not updated in the - `execution_strings' array. This happens when processing the - (synthetic) Overview-Footnotes node in the Texinfo manual. */ - - if (expand && !executing_string && !me_executing_string) + whole line then. */ + if (expand) { - char *xp; - unsigned xp_len, new_len; - - /* Get original string from input. */ - unsigned raw_len = limit - input_text_offset; - char *str = xmalloc (raw_len + 1); - strncpy (str, input_text + input_text_offset, raw_len); - str[raw_len] = 0; - - /* Expand it. */ - xp = expansion (str, 0); - xp_len = strlen (xp); - free (str); - - /* Plunk the expansion into the middle of `input_text' -- - which is terminated by a newline, not a null. */ - str = xmalloc (real_bottom - limit + 1); - strncpy (str, input_text + limit, real_bottom - limit + 1); - new_len = input_text_offset + xp_len + real_bottom - limit + 1; - input_text = xrealloc (input_text, new_len); - strcpy (input_text + input_text_offset, xp); - strncpy (input_text + input_text_offset + xp_len, str, - real_bottom - limit + 1); - free (str); - free (xp); - - limit += xp_len - raw_len; - real_bottom += xp_len - raw_len; + replace_with_expansion (input_text_offset, &limit); } - size_of_input_text = limit; + real_bottom = input_text_length; + input_text_length = limit; get_until (match, string); - size_of_input_text = real_bottom; + input_text_length = real_bottom; } void -get_rest_of_line (string) +get_rest_of_line (expand, string) + int expand; char **string; { - get_until ("\n", string); + if (expand) + { + char *tem; + + /* Don't expand non-macros in input, since we want them + intact in the macro-expanded output. */ + only_macro_expansion++; + get_until_in_line (1, "\n", &tem); + only_macro_expansion--; + *string = expansion (tem, 0); + free (tem); + } + else + get_until_in_line (0, "\n", string); + canon_white (*string); if (curchar () == '\n') /* as opposed to the end of the file... */ @@ -1898,7 +1018,7 @@ get_until_in_braces (match, string) int i, brace = 0; int match_len = strlen (match); - for (i = input_text_offset; i < size_of_input_text; i++) + for (i = input_text_offset; i < input_text_length; i++) { if (input_text[i] == '{') brace++; @@ -1913,27 +1033,27 @@ get_until_in_braces (match, string) } match_len = i - input_text_offset; - temp = (char *)xmalloc (2 + match_len); - strncpy (temp, input_text + input_text_offset, match_len); + temp = xmalloc (2 + match_len); + memcpy (temp, input_text + input_text_offset, match_len); temp[match_len] = 0; input_text_offset = i; *string = temp; } -/* **************************************************************** */ -/* */ -/* Converting the File */ -/* */ -/* **************************************************************** */ +/* Converting a file. */ /* Convert the file named by NAME. The output is saved on the file named as the argument to the @setfilename command. */ static char *suffixes[] = { + /* ".txi" is checked first so that on 8+3 DOS filesystems, if they + have "texinfo.txi" and "texinfo.tex" in the same directory, the + former is used rather than the latter, due to file name truncation. */ + ".txi", ".texinfo", ".texi", ".txinfo", "", - (char *)NULL + NULL }; void @@ -1951,6 +1071,30 @@ initialize_conversion () output_position = 0; } +typedef struct generic_list { + struct generic_list *next; +} GENERIC_LIST; + +/* Reverse the chain of structures in LIST. Output the new head + of the chain. You should always assign the output value of this + function to something, or you will lose the chain. */ +GENERIC_LIST * +reverse_list (list) + GENERIC_LIST *list; +{ + GENERIC_LIST *next; + GENERIC_LIST *prev = NULL; + + while (list) + { + next = list->next; + list->next = prev; + prev = list; + list = next; + } + return prev; +} + /* We read in multiples of 4k, simply because it is a typical pipe size on unix systems. */ #define READ_BUFFER_GROWTH (4 * 4096) @@ -1962,7 +1106,7 @@ convert_from_stream (stream, name) FILE *stream; char *name; { - char *buffer = (char *)NULL; + char *buffer = NULL; int buffer_offset = 0, buffer_size = 0; initialize_conversion (); @@ -1983,7 +1127,7 @@ convert_from_stream (stream, name) if (count < 0) { perror (name); - exit (FATAL); + xexit (1); } buffer_offset += count; @@ -1993,7 +1137,7 @@ convert_from_stream (stream, name) /* Set the globals to the new file. */ input_text = buffer; - size_of_input_text = buffer_offset; + input_text_length = buffer_offset; input_filename = xstrdup (name); node_filename = xstrdup (name); input_text_offset = 0; @@ -2001,8 +1145,8 @@ convert_from_stream (stream, name) /* Not strictly necessary. This magic prevents read_token () from doing extra unnecessary work each time it is called (that is a lot of times). - The SIZE_OF_INPUT_TEXT is one past the actual end of the text. */ - input_text[size_of_input_text] = '\n'; + The INPUT_TEXT_LENGTH is one past the actual end of the text. */ + input_text[input_text_length] = '\n'; convert_from_loaded_file (name); } @@ -2011,8 +1155,8 @@ void convert_from_file (name) char *name; { - register int i; - char *filename = (char *)xmalloc (strlen (name) + 50); + int i; + char *filename = xmalloc (strlen (name) + 50); initialize_conversion (); @@ -2046,29 +1190,32 @@ convert_from_file (name) convert_from_loaded_file (name); } - + void convert_from_loaded_file (name) char *name; { - char *expand_filename (), *filename_part (); - char *real_output_filename = (char *)NULL; + char *real_output_filename = NULL; -#if defined (HAVE_MACROS) remember_itext (input_text, 0); -#endif /* HAVE_MACROS */ + + input_text_offset = 0; + + /* Avoid the `\input texinfo' line in HTML output (assuming it starts + the file). */ + if (looking_at ("\\input")) + discard_until ("\n"); /* Search this file looking for the special string which starts conversion. Once found, we may truly begin. */ - input_text_offset = 0; while (input_text_offset >= 0) { input_text_offset = search_forward (setfilename_search, input_text_offset); - if ((input_text_offset == 0) || - ((input_text_offset > 0) && - (input_text[input_text_offset -1] == '\n'))) + if (input_text_offset == 0 + || (input_text_offset > 0 + && input_text[input_text_offset -1] == '\n')) break; else if (input_text_offset > 0) input_text_offset++; @@ -2082,35 +1229,62 @@ convert_from_loaded_file (name) error (_("No `%s' found in `%s'"), setfilename_search, name); goto finished; #else - register int i, end_of_first_line; - - /* Find the end of the first line in the file. */ - for (i = 0; i < size_of_input_text - 1; i++) - if (input_text[i] == '\n') - break; - - end_of_first_line = i + 1; - - input_text_offset = 0; - - for (i = 0; i < end_of_first_line; i++) - { - if ((input_text[i] == '\\') && - (strncmp (input_text + i + 1, "include", 7) == 0)) - { - input_text_offset = end_of_first_line; - break; - } - } command_output_filename = output_name_from_input_name (name); #endif /* !REQUIRE_SETFILENAME */ } + + { + int i, end_of_first_line; + + /* Find the end of the first line in the file. */ + for (i = 0; i < input_text_length - 1; i++) + if (input_text[i] == '\n') + break; + + end_of_first_line = i + 1; + + for (i = 0; i < end_of_first_line; i++) + { + if ((input_text[i] == '\\') && + (strncmp (input_text + i + 1, "input", 5) == 0)) + { + input_text_offset = i; + break; + } + } + } } else input_text_offset += strlen (setfilename_search); if (!command_output_filename) - get_until ("\n", &output_filename); + { + get_until ("\n", &output_filename); /* read rest of line */ + if (html) + { /* Change any extension to .html. */ + char *html_name, *directory_part, *basename_part, *temp; + + canon_white (output_filename); + directory_part = pathname_part (output_filename); + basename_part = filename_part (output_filename); + + /* Zap any existing extension. */ + temp = strrchr (basename_part, '.'); + if (temp) + *temp = 0; + + /* Construct new filename. */ + html_name = xmalloc (strlen (directory_part) + + strlen (basename_part) + 6); + strcpy (html_name, directory_part); + strcat (html_name, basename_part); + strcat (html_name, ".html"); + + /* Replace name from @setfilename with the html name. */ + free (output_filename); + output_filename = html_name; + } + } else { if (input_text_offset != -1) @@ -2119,7 +1293,7 @@ convert_from_loaded_file (name) input_text_offset = 0; real_output_filename = output_filename = command_output_filename; - command_output_filename = (char *)NULL; + command_output_filename = NULL; } canon_white (output_filename); @@ -2147,9 +1321,12 @@ convert_from_loaded_file (name) output_stream = fopen (real_output_filename, "w"); } - if (verbose_mode && output_stream != stdout) + set_current_output_filename (real_output_filename); + + if (verbose_mode) printf (_("Making %s file `%s' from `%s'.\n"), - no_headers ? "text" : "info", output_filename, input_filename); + no_headers ? "text" : (html ? "HTML" : "info"), + output_filename, input_filename); if (output_stream == NULL) { @@ -2176,12 +1353,10 @@ convert_from_loaded_file (name) line_number++; } - if (!no_headers) - { - add_word_args (_("This is Info file %s, produced by Makeinfo version %d.%d"), - output_filename, major_version, minor_version); - add_word_args (_(" from the input file %s.\n"), input_filename); - } + /* html fixxme: should output this as trailer on first page. */ + if (!no_headers && !html) + add_word_args (_("This is %s, produced by makeinfo version %s from %s.\n"), + output_filename, VERSION, input_filename); close_paragraph (); reader_loop (); @@ -2191,13 +1366,13 @@ convert_from_loaded_file (name) close_paragraph (); flush_file_stack (); -#if defined (HAVE_MACROS) if (macro_expansion_output_stream) { fclose (macro_expansion_output_stream); if (errors_printed && !force && strcmp (macro_expansion_filename, "-") != 0 - && strcmp (macro_expansion_filename, "/dev/null") != 0) + && FILENAME_CMP (macro_expansion_filename, NULL_DEVICE) != 0 + && FILENAME_CMP (macro_expansion_filename, ALSO_NULL_DEVICE) != 0) { fprintf (stderr, _("%s: Removing macro output file `%s' due to errors; use --force to preserve.\n"), progname, macro_expansion_filename); @@ -2205,19 +1380,24 @@ convert_from_loaded_file (name) perror (macro_expansion_filename); } } -#endif /* HAVE_MACROS */ if (output_stream) { output_pending_notes (); - free_pending_notes (); - if (tag_table != NULL) + if (tag_table) { tag_table = (TAG_ENTRY *) reverse_list (tag_table); - if (!no_headers) + if (!no_headers && !html) write_tag_table (); } + if (html) + { + start_paragraph (); + add_word ("\n"); + close_paragraph (); + } + if (output_stream != stdout) fclose (output_stream); @@ -2225,11 +1405,17 @@ convert_from_loaded_file (name) if (validating) validate_file (tag_table); - if (splitting && (!errors_printed || force)) + /* If we need to output the table of contents, do it now. */ + if (contents_filename || shortcontents_filename) + toc_update (); + + if (splitting && !html && (!errors_printed || force)) split_file (real_output_filename, 0); - else if (errors_printed && !force + else if (errors_printed + && !force && strcmp (real_output_filename, "-") != 0 - && strcmp (real_output_filename, "/dev/null") != 0) + && FILENAME_CMP (real_output_filename, NULL_DEVICE) != 0 + && FILENAME_CMP (real_output_filename, ALSO_NULL_DEVICE) != 0) { /* If there were errors, and no --force, remove the output. */ fprintf (stderr, _("%s: Removing output file `%s' due to errors; use --force to preserve.\n"), progname, real_output_filename); @@ -2247,7 +1433,7 @@ free_and_clear (pointer) if (*pointer) { free (*pointer); - *pointer = (char *) NULL; + *pointer = NULL; } } @@ -2259,6 +1445,8 @@ init_internals () free_and_clear (&command); free_and_clear (&input_filename); free_node_references (); + free_node_node_references (); + toc_free (); init_insertion_stack (); init_brace_stack (); current_node = NULL; /* sometimes already freed */ @@ -2267,171 +1455,140 @@ init_internals () in_detailmenu = 0; top_node_seen = 0; non_top_node_seen = 0; + node_number = -1; } void init_paragraph () { free_and_clear (&output_paragraph); - output_paragraph = (unsigned char *)xmalloc (paragraph_buffer_len); + output_paragraph = xmalloc (paragraph_buffer_len); output_paragraph[0] = 0; output_paragraph_offset = 0; output_column = 0; paragraph_is_open = 0; current_indent = 0; + meta_char_pos = 0; } + +/* This is called from `reader_loop' when we are at the * beginning a + menu line. */ -/* Okay, we are ready to start the conversion. Call the reader on - some text, and fill the text as it is output. Handle commands by - remembering things like open braces and the current file position on a - stack, and when the corresponding close brace is found, you can call - the function with the proper arguments. */ -void -reader_loop () +static void +handle_menu_entry () { - int character; - int done = 0; - int dash_count = 0; + char *tem; + + /* Ugh, glean_node_from_menu wants to read the * itself. */ + input_text_offset--; + + /* Find node name in menu entry and save it in references list for + later validation. Use followed_reference type for detailmenu + references since we don't want to use them for default node pointers. */ + tem = glean_node_from_menu (1, in_detailmenu + ? followed_reference : menu_reference); - while (!done) - { - if (input_text_offset >= size_of_input_text) - break; + if (html && tem) + { /* Start a menu item with the cleaned-up line. Put an anchor + around the start text (before `:' or the node name). */ + char *string; - character = curchar (); + discard_until ("* "); - if (!in_fixed_width_font && - (character == '\'' || character == '`') && - input_text[input_text_offset + 1] == character) + /* The line number was already incremented in reader_loop when we + saw the newline, and discard_until has now incremented again. */ + line_number--; + + if (had_menu_commentary) { - input_text_offset++; - character = '"'; + add_word ("
    \n"); + had_menu_commentary = 0; + in_paragraph = 0; + } + else if (!in_paragraph && !paragraph_is_open) + { + add_word ("

    \n"); + in_paragraph = 1; + } + + if (in_paragraph) + { + add_word ("

    "); + in_paragraph = 0; } - if (character == '-') - { - dash_count++; - if (dash_count == 2 && !in_fixed_width_font) - { - input_text_offset++; - continue; - } - } + add_word ("
  • "); + free (string); + + /* The menu item may use macros, so expand them now. */ + only_macro_expansion++; + get_until_in_line (1, ":", &string); + only_macro_expansion--; + execute_string ("%s", string); /* get escaping done */ + free (string); + + add_word (""); + + if (looking_at ("::")) + discard_until (":"); else - { - dash_count = 0; - } - - /* If this is a whitespace character, then check to see if the line - is blank. If so, advance to the carriage return. */ - if (whitespace (character)) - { - register int i = input_text_offset + 1; - - while (i < size_of_input_text && whitespace (input_text[i])) - i++; - - if (i == size_of_input_text || input_text[i] == '\n') - { - if (i == size_of_input_text) - i--; - - input_text_offset = i; - character = curchar (); - } - } - - if (character == '\n') - { - line_number++; - - /* Check for a menu entry here, since the "escape sequence" - that begins menu entries is "\n* ". */ - if (in_menu && input_text_offset + 1 < size_of_input_text) - { - char *glean_node_from_menu (), *tem; - - /* Note that the value of TEM is discarded, since it is - gauranteed to be NULL when glean_node_from_menu () is - called with a Nonzero argument. */ - if (!in_detailmenu) - tem = glean_node_from_menu (1); - } - } - - switch (character) - { - case COMMAND_PREFIX: - read_command (); - break; - - case '{': - /* Special case. I'm not supposed to see this character by itself. - If I do, it means there is a syntax error in the input text. - Report the error here, but remember this brace on the stack so - you can ignore its partner. */ - - line_error (_("Misplaced %c"), '{'); - remember_brace (misplaced_brace); - - /* Don't advance input_text_offset since this happens in - remember_brace (). - input_text_offset++; - */ - break; - - case '}': - pop_and_call_brace (); - input_text_offset++; - break; - - default: - add_char (character); - input_text_offset++; + { /* discard the node name */ + get_until_in_line (0, ".", &string); + free (string); } + input_text_offset++; /* discard the second colon or the period */ + add_word (": "); + } + else if (tem) + { /* For Info output, we can just use the input and the main case in + reader_loop where we output what comes in. Just move off the * + so the next time through reader_loop we don't end up back here. */ + add_char ('*'); + input_text_offset += 2; /* undo the pointer back-up above. */ } -#if defined (HAVE_MACROS) - if (macro_expansion_output_stream) - maybe_write_itext (input_text, input_text_offset); -#endif /* HAVE_MACROS */ -} -/* Find the command corresponding to STRING. If the command - is found, return a pointer to the data structure. Otherwise - return (-1). */ -COMMAND * + if (tem) + free (tem); +} + +/* Find the command corresponding to STRING. If the command is found, + return a pointer to the data structure. Otherwise return -1. */ +static COMMAND * get_command_entry (string) char *string; { - register int i; + int i; for (i = 0; command_table[i].name; i++) if (strcmp (command_table[i].name, string) == 0) - return (&command_table[i]); + return &command_table[i]; /* This command is not in our predefined command table. Perhaps it is a user defined command. */ for (i = 0; i < user_command_array_len; i++) if (user_command_array[i] && (strcmp (user_command_array[i]->name, string) == 0)) - return (user_command_array[i]); + return user_command_array[i]; /* We never heard of this command. */ - return ((COMMAND *) -1); + return (COMMAND *) -1; } - + /* input_text_offset is right at the command prefix character. - Read the next token to determine what to do. */ -void + Read the next token to determine what to do. Return zero + if there's no known command or macro after the prefix character. */ +static int read_command () { COMMAND *entry; + int old_text_offset = input_text_offset++; - input_text_offset++; free_and_clear (&command); command = read_token (); -#if defined (HAVE_MACROS) /* Check to see if this command is a macro. If so, execute it here. */ { MACRO_DEF *def; @@ -2450,41 +1607,227 @@ read_command () if (!(def->flags & ME_RECURSE)) def->inhibited = 0; - return; + return 1; } } -#endif /* HAVE_MACROS */ + + if (only_macro_expansion) + { + /* Back up to the place where we were called, so the + caller will have a chance to process this non-macro. */ + input_text_offset = old_text_offset; + return 0; + } + + /* Perform alias expansion */ + command = alias_expand (command); + + if (enclosure_command (command)) + { + remember_brace (enclosure_expand); + enclosure_expand (START, output_paragraph_offset, 0); + return 0; + } entry = get_command_entry (command); if (entry == (COMMAND *)-1) { line_error (_("Unknown command `%s'"), command); - return; + return 0; } - if (entry->argument_in_braces) + if (entry->argument_in_braces == BRACE_ARGS) remember_brace (entry->proc); + else if (entry->argument_in_braces == MAYBE_BRACE_ARGS) + { + if (curchar () == '{') + remember_brace (entry->proc); + else + { /* No braces, so arg is next char. */ + int ch; + int saved_offset = output_paragraph_offset; + (*(entry->proc)) (START, output_paragraph_offset, 0); + /* Possibilities left for the next character: @ (error), } + (error), whitespace (skip) anything else (normal char). */ + skip_whitespace (); + ch = curchar (); + if (ch == '@') + { + line_error (_("Use braces to give a command as an argument to @%s"), + entry->name); + return 0; + } + else if (ch == '}') + { + /* Our caller will give the error message, because this } + won't match anything. */ + return 0; + } + + add_char (ch); + input_text_offset++; + (*(entry->proc)) (END, saved_offset, output_paragraph_offset); + return 1; + } + } + + /* Get here if we have BRACE_ARGS, NO_BRACE_ARGS, or MAYBE_BRACE_ARGS + with braces. */ (*(entry->proc)) (START, output_paragraph_offset, 0); + return 1; } -/* Return the string which invokes PROC; a pointer to a function. */ -char * -find_proc_name (proc) - COMMAND_FUNCTION *proc; +/* Okay, we are ready to start the conversion. Call the reader on + some text, and fill the text as it is output. Handle commands by + remembering things like open braces and the current file position on a + stack, and when the corresponding close brace is found, you can call + the function with the proper arguments. Although the filling isn't + necessary for HTML, it should do no harm. */ +void +reader_loop () { - register int i; + int character; + int done = 0; + int dash_count = 0; - for (i = 0; command_table[i].name; i++) - if (proc == command_table[i].proc) - return command_table[i].name; - return _("NO_NAME!"); + while (!done) + { + if (input_text_offset >= input_text_length) + break; + + character = curchar (); + + /* If only_macro_expansion, only handle macros and leave + everything else intact. */ + if (!only_macro_expansion && !in_fixed_width_font + && (character == '\'' || character == '`') + && input_text[input_text_offset + 1] == character) + { + input_text_offset++; + character = '"'; /* html fixxme */ + } + + /* Convert --- to --. */ + if (!only_macro_expansion && character == '-') + { + dash_count++; + if (dash_count == 2 && !in_fixed_width_font) + { + input_text_offset++; + continue; + } + } + else if (dash_count > 0) + dash_count = 0; + + /* If this is a whitespace character, then check to see if the line + is blank. If so, advance to the carriage return. */ + if (!only_macro_expansion && whitespace (character)) + { + int i = input_text_offset + 1; + + while (i < input_text_length && whitespace (input_text[i])) + i++; + + if (i == input_text_length || input_text[i] == '\n') + { + if (i == input_text_length) + i--; + + input_text_offset = i; + character = curchar (); + } + } + + if (character == '\n') + line_number++; + + switch (character) + { + case '*': /* perhaps we are at a menu */ + /* We used to check for this in the \n case but an @c in a + menu swallows its newline, so check here instead. */ + if (!only_macro_expansion && in_menu + && input_text_offset + 1 < input_text_length + && input_text[input_text_offset-1] == '\n') + handle_menu_entry (); + else + { /* Duplicate code from below, but not worth twisting the + fallthroughs to get down there. */ + add_char (character); + input_text_offset++; + } + break; + + /* Escapes for HTML unless we're outputting raw HTML. Do + this always, even if SGML rules don't require it since + that's easier and safer for non-conforming browsers. */ + case '&': + if (html && escape_html) + add_word ("&"); + else + add_char (character); + input_text_offset++; + break; + + case '<': + if (html && escape_html) + add_word ("<"); + else + add_char (character); + input_text_offset++; + break; + + case '>': + if (html && escape_html) + add_word (">"); + else + add_char (character); + input_text_offset++; + break; + + case COMMAND_PREFIX: /* @ */ + if (read_command () || !only_macro_expansion) + break; + + /* FALLTHROUGH (usually) */ + case '{': + /* Special case. We're not supposed to see this character by itself. + If we do, it means there is a syntax error in the input text. + Report the error here, but remember this brace on the stack so + we can ignore its partner. */ + if (!only_macro_expansion) + { + line_error (_("Misplaced %c"), '{'); + remember_brace (misplaced_brace); + /* remember_brace advances input_text_offset. */ + break; + } + + /* FALLTHROUGH (usually) */ + case '}': + if (!only_macro_expansion) + { + pop_and_call_brace (); + input_text_offset++; + break; + } + + /* FALLTHROUGH (usually) */ + default: + add_char (character); + input_text_offset++; + } + } + if (macro_expansion_output_stream && !only_macro_expansion) + maybe_write_itext (input_text, input_text_offset); } - + void init_brace_stack () { - brace_stack = (BRACE_ELEMENT *) NULL; + brace_stack = NULL; } void @@ -2505,9 +1848,10 @@ remember_brace_1 (proc, position) COMMAND_FUNCTION *proc; int position; { - BRACE_ELEMENT *new = (BRACE_ELEMENT *) xmalloc (sizeof (BRACE_ELEMENT)); + BRACE_ELEMENT *new = xmalloc (sizeof (BRACE_ELEMENT)); new->next = brace_stack; new->proc = proc; + new->command = xstrdup (command); new->pos = position; new->line = line_number; new->in_fixed_width_font = in_fixed_width_font; @@ -2519,24 +1863,29 @@ remember_brace_1 (proc, position) void pop_and_call_brace () { - BRACE_ELEMENT *temp; - COMMAND_FUNCTION *proc; - int pos; - - if (brace_stack == (BRACE_ELEMENT *) NULL) + if (brace_stack == NULL) { line_error (_("Unmatched }")); return; } - pos = brace_stack->pos; - proc = brace_stack->proc; - in_fixed_width_font = brace_stack->in_fixed_width_font; - temp = brace_stack->next; - free (brace_stack); - brace_stack = temp; + { + BRACE_ELEMENT *temp; - (*proc) (END, pos, output_paragraph_offset); + int pos = brace_stack->pos; + COMMAND_FUNCTION *proc = brace_stack->proc; + in_fixed_width_font = brace_stack->in_fixed_width_font; + + /* Reset current command, so the proc can know who it is. This is + used in cm_accent. */ + command = brace_stack->command; + + temp = brace_stack->next; + free (brace_stack); + brace_stack = temp; + + (*proc) (END, pos, output_paragraph_offset); + } } /* Shift all of the markers in `brace_stack' by AMOUNT. */ @@ -2544,7 +1893,7 @@ void adjust_braces_following (here, amount) int here, amount; { - register BRACE_ELEMENT *stack = brace_stack; + BRACE_ELEMENT *stack = brace_stack; while (stack) { @@ -2554,6 +1903,21 @@ adjust_braces_following (here, amount) } } +/* Return the string which invokes PROC; a pointer to a function. + Always returns the first function in the command table if more than + one matches PROC. */ +static char * +find_proc_name (proc) + COMMAND_FUNCTION *proc; +{ + int i; + + for (i = 0; command_table[i].name; i++) + if (proc == command_table[i].proc) + return command_table[i].name; + return _("NO_NAME!"); +} + /* You call discard_braces () when you shouldn't have any braces on the stack. I used to think that this happens for commands that don't take arguments in braces, but that was wrong because of things like @code{foo @@}. So now @@ -2618,9 +1982,9 @@ get_char_len (character) else len = 1; } - return (len); + return len; } - + void #if defined (VA_FPRINTF) && __STDC__ add_word_args (char *format, ...) @@ -2630,7 +1994,7 @@ add_word_args (format, va_alist) va_dcl #endif { - char buffer[1000]; + char buffer[2000]; /* xx no fixed limits */ #ifdef VA_FPRINTF va_list ap; #endif @@ -2639,7 +2003,7 @@ add_word_args (format, va_alist) #ifdef VA_SPRINTF VA_SPRINTF (buffer, format, ap); #else - sprintf (temp_string, format, a1, a2, a3, a4, a5, a6, a7, a8); + sprintf (buffer, format, a1, a2, a3, a4, a5, a6, a7, a8); #endif /* not VA_SPRINTF */ va_end (ap); add_word (buffer); @@ -2654,24 +2018,6 @@ add_word (string) add_char (*string++); } -/* Nonzero if the last character inserted has the syntax class of NEWLINE. */ -int last_char_was_newline = 1; - -/* The actual last inserted character. Note that this may be something - other than NEWLINE even if last_char_was_newline is 1. */ -int last_inserted_character = 0; - -/* Nonzero means that a newline character has already been - inserted, so close_paragraph () should insert one less. */ -int line_already_broken = 0; - -/* When nonzero we have finished an insertion (see `end_insertion') and we - want to ignore false continued paragraph closings. */ -int insertion_paragraph_closed = 0; - -/* Nonzero means attempt to make all of the lines have fill_column width. */ -int do_justification = 0; - /* Add the character to the current paragraph. If filling_enabled is nonzero, then do filling as well. */ void @@ -2679,8 +2025,10 @@ add_char (character) int character; { /* If we are avoiding outputting headers, and we are currently - in a menu, then simply return. */ - if (no_headers && (in_menu || in_detailmenu)) + in a menu, then simply return. But if we're only expanding macros, + then we're being called from glean_node_from_menu to try to + remember a menu reference, and we need that so we can do defaulting. */ + if (no_headers && !only_macro_expansion && (in_menu || in_detailmenu)) return; /* If we are adding a character now, then we don't have to @@ -2696,15 +2044,23 @@ add_char (character) } } - if (non_splitting_words && member (character, " \t\n")) - character = ' ' | 0x80; + if (non_splitting_words && strchr (" \t\n", character)) + { + if (html) + { /* Seems cleaner to use   than an 8-bit char. */ + add_word (" "); + character = ';'; + } + else + character = META (' '); /* unmeta-d in flush_output */ + } insertion_paragraph_closed = 0; switch (character) { case '\n': - if (!filling_enabled) + if (!filling_enabled && ! (html && (in_menu || in_detailmenu))) { insert ('\n'); @@ -2721,30 +2077,33 @@ add_char (character) indent (output_column = current_indent); break; } - else /* CHARACTER is newline, and filling is enabled. */ + else if (end_of_sentence_p ()) + /* CHARACTER is newline, and filling is enabled. */ { - if (end_of_sentence_p ()) - { - insert (' '); - output_column++; - last_inserted_character = character; - } + insert (' '); + output_column++; + last_inserted_character = character; } if (last_char_was_newline) { + if (html) + last_char_was_newline++; close_paragraph (); pending_indent = 0; } else { last_char_was_newline = 1; - insert (' '); + if (html) + insert ('\n'); + else + insert (' '); output_column++; } break; - default: + default: /* not at newline */ { int len = get_char_len (character); int suppress_insert = 0; @@ -2761,18 +2120,29 @@ add_char (character) if (!paragraph_is_open) { start_paragraph (); - - /* If the paragraph is supposed to be indented a certain way, - then discard all of the pending whitespace. Otherwise, we - let the whitespace stay. */ + /* If the paragraph is supposed to be indented a certain + way, then discard all of the pending whitespace. + Otherwise, we let the whitespace stay. */ if (!paragraph_start_indent) indent (pending_indent); pending_indent = 0; + + /* This horrible kludge of checking for a < prevents

    + from being inserted when we already have html markup + starting a paragraph, as with

      and

      and the like. */ + if (html && escape_html && character != '<' + && (!in_fixed_width_font || in_menu || in_detailmenu)) + { + insert_string ("

      "); + in_paragraph = 1; + adjust_braces_following (0, 3); /* adjust for

      */ + } } - if ((output_column += len) > fill_column) + output_column += len; + if (output_column > fill_column) { - if (filling_enabled) + if (filling_enabled && !html) { int temp = output_paragraph_offset; while (--temp > 0 && output_paragraph[temp] != '\n') @@ -2785,7 +2155,15 @@ add_char (character) while (temp && whitespace (output_paragraph[temp - 1])) temp--; - output_paragraph[temp++] = '\n'; + /* If we went back all the way to the newline of the + preceding line, it probably means that the word we + are adding is itself wider than the space that the + indentation and the fill_column let us use. In + that case, do NOT insert another newline, since it + won't help. Just indent to current_indent and + leave it alone, since that's the most we can do. */ + if (temp && output_paragraph[temp - 1] != '\n') + output_paragraph[temp++] = '\n'; /* We have correctly broken the line where we want to. What we don't want is spaces following where @@ -2817,11 +2195,11 @@ add_char (character) } /* Filled, but now indent if that is right. */ - if (indented_fill && current_indent) + if (indented_fill && current_indent > 0) { int buffer_len = ((output_paragraph_offset - temp) + current_indent); - char *temp_buffer = (char *)xmalloc (buffer_len); + char *temp_buffer = xmalloc (buffer_len); int indentation = 0; /* We have to shift any markers that are in @@ -2832,7 +2210,7 @@ add_char (character) indentation != current_indent) temp_buffer[indentation++] = ' '; - strncpy ((char *) &temp_buffer[current_indent], + memcpy ((char *) &temp_buffer[current_indent], (char *) &output_paragraph[temp], buffer_len - current_indent); @@ -2844,7 +2222,7 @@ add_char (character) (paragraph_buffer_len += buffer_len)); output_paragraph = tt; } - strncpy ((char *) &output_paragraph[temp], + memcpy ((char *) &output_paragraph[temp], temp_buffer, buffer_len); output_paragraph_offset += current_indent; free (temp_buffer); @@ -2871,11 +2249,27 @@ add_char (character) } } +/* Add a character and store its position in meta_char_pos. */ +void +add_meta_char (character) + int character; +{ + meta_char_pos = output_paragraph_offset; + add_char (character); +} + /* Insert CHARACTER into `output_paragraph'. */ void insert (character) int character; { + /* This is sad, but it seems desirable to not force any particular + order on the front matter commands. This way, the document can do + @settitle, @documentlanguage, etc, in any order and with any + omissions, and we'll still output the html `just in time'. */ + if (!executing_string && html && !html_output_head_p) + html_output_head (); + output_paragraph[output_paragraph_offset++] = character; if (output_paragraph_offset == paragraph_buffer_len) { @@ -2895,7 +2289,7 @@ insert_string (string) /* Sentences might have these characters after the period (or whatever). */ -#define post_sentence(c) ((c) == ')' || (c) == '\'' || (c) == '"' \ +#define POST_SENTENCE(c) ((c) == ')' || (c) == '\'' || (c) == '"' \ || (c) == ']') /* Return true if at an end-of-sentence character, possibly followed by @@ -2904,9 +2298,19 @@ static int end_of_sentence_p () { int loc = output_paragraph_offset - 1; - while (loc > 0 && post_sentence (output_paragraph[loc])) + + /* If nothing has been output, don't check output_paragraph[-1]. */ + if (loc < 0) + return 0; + + /* A post-sentence character that is at meta_char_pos is not really + a post-sentence character; it was produced by a markup such as + @samp. We don't want the period inside @samp to be treated as a + sentence ender. */ + while (loc > 0 + && loc != meta_char_pos && POST_SENTENCE (output_paragraph[loc])) loc--; - return sentence_ender (output_paragraph[loc]); + return loc != meta_char_pos && sentence_ender (output_paragraph[loc]); } @@ -2959,16 +2363,19 @@ uninhibit_output_flushing () void flush_output () { - register int i; + int i; if (!output_paragraph_offset || flushing_ignored) return; for (i = 0; i < output_paragraph_offset; i++) { - /* If we turned on the 8th bit for a space - inside @w, turn it back off for output. */ - if (output_paragraph[i] & meta_character_bit) + /* If we turned on the 8th bit for a space inside @w, turn it + back off for output. This might be problematic, since the + 0x80 character may be used in 8-bit character sets. Sigh. + In any case, don't do this for HTML, since the nbsp character + is valid input and must be passed along to the browser. */ + if (!html && (output_paragraph[i] & meta_character_bit)) { int temp = UNMETA (output_paragraph[i]); if (temp == ' ') @@ -2980,6 +2387,7 @@ flush_output () output_position += output_paragraph_offset; output_paragraph_offset = 0; + meta_char_pos = 0; } /* How to close a paragraph controlling the number of lines between @@ -2990,6 +2398,16 @@ flush_output () 1 creates a single blank line between paragraphs. */ int paragraph_spacing = DEFAULT_PARAGRAPH_SPACING; +static void +close_paragraph_with_lines (lines) + int lines; +{ + int old_spacing = paragraph_spacing; + paragraph_spacing = lines; + close_paragraph (); + paragraph_spacing = old_spacing; +} + /* Close the current paragraph, leaving no blank lines between them. */ void close_single_paragraph () @@ -3033,28 +2451,18 @@ close_insertion_paragraph () insertion_paragraph_closed = 1; } -void -close_paragraph_with_lines (lines) - int lines; -{ - int old_spacing = paragraph_spacing; - paragraph_spacing = lines; - close_paragraph (); - paragraph_spacing = old_spacing; -} - /* Close the currently open paragraph. */ void close_paragraph () { - register int i; + int i; /* The insertion paragraph is no longer closed. */ insertion_paragraph_closed = 0; if (paragraph_is_open && !must_start_paragraph) { - register int tindex, c; + int tindex, c; tindex = output_paragraph_offset; @@ -3083,7 +2491,13 @@ close_paragraph () if (!force_flush_right) { for (i = 0; i < (paragraph_spacing - line_already_broken); i++) - insert ('\n'); + { + insert ('\n'); + /* Don't need anything extra for HTML in usual case of no + extra paragraph spacing. */ + if (html && i > 0) + insert_string ("
      "); + } } /* If we are doing flush right indentation, then do it now @@ -3096,6 +2510,7 @@ close_paragraph () no_indent = 0; output_column = 0; } + ignore_blank_line (); } @@ -3122,7 +2537,7 @@ do_flush_right_indentation () if (output_paragraph_offset < fill_column) { - register int i; + int i; if (fill_column >= paragraph_buffer_len) output_paragraph = @@ -3130,7 +2545,7 @@ do_flush_right_indentation () (paragraph_buffer_len += fill_column)); temp_len = strlen ((char *)output_paragraph); - temp = (char *)xmalloc (temp_len + 1); + temp = xmalloc (temp_len + 1); memcpy (temp, (char *)output_paragraph, temp_len); for (i = 0; i < fill_column - output_paragraph_offset; i++) @@ -3139,6 +2554,7 @@ do_flush_right_indentation () memcpy ((char *)output_paragraph + i, temp, temp_len); free (temp); output_paragraph_offset = fill_column; + adjust_braces_following (0, i); } } } @@ -3195,16 +2611,12 @@ void indent (amount) int amount; { - register BRACE_ELEMENT *elt = brace_stack; + if (html) + return; /* For every START_POS saved within the brace stack which will be affected by this indentation, bump that start pos forward. */ - while (elt) - { - if (elt->pos >= output_paragraph_offset) - elt->pos += amount; - elt = elt->next; - } + adjust_braces_following (output_paragraph_offset, amount); while (--amount >= 0) insert (' '); @@ -3219,2492 +2631,62 @@ search_forward (string, from) { int len = strlen (string); - while (from < size_of_input_text) + while (from < input_text_length) { if (strncmp (input_text + from, string, len) == 0) - return (from); + return from; from++; } - return (-1); -} - -/* Whoops, Unix doesn't have strcasecmp. */ - -/* Case independent string compare. */ -#if !defined (HAVE_STRCASECMP) -int -strcasecmp (string1, string2) - char *string1, *string2; -{ - char ch1, ch2; - - for (;;) - { - ch1 = *string1++; - ch2 = *string2++; - - if (!(ch1 | ch2)) - return (0); - - ch1 = coerce_to_upper (ch1); - ch2 = coerce_to_upper (ch2); - - if (ch1 != ch2) - return (ch1 - ch2); - } -} -#endif /* !HAVE_STRCASECMP */ - -void -init_insertion_stack () -{ - insertion_stack = (INSERTION_ELT *) NULL; -} - -/* Return the type of the current insertion. */ -enum insertion_type -current_insertion_type () -{ - if (!insertion_level) - return (bad_type); - else - return (insertion_stack->insertion); -} - -/* Return a pointer to the string which is the function to wrap around - items. */ -char * -current_item_function () -{ - register int level, done; - register INSERTION_ELT *elt; - - level = insertion_level; - elt = insertion_stack; - done = 0; - - /* Skip down through the stack until we find a non-conditional insertion. */ - while (!done && (elt != NULL)) - { - switch (elt->insertion) - { - case ifinfo: - case ifnothtml: - case ifnottex: - case ifset: - case ifclear: - case cartouche: - elt = elt->next; - level--; - break; - - default: - done = 1; - } - } - - if (!level) - return ((char *) NULL); - else - return (elt->item_function); -} - -char * -get_item_function () -{ - char *item_function; - get_rest_of_line (&item_function); - backup_input_pointer (); - 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 = (INSERTION_ELT *) 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 == (INSERTION_ELT *) 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 (strcmp (name, insertion_type_names[index]) == 0) - return (enum insertion_type) index; - index++; - } - return (bad_type); -} - -int -defun_insertion (type) - enum insertion_type type; -{ - return - ((type == deffn) - || (type == defun) - || (type == defmac) - || (type == defspec) - || (type == defvr) - || (type == defvar) - || (type == defopt) - || (type == deftypefn) - || (type == deftypefun) - || (type == deftypevr) - || (type == deftypevar) - || (type == defcv) - || (type == defivar) - || (type == defop) - || (type == defmethod) - || (type == deftypemethod) - || (type == deftp)); -} - -/* 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 = (char *)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++; -} - -/* 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 (!no_headers) - add_word (_("* Menu:\n")); - - in_menu++; - 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_detailmenu++; - break; - - case direntry: - close_single_paragraph (); - filling_enabled = no_indent = 0; - inhibit_paragraph_indentation = 1; - insert_string ("START-INFO-DIR-ENTRY\n"); - break; - - /* I think @quotation is meant to do filling. - If you don't want filling, then use @display. */ - case quotation: - 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 example: - case smallexample: - case lisp: - case smalllisp: - /* Just like @example, but no indentation. */ - case format: - close_single_paragraph (); - inhibit_paragraph_indentation = 1; - in_fixed_width_font++; - filling_enabled = 0; - last_char_was_newline = 0; - if (type != format) - 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 (allow_lax_format && (type == itemize)) - { - if (!(*insertion_stack->item_function)) - { - free (insertion_stack->item_function); - insertion_stack->item_function = xstrdup ("@bullet"); - insertion_stack->item_function[0] = COMMAND_PREFIX; - } - } - - if (!*insertion_stack->item_function) - { - line_error (_("%s requires an argument: the formatter for %citem"), - insertion_type_pname (type), COMMAND_PREFIX); - } - 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 (isdigit (*enumeration_arg)) - start_enumerating (atoi (enumeration_arg), ENUM_DIGITS); - else - start_enumerating (*enumeration_arg, ENUM_ALPHA); - break; - - /* Does nothing special in makeinfo. */ - case group: - /* Only close the paragraph if we are not inside of an @example. */ - if (!insertion_stack->next || - insertion_stack->next->insertion != example) - close_single_paragraph (); - break; - - /* Insertions that are no-ops in info, but do something in TeX. */ - case ifinfo: - case ifnothtml: - case ifnottex: - case ifset: - case ifclear: - case cartouche: - if (in_menu) - no_discard++; - break; - - case deffn: - case defun: - case defmac: - case defspec: - case defvr: - case defvar: - case defopt: - case deftypefn: - case deftypefun: - case deftypevr: - case deftypevar: - case defcv: - case defivar: - case defop: - case defmethod: - case deftypemethod: - case deftp: - 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; - } - - 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 - (_("`%cend' expected `%s', but saw `%s'"), COMMAND_PREFIX, - insertion_type_pname (temp_type), insertion_type_pname (type)); - return; - } - - pop_insertion (); - - switch (type) - { - /* Insertions which have no effect on paragraph formatting. */ - case ifnothtml: - case ifnottex: - case ifinfo: - case ifset: - case ifclear: - break; - - case direntry: - 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 (!no_headers) - close_insertion_paragraph (); - break; - - case multitable: - end_multitable (); - break; - - case enumerate: - stop_enumerating (); - close_insertion_paragraph (); - current_indent -= default_indentation_increment; - break; - - case flushleft: - case group: - case cartouche: - close_insertion_paragraph (); - break; - - case format: - case display: - case example: - case smallexample: - case lisp: - case smalllisp: - case quotation: - /* @format is the only fixed_width insertion without a change - in indentation. */ - if (type != format) - current_indent -= default_indentation_increment; - - /* 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: - case itemize: - current_indent -= default_indentation_increment; - break; - - case flushright: - force_flush_right--; - close_insertion_paragraph (); - break; - - /* Handle the @defun style insertions with a default clause. */ - default: - current_indent -= default_indentation_increment; - close_insertion_paragraph (); - break; - } -} - -/* 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, allows unmatched - ifinfo, ifset, ifclear, otherwise not. */ -void -discard_insertions (specials_ok) - int specials_ok; -{ - int real_line_number = line_number; - while (insertion_stack) - { - if (specials_ok && (insertion_stack->insertion == ifinfo - || insertion_stack->insertion == ifset - || insertion_stack->insertion == ifclear)) - 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; + return -1; } -/* The Texinfo commands. */ - -/* Commands which insert their own names. */ -void -insert_self (arg) - int arg; -{ - if (arg == START) - add_word (command); -} - -void -insert_space (arg) - int arg; -{ - if (arg == START) - add_char (' '); -} - -/* Force a line break in the output. */ -void -cm_asterisk () -{ - close_single_paragraph (); - cm_noindent (); -} - -/* Insert ellipsis. */ -void -cm_dots (arg) - int arg; -{ - if (arg == START) - add_word ("..."); -} - -/* Insert ellipsis for sentence end. */ -void -cm_enddots (arg) - int arg; -{ - if (arg == START) - add_word ("...."); -} - -void -cm_bullet (arg) - int arg; -{ - if (arg == START) - add_char ('*'); -} - -void -cm_minus (arg) - int arg; -{ - if (arg == START) - add_char ('-'); -} - -/* Insert "TeX". */ -void -cm_TeX (arg) - int arg; -{ - if (arg == START) - add_word ("TeX"); -} - -/* Copyright symbol. */ -void -cm_copyright (arg) - int arg; -{ - if (arg == START) - add_word ("(C)"); -} - -/* Accent commands that take explicit arguments. */ -void -cm_accent (arg) - int arg; -{ - if (arg == START) - { - if (strcmp (command, "dotaccent") == 0) /* overdot */ - add_char ('.'); - else if (strcmp (command, "H") == 0) /* Hungarian umlaut */ - add_word ("''"); - else if (strcmp (command, "ringaccent") == 0) - add_char ('*'); - else if (strcmp (command, "tieaccent") == 0) - add_char ('['); - else if (strcmp (command, "u") == 0) /* breve */ - add_char ('('); - else if (strcmp (command, "v") == 0) /* hacek/check */ - add_char ('<'); - } - else if (arg == END) - { - if (strcmp (command, "ubaraccent") == 0) /* underbar */ - add_char ('_'); - else if (strcmp (command, "udotaccent") == 0) /* underdot */ - add_word ("-."); - else if (strcmp (command, ",") == 0) /* cedilla */ - add_word (","); - } -} - -/* Non-English letters/characters that don't insert themselves. */ -void -cm_special_char (arg) -{ - if (arg == START) - { - if ((*command == 'L' || *command == 'l' - || *command == 'O' || *command == 'o') - && command[1] == 0) - { - /* Lslash lslash Oslash oslash */ - add_char (*command); - add_char ('/'); - } - else if (strcmp (command, "exclamdown") == 0) - add_char ('!'); - else if (strcmp (command, "pounds") == 0) - add_char ('#'); - else if (strcmp (command, "questiondown") == 0) - add_char ('?'); - else - fprintf (stderr, _("How did @%s end up in cm_special_char?\n"), command); - } -} - -/* Dotless i or j. */ -void -cm_dotless (arg, start, end) - int arg, start, end; -{ - if (arg == END) - { - if (output_paragraph[start] != 'i' && output_paragraph[start] != 'j') - /* This error message isn't perfect if the argument is multiple - characters, but it doesn't seem worth getting right. */ - line_error (_("%c%s expects `i' or `j' as argument, not `%c'"), - COMMAND_PREFIX, command, output_paragraph[start]); - - else if (end - start != 1) - line_error (_("%c%s expects a single character `i' or `j' as argument"), - COMMAND_PREFIX, command); - - /* We've already inserted the `i' or `j', so nothing to do. */ - } -} - -void -cm_today (arg) - int arg; -{ - static char *months [12] = - { N_("January"), N_("February"), N_("March"), N_("April"), N_("May"), - N_("June"), N_("July"), N_("August"), N_("September"), N_("October"), - N_("November"), N_("December") }; - if (arg == START) - { - time_t timer = time (0); - struct tm *ts = localtime (&timer); - add_word_args ("%d %s %d", ts->tm_mday, _(months[ts->tm_mon]), - ts->tm_year + 1900); - } -} - -void -cm_code (arg) - int arg; -{ - extern int printing_index; - - if (arg == START) - { - in_fixed_width_font++; - - if (!printing_index) - add_char ('`'); - } - else - { - if (!printing_index) - add_char ('\''); - } -} - -void -cm_kbd (arg) - int arg; -{ - /* People use @kbd in an example to get the "user input" font. - We don't want quotes in that case. */ - if (!in_fixed_width_font) - cm_code (arg); -} - -void -cm_key (arg) - int arg; -{ - add_char (arg == START ? '<' : '>'); -} - -/* Convert the character at position into a true control character. */ -void -cm_ctrl (arg, start, end) - int arg, start, end; -{ - /* Should we allow multiple character arguments? I think yes. */ - if (arg == END) - { - register int i, character; -#if defined (NO_MULTIPLE_CTRL) - if ((end - start) != 1) - line_error (_("%c%s expects a single character as an argument"), - COMMAND_PREFIX, command); - else -#endif - for (i = start; i < end; i++) - { - character = output_paragraph[i]; - - if (isletter (character)) - output_paragraph[i] = CTL (coerce_to_upper (character)); - } - } -} - -/* Handle a command that switches to a non-fixed-width font. */ -void -not_fixed_width (arg) - int arg; -{ - if (arg == START) - in_fixed_width_font = 0; -} - -/* Small caps and @var in makeinfo just uppercase the text. */ -void -cm_var_sc (arg, start_pos, end_pos) - int arg, start_pos, end_pos; -{ - not_fixed_width (arg); - - if (arg == END) - { - while (start_pos < end_pos) - { - output_paragraph[start_pos] = - coerce_to_upper (output_paragraph[start_pos]); - start_pos++; - } - } -} - -void -cm_dfn (arg, position) - int arg, position; -{ - add_char ('"'); -} - -void -cm_emph (arg) - int arg; -{ - add_char ('*'); -} - -void -cm_strong (arg, position) - int arg, position; -{ - cm_emph (arg); -} - -void -cm_cite (arg, position) - int arg, position; -{ - if (arg == START) - add_word ("`"); - else - add_word ("'"); -} - -/* No highlighting, but argument switches fonts. */ -void -cm_not_fixed_width (arg, start, end) - int arg, start, end; -{ - not_fixed_width (arg); -} - -/* Various commands are no-op's. */ -void -cm_no_op () -{ -} - -/* No-op that eats its argument on same line. */ -void -cm_no_op_line_arg () -{ - char *temp; - get_rest_of_line (&temp); - free (temp); -} - -/* Prevent the argument from being split across two lines. */ -void -cm_w (arg, start, end) - int arg, start, end; -{ - if (arg == START) - non_splitting_words++; - else - non_splitting_words--; -} - - -/* Explain that this command is obsolete, thus the user shouldn't - do anything with it. */ -void -cm_obsolete (arg, start, end) - int arg, start, end; -{ - if (arg == START) - warning (_("%c%s is obsolete"), COMMAND_PREFIX, command); -} - -/* Insert the text following input_text_offset up to the end of the line - in a new, separate paragraph. Directly underneath it, insert a - line of WITH_CHAR, the same length of the inserted text. */ -void -insert_and_underscore (with_char) - int with_char; -{ - register int i, len; - int old_no_indent, starting_pos, ending_pos; - char *temp; - - close_paragraph (); - filling_enabled = indented_fill = 0; - old_no_indent = no_indent; - no_indent = 1; - -#if defined (HAVE_MACROS) - if (macro_expansion_output_stream && !executing_string) - append_to_expansion_output (input_text_offset + 1); -#endif /* HAVE_MACROS */ - - get_rest_of_line (&temp); - - starting_pos = output_position + output_paragraph_offset; -#if defined (HAVE_MACROS) - if (macro_expansion_output_stream && !executing_string) - { - char *temp1 = (char *) xmalloc (2 + strlen (temp)); - sprintf (temp1, "%s\n", temp); - remember_itext (input_text, input_text_offset); - me_execute_string (temp1); - free (temp1); - } - else -#endif /* HAVE_MACROS */ - execute_string ("%s\n", temp); - - ending_pos = output_position + output_paragraph_offset; - free (temp); - - len = (ending_pos - starting_pos) - 1; - for (i = 0; i < len; i++) - add_char (with_char); - insert ('\n'); - close_paragraph (); - filling_enabled = 1; - no_indent = old_no_indent; -} - -/* Here is a structure which associates sectioning commands with - an integer, hopefully to reflect the `depth' of the current - section. */ -struct { - char *name; - int level; -} section_alist[] = { - { "unnumberedsubsubsec", 5 }, - { "unnumberedsubsec", 4 }, - { "unnumberedsec", 3 }, - { "unnumbered", 2 }, - { "appendixsubsubsec", 5 }, - { "appendixsubsec", 4 }, - { "appendixsec", 3 }, - { "appendixsection", 3 }, - { "appendix", 2 }, - { "subsubsec", 5 }, - { "subsubsection", 5 }, - { "subsection", 4 }, - { "section", 3 }, - { "chapter", 2 }, - { "top", 1 }, - - { (char *)NULL, 0 } -}; - -/* Amount to offset the name of sectioning commands to levels by. */ -int section_alist_offset = 0; - -/* Shift the meaning of @section to @chapter. */ -void -cm_raisesections () -{ - discard_until ("\n"); - section_alist_offset--; -} - -/* Shift the meaning of @chapter to @section. */ -void -cm_lowersections () -{ - discard_until ("\n"); - section_alist_offset++; -} - -/* Return an integer which identifies the type section present in TEXT. */ -int -what_section (text) - char *text; -{ - register int i, j; - char *t; - - find_section_command: - for (j = 0; text[j] && cr_or_whitespace (text[j]); j++); - if (text[j] != COMMAND_PREFIX) - return (-1); - - text = text + j + 1; - - /* We skip @c, @comment, and @?index commands. */ - if ((strncmp (text, "comment", strlen ("comment")) == 0) || - (text[0] == 'c' && cr_or_whitespace (text[1])) || - (strcmp (text + 1, "index") == 0)) - { - while (*text++ != '\n'); - goto find_section_command; - } - - /* Handle italicized sectioning commands. */ - if (*text == 'i') - text++; - - for (j = 0; text[j] && !cr_or_whitespace (text[j]); j++); - - for (i = 0; (t = section_alist[i].name); i++) - { - if (j == strlen (t) && strncmp (t, text, j) == 0) - { - int return_val; - - return_val = (section_alist[i].level + section_alist_offset); - - if (return_val < 0) - return_val = 0; - else if (return_val > 5) - return_val = 5; - return (return_val); - } - } - return (-1); -} - -/* Set the level of @top to LEVEL. Return the old level of @top. */ -int -set_top_section_level (level) - int level; -{ - register int i, result = -1; - - for (i = 0; section_alist[i].name; i++) - if (strcmp (section_alist[i].name, "top") == 0) - { - result = section_alist[i].level; - section_alist[i].level = level; - break; - } - return (result); -} - -/* Treat this just like @unnumbered. The only difference is - in node defaulting. */ -void -cm_top () -{ - /* It is an error to have more than one @top. */ - if (top_node_seen) - { - TAG_ENTRY *tag = tag_table; - - line_error (_("Node with %ctop as a section already exists"), - COMMAND_PREFIX); - - while (tag != (TAG_ENTRY *)NULL) - { - if ((tag->flags & IS_TOP)) - { - int old_line_number = line_number; - char *old_input_filename = input_filename; - - line_number = tag->line_no; - input_filename = tag->filename; - line_error (_("Here is the %ctop node"), COMMAND_PREFIX); - input_filename = old_input_filename; - line_number = old_line_number; - return; - } - tag = tag->next_ent; - } - } - else - { - top_node_seen = 1; - - /* It is an error to use @top before you have used @node. */ - if (!tag_table) - { - char *top_name; - - get_rest_of_line (&top_name); - free (top_name); - line_error (_("%ctop used before %cnode, defaulting to %s"), - COMMAND_PREFIX, COMMAND_PREFIX, top_name); - execute_string ("@node Top, , (dir), (dir)\n@top %s\n", top_name); - return; - } - - cm_unnumbered (); - - /* The most recently defined node is the top node. */ - tag_table->flags |= IS_TOP; - - /* Now set the logical hierarchical level of the Top node. */ - { - int orig_offset = input_text_offset; - - input_text_offset = search_forward (node_search_string, orig_offset); - - if (input_text_offset > 0) - { - int this_section; - - /* We have encountered a non-top node, so mark that one exists. */ - non_top_node_seen = 1; - - /* Move to the end of this line, and find out what the - sectioning command is here. */ - while (input_text[input_text_offset] != '\n') - input_text_offset++; - - if (input_text_offset < size_of_input_text) - input_text_offset++; - - this_section = what_section (input_text + input_text_offset); - - /* If we found a sectioning command, then give the top section - a level of this section - 1. */ - if (this_section != -1) - set_top_section_level (this_section - 1); - } - input_text_offset = orig_offset; - } - } -} - -/* Organized by level commands. That is, "*" == chapter, "=" == section. */ -char *scoring_characters = "*=-."; - -void -sectioning_underscore (command) - char *command; -{ - char character; - char *temp; - int level; - - temp = (char *)xmalloc (2 + strlen (command)); - temp[0] = COMMAND_PREFIX; - strcpy (&temp[1], command); - level = what_section (temp); - free (temp); - level -= 2; - - if (level < 0) - level = 0; - - character = scoring_characters[level]; - - insert_and_underscore (character); -} - -/* The command still works, but prints a warning message in addition. */ -void -cm_ideprecated (arg, start, end) - int arg, start, end; -{ - warning (_("%c%s is obsolete; use %c%s instead"), - COMMAND_PREFIX, command, COMMAND_PREFIX, command + 1); - sectioning_underscore (command + 1); -} - -/* The remainder of the text on this line is a chapter heading. */ -void -cm_chapter () -{ - sectioning_underscore ("chapter"); -} - -/* The remainder of the text on this line is a section heading. */ -void -cm_section () -{ - sectioning_underscore ("section"); -} - -/* The remainder of the text on this line is a subsection heading. */ -void -cm_subsection () -{ - sectioning_underscore ("subsection"); -} - -/* The remainder of the text on this line is a subsubsection heading. */ -void -cm_subsubsection () -{ - sectioning_underscore ("subsubsection"); -} - -/* The remainder of the text on this line is an unnumbered heading. */ -void -cm_unnumbered () -{ - cm_chapter (); -} - -/* The remainder of the text on this line is an unnumbered section heading. */ -void -cm_unnumberedsec () -{ - cm_section (); -} - -/* The remainder of the text on this line is an unnumbered - subsection heading. */ -void -cm_unnumberedsubsec () -{ - cm_subsection (); -} - -/* The remainder of the text on this line is an unnumbered - subsubsection heading. */ -void -cm_unnumberedsubsubsec () -{ - cm_subsubsection (); -} - -/* The remainder of the text on this line is an appendix heading. */ -void -cm_appendix () -{ - cm_chapter (); -} - -/* The remainder of the text on this line is an appendix section heading. */ -void -cm_appendixsec () -{ - cm_section (); -} - -/* The remainder of the text on this line is an appendix subsection heading. */ -void -cm_appendixsubsec () -{ - cm_subsection (); -} - -/* The remainder of the text on this line is an appendix - subsubsection heading. */ -void -cm_appendixsubsubsec () -{ - cm_subsubsection (); -} - -/* Compatibility functions substitute for chapter, section, etc. */ -void -cm_majorheading () -{ - cm_chapheading (); -} - -void -cm_chapheading () -{ - cm_chapter (); -} - -void -cm_heading () -{ - cm_section (); -} - -void -cm_subheading () -{ - cm_subsection (); -} - -void -cm_subsubheading () -{ - cm_subsubsection (); -} - -/* **************************************************************** */ -/* */ -/* Adding nodes, and making tags */ -/* */ -/* **************************************************************** */ - -/* Start a new tag table. */ -void -init_tag_table () -{ - while (tag_table != (TAG_ENTRY *) NULL) - { - TAG_ENTRY *temp = tag_table; - free (temp->node); - free (temp->prev); - free (temp->next); - free (temp->up); - tag_table = tag_table->next_ent; - free (temp); - } -} - -void -write_tag_table () -{ - write_tag_table_internal (0); /* Not indirect. */ -} - -void -write_tag_table_indirect () -{ - write_tag_table_internal (1); -} - -/* Write out the contents of the existing tag table. - INDIRECT_P says how to format the output. */ -void -write_tag_table_internal (indirect_p) - int indirect_p; -{ - TAG_ENTRY *node = tag_table; - int old_indent = no_indent; - - no_indent = 1; - filling_enabled = 0; - must_start_paragraph = 0; - close_paragraph (); - - if (!indirect_p) - { - no_indent = 1; - insert ('\n'); - } - - add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : ""); - - while (node != (TAG_ENTRY *) NULL) - { - execute_string ("Node: %s", node->node); - add_word_args ("\177%d\n", node->position); - node = node->next_ent; - } - - add_word ("\037\nEnd Tag Table\n"); - flush_output (); - no_indent = old_indent; -} - -char * -get_node_token (expand) - int expand; -{ - char *string; - - get_until_in_line (expand, ",", &string); - - if (curchar () == ',') - input_text_offset++; - - canon_white (string); - - /* Force all versions of "top" to be "Top". */ - normalize_node_name (string); - - return (string); -} - -/* Convert "top" and friends into "Top". */ -void -normalize_node_name (string) - char *string; -{ - if (strcasecmp (string, "Top") == 0) - strcpy (string, "Top"); -} - -/* Look up NAME in the tag table, and return the associated - tag_entry. If the node is not in the table return NULL. */ -TAG_ENTRY * -find_node (name) - char *name; -{ - TAG_ENTRY *tag = tag_table; - - while (tag != (TAG_ENTRY *) NULL) - { - if (strcmp (tag->node, name) == 0) - return (tag); - tag = tag->next_ent; - } - return ((TAG_ENTRY *) NULL); -} - -/* Remember NODE and associates. */ -void -remember_node (node, prev, next, up, position, line_no, no_warn) - char *node, *prev, *next, *up; - int position, line_no, no_warn; -{ - /* Check for existence of this tag already. */ - if (validating) - { - register TAG_ENTRY *tag = find_node (node); - if (tag) - { - line_error ( - _("Node `%s' multiply defined (line %d is first definition at)"), - node, tag->line_no); - return; - } - } - - /* First, make this the current node. */ - current_node = node; - - /* Now add it to the list. */ - { - TAG_ENTRY *new = (TAG_ENTRY *) xmalloc (sizeof (TAG_ENTRY)); - new->node = node; - new->prev = prev; - new->next = next; - new->up = up; - new->position = position; - new->line_no = line_no; - new->filename = node_filename; - new->touched = 0; /* not yet referenced. */ - new->flags = 0; - if (no_warn) - new->flags |= NO_WARN; - new->next_ent = tag_table; - tag_table = new; - } -} - -/* The order is: nodename, nextnode, prevnode, upnode. - If all of the NEXT, PREV, and UP fields are empty, they are defaulted. - You must follow a node command which has those fields defaulted - with a sectioning command (e.g. @chapter) giving the "level" of that node. - It is an error not to do so. - The defaults come from the menu in this node's parent. */ -void -cm_node () -{ - char *node, *prev, *next, *up; - int new_node_pos, defaulting, this_section, no_warn = 0; - extern int already_outputting_pending_notes; - - if (strcmp (command, "nwnode") == 0) - no_warn = 1; - - /* Get rid of unmatched brace arguments from previous commands. */ - discard_braces (); - - /* There also might be insertions left lying around that haven't been - ended yet. Do that also. */ - discard_insertions (1); - - if (!already_outputting_pending_notes) - { - close_paragraph (); - output_pending_notes (); - free_pending_notes (); - } - - filling_enabled = indented_fill = 0; - new_node_pos = output_position; - current_footnote_number = 1; - -#if defined (HAVE_MACROS) - if (macro_expansion_output_stream && !executing_string) - append_to_expansion_output (input_text_offset + 1); -#endif /* HAVE_MACROS */ - - node = get_node_token (1); - next = get_node_token (0); - prev = get_node_token (0); - up = get_node_token (0); - - if (verbose_mode) - printf (_("Formatting node %s...\n"), node); - -#if defined (HAVE_MACROS) - if (macro_expansion_output_stream && !executing_string) - remember_itext (input_text, input_text_offset); -#endif /* HAVE_MACROS */ - - no_indent = 1; - if (!no_headers) - { - add_word_args ("\037\nFile: %s, Node: ", pretty_output_filename); - -#if defined (HAVE_MACROS) - if (macro_expansion_output_stream && !executing_string) - me_execute_string (node); - else -#endif /* HAVE_MACROS */ - execute_string ("%s", node); - filling_enabled = indented_fill = 0; - } - - /* Check for defaulting of this node's next, prev, and up fields. */ - defaulting = (*next == 0 && *prev == 0 && *up == 0); - - this_section = what_section (input_text + input_text_offset); - - /* If we are defaulting, then look at the immediately following - sectioning command (error if none) to determine the node's - level. Find the node that contains the menu mentioning this node - that is one level up (error if not found). That node is the "Up" - of this node. Default the "Next" and "Prev" from the menu. */ - if (defaulting) - { - NODE_REF *last_ref = (NODE_REF *)NULL; - NODE_REF *ref = node_references; - - if ((this_section < 0) && (strcmp (node, "Top") != 0)) - { - char *polite_section_name = "top"; - int i; - - for (i = 0; section_alist[i].name; i++) - if (section_alist[i].level == current_section + 1) - { - polite_section_name = section_alist[i].name; - break; - } - - line_error - (_("Node `%s' requires a sectioning command (e.g. %c%s)"), - node, COMMAND_PREFIX, polite_section_name); - } - else - { - if (strcmp (node, "Top") == 0) - { - /* Default the NEXT pointer to be the first menu item in - this node, if there is a menu in this node. We have to - try very hard to find the menu, as it may be obscured - by execution_strings which are on the filestack. For - every member of the filestack which has a FILENAME - member which is identical to the current INPUT_FILENAME, - search forward from that offset. */ - int saved_input_text_offset = input_text_offset; - int saved_size_of_input_text = size_of_input_text; - char *saved_input_text = input_text; - FSTACK *next_file = filestack; - - int orig_offset, orig_size; - char *glean_node_from_menu (); - - /* No matter what, make this file point back at `(dir)'. */ - free (up); up = xstrdup ("(dir)"); - - while (1) - { - orig_offset = input_text_offset; - orig_size = - search_forward (node_search_string, orig_offset); - - if (orig_size < 0) - orig_size = size_of_input_text; - - input_text_offset = - search_forward (menu_search_string, orig_offset); - - if (input_text_offset > -1) - { - char *nodename_from_menu = (char *)NULL; - - input_text_offset = - search_forward ("\n* ", input_text_offset); - - if (input_text_offset != -1) - nodename_from_menu = glean_node_from_menu (0); - - if (nodename_from_menu) - { - free (next); next = nodename_from_menu; - break; - } - } - - /* We got here, so it hasn't been found yet. Try - the next file on the filestack if there is one. */ - if (next_file && - (strcmp (next_file->filename, input_filename) == 0)) - { - input_text = next_file->text; - input_text_offset = next_file->offset; - size_of_input_text = next_file->size; - next_file = next_file->next; - } - else - { - /* No more input files to check. */ - break; - } - } - - input_text = saved_input_text; - input_text_offset = saved_input_text_offset; - size_of_input_text = saved_size_of_input_text; - } - } - - /* Fix the level of the menu references in the Top node, iff it - was declared with @top, and no subsequent reference was found. */ - if (top_node_seen && !non_top_node_seen) - { - /* Then this is the first non-@top node seen. */ - int level; - - level = set_top_section_level (this_section - 1); - non_top_node_seen = 1; - - while (ref) - { - if (ref->section == level) - ref->section = this_section - 1; - ref = ref->next; - } - - ref = node_references; - } - - while (ref) - { - if (ref->section == (this_section - 1) && - ref->type == menu_reference && - strcmp (ref->node, node) == 0) - { - char *containing_node = ref->containing_node; - - free (up); - up = xstrdup (containing_node); - - if (last_ref && - last_ref->type == menu_reference && - (strcmp (last_ref->containing_node, - containing_node) == 0)) - { - free (next); - next = xstrdup (last_ref->node); - } - - while ((ref->section == this_section - 1) && - (ref->next) && - (ref->next->type != menu_reference)) - ref = ref->next; - - if (ref->next && ref->type == menu_reference && - (strcmp (ref->next->containing_node, - containing_node) == 0)) - { - free (prev); - prev = xstrdup (ref->next->node); - } - else if (!ref->next && - strcasecmp (ref->containing_node, "Top") == 0) - { - free (prev); - prev = xstrdup (ref->containing_node); - } - break; - } - last_ref = ref; - ref = ref->next; - } - } - -#if defined (HAVE_MACROS) - /* Insert the correct args if we are expanding macros, and the node's - pointers weren't defaulted. */ - if (macro_expansion_output_stream && !executing_string && !defaulting) - { - char *temp; - int op_orig = output_paragraph_offset; - - temp = (char *)xmalloc (3 + strlen (next)); - sprintf (temp, ", %s", next); - me_execute_string (temp); - free (temp); - - temp = (char *)xmalloc (3 + strlen (prev)); - sprintf (temp, ", %s", prev); - me_execute_string (temp); - free (temp); - - temp = (char *)xmalloc (4 + strlen (up)); - sprintf (temp, ", %s", up); - me_execute_string (temp); - free (temp); - - output_paragraph_offset = op_orig; - } -#endif /* HAVE_MACROS */ - - if (!no_headers) - { -#if defined (HAVE_MACROS) - if (macro_expansion_output_stream) - me_inhibit_expansion++; -#endif /* HAVE_MACROS */ - - if (*next) - { - execute_string (", Next: %s", next); - filling_enabled = indented_fill = 0; - } - - if (*prev) - { - execute_string (", Prev: %s", prev); - filling_enabled = indented_fill = 0; - } - - if (*up) - { - execute_string (", Up: %s", up); - filling_enabled = indented_fill = 0; - } -#if defined (HAVE_MACROS) - if (macro_expansion_output_stream) - me_inhibit_expansion--; -#endif /* HAVE_MACROS */ - } - - close_paragraph (); - no_indent = 0; - - if (!*node) - { - line_error ("No node name specified for `%c%s' command", - COMMAND_PREFIX, command); - free (node); - free (next); - free (prev); - free (up); - } - else - { - if (!*next) { free (next); next = (char *)NULL; } - if (!*prev) { free (prev); prev = (char *)NULL; } - if (!*up) { free (up); up = (char *)NULL; } - remember_node (node, prev, next, up, new_node_pos, line_number, no_warn); - } - - /* Change the section only if there was a sectioning command. */ - if (this_section >= 0) - current_section = this_section; - - filling_enabled = 1; -} - -/* Validation of an info file. - Scan through the list of tag entries touching the Prev, Next, and Up - elements of each. It is an error not to be able to touch one of them, - except in the case of external node references, such as "(DIR)". - - If the Prev is different from the Up, - then the Prev node must have a Next pointing at this node. - - Every node except Top must have an Up. - The Up node must contain some sort of reference, other than a Next, - to this node. - - If the Next is different from the Next of the Up, - then the Next node must have a Prev pointing at this node. */ -void -validate_file (tag_table) - TAG_ENTRY *tag_table; -{ - char *old_input_filename = input_filename; - TAG_ENTRY *tags = tag_table; - - while (tags != (TAG_ENTRY *) NULL) - { - register TAG_ENTRY *temp_tag; - - input_filename = tags->filename; - line_number = tags->line_no; - - /* If this is a "no warn" node, don't validate it in any way. */ - if (tags->flags & NO_WARN) - { - tags = tags->next_ent; - continue; - } - - /* If this node has a Next, then make sure that the Next exists. */ - if (tags->next) - { - validate (tags->next, tags->line_no, "Next"); - - /* If the Next node exists, and there is no Up, then make - sure that the Prev of the Next points back. */ - temp_tag = find_node (tags->next); - if (temp_tag) - { - char *prev; - - if (temp_tag->flags & NO_WARN) - { - /* Do nothing if we aren't supposed to issue warnings - about this node. */ - } - else - { - prev = temp_tag->prev; - if (!prev || (strcmp (prev, tags->node) != 0)) - { - line_error (_("Node `%s''s Next field not pointed back to"), - tags->node); - line_number = temp_tag->line_no; - input_filename = temp_tag->filename; - line_error - (_("This node (`%s') is the one with the bad `Prev'"), - temp_tag->node); - input_filename = tags->filename; - line_number = tags->line_no; - temp_tag->flags |= PREV_ERROR; - } - } - } - } - - /* Validate the Prev field if there is one, and we haven't already - complained about it in some way. You don't have to have a Prev - field at this stage. */ - if (!(tags->flags & PREV_ERROR) && tags->prev) - { - int valid_p = validate (tags->prev, tags->line_no, "Prev"); - - if (!valid_p) - tags->flags |= PREV_ERROR; - else - { - /* If the Prev field is not the same as the Up field, - then the node pointed to by the Prev field must have - a Next field which points to this node. */ - if (tags->up && (strcmp (tags->prev, tags->up) != 0)) - { - temp_tag = find_node (tags->prev); - - /* If we aren't supposed to issue warnings about the - target node, do nothing. */ - if (!temp_tag || (temp_tag->flags & NO_WARN)) - { - /* Do nothing. */ - } - else - { - if (!temp_tag->next || - (strcmp (temp_tag->next, tags->node) != 0)) - { - line_error - (_("Node `%s's Prev field not pointed back to"), - tags->node); - line_number = temp_tag->line_no; - input_filename = temp_tag->filename; - line_error (_("This node (`%s') has the bad Next"), - temp_tag->node); - input_filename = tags->filename; - line_number = tags->line_no; - temp_tag->flags |= NEXT_ERROR; - } - } - } - } - } - - if (!tags->up && (strcasecmp (tags->node, _("Top")) != 0)) - line_error (_("Node `%s' missing Up field"), tags->node); - else if (tags->up) - { - int valid_p = validate (tags->up, tags->line_no, "Up"); - - /* If node X has Up: Y, then warn if Y fails to have a menu item - or note pointing at X, if Y isn't of the form "(Y)". */ - if (valid_p && *tags->up != '(') - { - NODE_REF *nref, *tref, *list; - NODE_REF *find_node_reference (); - - tref = (NODE_REF *) NULL; - list = node_references; - - for (;;) - { - if (!(nref = find_node_reference (tags->node, list))) - break; - - if (strcmp (nref->containing_node, tags->up) == 0) - { - if (nref->type != menu_reference) - { - tref = nref; - list = nref->next; - } - else - break; - } - list = nref->next; - } - - if (!nref) - { - temp_tag = find_node (tags->up); - line_number = temp_tag->line_no; - input_filename = temp_tag->filename; - if (!tref) - line_error ( -_("`%s' has an Up field of `%s', but `%s' has no menu item for `%s'"), - tags->node, tags->up, tags->up, tags->node); - line_number = tags->line_no; - input_filename = tags->filename; - } - } - } - tags = tags->next_ent; - } - - validate_other_references (node_references); - /* We have told the user about the references which didn't exist. - Now tell him about the nodes which aren't referenced. */ - - tags = tag_table; - while (tags != (TAG_ENTRY *) NULL) - { - /* If this node is a "no warn" node, do nothing. */ - if (tags->flags & NO_WARN) - { - tags = tags->next_ent; - continue; - } - - /* Special hack. If the node in question appears to have - been referenced more than REFERENCE_WARNING_LIMIT times, - give a warning. */ - if (tags->touched > reference_warning_limit) - { - input_filename = tags->filename; - line_number = tags->line_no; - warning (_("node `%s' has been referenced %d times"), - tags->node, tags->touched); - } - - if (tags->touched == 0) - { - input_filename = tags->filename; - line_number = tags->line_no; - - /* Notice that the node "Top" is special, and doesn't have to - be referenced. */ - if (strcasecmp (tags->node, _("Top")) != 0) - warning (_("unreferenced node `%s'"), tags->node); - } - tags = tags->next_ent; - } - input_filename = old_input_filename; -} - -/* Return 1 if tag correctly validated, or 0 if not. */ -int -validate (tag, line, label) - char *tag; - int line; - char *label; -{ - TAG_ENTRY *result; - - /* If there isn't a tag to verify, or if the tag is in another file, - then it must be okay. */ - if (!tag || !*tag || *tag == '(') - return (1); - - /* Otherwise, the tag must exist. */ - result = find_node (tag); - - if (!result) - { - line_number = line; - line_error (_("%s reference to nonexistent node `%s'"), label, tag); - return (0); - } - result->touched++; - return (1); -} - -/* Split large output files into a series of smaller files. Each file - is pointed to in the tag table, which then gets written out as the - original file. The new files have the same name as the original file - with a "-num" attached. SIZE is the largest number of bytes to allow - in any single split file. */ -void -split_file (filename, size) - char *filename; - int size; -{ - char *root_filename, *root_pathname; - char *the_file, *filename_part (); - struct stat fileinfo; - long file_size; - char *the_header; - int header_size; - - /* Can only do this to files with tag tables. */ - if (!tag_table) - return; - - if (size == 0) - size = DEFAULT_SPLIT_SIZE; - - if ((stat (filename, &fileinfo) != 0) || - (((long) fileinfo.st_size) < SPLIT_SIZE_THRESHOLD)) - return; - file_size = (long) fileinfo.st_size; - - the_file = find_and_load (filename); - if (!the_file) - return; - - root_filename = filename_part (filename); - root_pathname = pathname_part (filename); - - if (!root_pathname) - root_pathname = xstrdup (""); - - /* Start splitting the file. Walk along the tag table - outputting sections of the file. When we have written - all of the nodes in the tag table, make the top-level - pointer file, which contains indirect pointers and - tags for the nodes. */ - { - int which_file = 1; - TAG_ENTRY *tags = tag_table; - char *indirect_info = (char *)NULL; - - /* Remember the `header' of this file. The first tag in the file is - the bottom of the header; the top of the file is the start. */ - the_header = (char *)xmalloc (1 + (header_size = tags->position)); - memcpy (the_header, the_file, header_size); - - while (tags) - { - int file_top, file_bot, limit; - - /* Have to include the Control-_. */ - file_top = file_bot = tags->position; - limit = file_top + size; - - /* If the rest of this file is only one node, then - that is the entire subfile. */ - if (!tags->next_ent) - { - int i = tags->position + 1; - char last_char = the_file[i]; - - while (i < file_size) - { - if ((the_file[i] == '\037') && - ((last_char == '\n') || - (last_char == '\014'))) - break; - else - last_char = the_file[i]; - i++; - } - file_bot = i; - tags = tags->next_ent; - goto write_region; - } - - /* Otherwise, find the largest number of nodes that can fit in - this subfile. */ - for (; tags; tags = tags->next_ent) - { - if (!tags->next_ent) - { - /* This entry is the last node. Search forward for the end - of this node, and that is the end of this file. */ - int i = tags->position + 1; - char last_char = the_file[i]; - - while (i < file_size) - { - if ((the_file[i] == '\037') && - ((last_char == '\n') || - (last_char == '\014'))) - break; - else - last_char = the_file[i]; - i++; - } - file_bot = i; - - if (file_bot < limit) - { - tags = tags->next_ent; - goto write_region; - } - else - { - /* Here we want to write out everything before the last - node, and then write the last node out in a file - by itself. */ - file_bot = tags->position; - goto write_region; - } - } - - if (tags->next_ent->position > limit) - { - if (tags->position == file_top) - tags = tags->next_ent; - - file_bot = tags->position; - - write_region: - { - int fd; - char *split_filename; - - split_filename = (char *) xmalloc - (10 + strlen (root_pathname) + strlen (root_filename)); - sprintf - (split_filename, - "%s%s-%d", root_pathname, root_filename, which_file); - - fd = open - (split_filename, O_WRONLY | O_TRUNC | O_CREAT, 0666); - - if ((fd < 0) || - (write (fd, the_header, header_size) != header_size) || - (write (fd, the_file + file_top, file_bot - file_top) - != (file_bot - file_top)) || - ((close (fd)) < 0)) - { - perror (split_filename); - if (fd != -1) - close (fd); - exit (FATAL); - } - - if (!indirect_info) - { - indirect_info = the_file + file_top; - sprintf (indirect_info, "\037\nIndirect:\n"); - indirect_info += strlen (indirect_info); - } - - sprintf (indirect_info, "%s-%d: %d\n", - root_filename, which_file, file_top); - - free (split_filename); - indirect_info += strlen (indirect_info); - which_file++; - break; - } - } - } - } - - /* We have sucessfully created the subfiles. Now write out the - original again. We must use `output_stream', or - write_tag_table_indirect () won't know where to place the output. */ - output_stream = fopen (filename, "w"); - if (!output_stream) - { - perror (filename); - exit (FATAL); - } - - { - int distance = indirect_info - the_file; - fwrite (the_file, 1, distance, output_stream); - - /* Inhibit newlines. */ - paragraph_is_open = 0; - - write_tag_table_indirect (); - fclose (output_stream); - free (the_header); - free (the_file); - return; - } - } -} - -/* The strings here are followed in the message by `reference to...' in - the `validate' routine. */ -char * -reftype_type_string (type) - enum reftype type; -{ - switch (type) - { - case menu_reference: - return ("Menu"); - case followed_reference: - return ("Cross"); - default: - return ("Internal-bad-reference-type"); - } -} - -/* Remember this node name for later validation use. This is used to - remember menu references while reading the input file. After the - output file has been written, if validation is on, then we use the - contents of `node_references' as a list of nodes to validate. */ -void -remember_node_reference (node, line, type) - char *node; - int line; - enum reftype type; -{ - NODE_REF *temp = (NODE_REF *) xmalloc (sizeof (NODE_REF)); - - temp->next = node_references; - temp->node = xstrdup (node); - temp->line_no = line; - temp->section = current_section; - temp->type = type; - temp->containing_node = xstrdup (current_node ? current_node : ""); - temp->filename = node_filename; - - node_references = temp; -} - -void -validate_other_references (ref_list) - register NODE_REF *ref_list; -{ - char *old_input_filename = input_filename; - - while (ref_list != (NODE_REF *) NULL) - { - input_filename = ref_list->filename; - validate (ref_list->node, ref_list->line_no, - reftype_type_string (ref_list->type)); - ref_list = ref_list->next; - } - input_filename = old_input_filename; -} - -/* Find NODE in REF_LIST. */ -NODE_REF * -find_node_reference (node, ref_list) - char *node; - register NODE_REF *ref_list; -{ - while (ref_list) - { - if (strcmp (node, ref_list->node) == 0) - break; - ref_list = ref_list->next; - } - return (ref_list); -} - -void -free_node_references () -{ - register NODE_REF *list, *temp; - - list = node_references; - - while (list) - { - temp = list; - free (list->node); - free (list->containing_node); - list = list->next; - free (temp); - } - node_references = (NODE_REF *) NULL; -} - - /* This function gets called at the start of every line while inside of - a menu. It checks to see if the line starts with "* ", and if so, - remembers the node reference that this menu refers to. - input_text_offset is at the \n just before the line start. */ -#define menu_starter "* " -char * -glean_node_from_menu (remember_reference) - int remember_reference; -{ - int i, orig_offset = input_text_offset; - char *nodename; - - if (strncmp (&input_text[input_text_offset + 1], - menu_starter, - strlen (menu_starter)) != 0) - return ((char *)NULL); - else - input_text_offset += strlen (menu_starter) + 1; - - get_until_in_line (0, ":", &nodename); - if (curchar () == ':') - input_text_offset++; - canon_white (nodename); - - if (curchar () == ':') - goto save_node; - - free (nodename); - get_rest_of_line (&nodename); - - /* Special hack: If the nodename follows the menu item name, - then we have to read the rest of the line in order to find - out what the nodename is. But we still have to read the - line later, in order to process any formatting commands that - might be present. So un-count the carriage return that has just - been counted. */ - line_number--; - - isolate_nodename (nodename); - -save_node: - input_text_offset = orig_offset; - normalize_node_name (nodename); - i = strlen (nodename); - if (i && nodename[i - 1] == ':') - nodename[i - 1] = 0; - - if (remember_reference) - { - remember_node_reference (nodename, line_number, menu_reference); - free (nodename); - return ((char *)NULL); - } - else - return (nodename); -} - -static void -isolate_nodename (nodename) - char *nodename; -{ - register int i, c; - int paren_seen, paren; - - if (!nodename) - return; - - canon_white (nodename); - paren_seen = paren = i = 0; - - if (*nodename == '.' || !*nodename) - { - *nodename = 0; - return; - } - - if (*nodename == '(') - { - paren++; - paren_seen++; - i++; - } - - for (; (c = nodename[i]); i++) - { - if (paren) - { - if (c == '(') - paren++; - else if (c == ')') - paren--; - - continue; - } - - /* If the character following the close paren is a space, then this - node has no more characters associated with it. */ - if (c == '\t' || - c == '\n' || - c == ',' || - ((paren_seen && nodename[i - 1] == ')') && - (c == ' ' || c == '.')) || - (c == '.' && - ((!nodename[i + 1] || - (cr_or_whitespace (nodename[i + 1])) || - (nodename[i + 1] == ')'))))) - break; - } - nodename[i] = 0; -} - -void -cm_menu () -{ - if (current_node == (char *)NULL) - { - warning (_("%cmenu seen before first node"), COMMAND_PREFIX); - warning (_("creating `Top' node")); - execute_string ("@node Top"); - } - begin_insertion (menu); -} - -void -cm_detailmenu () -{ - if (current_node == (char *)NULL) - { - warning (_("%cmenu seen before first node"), COMMAND_PREFIX); - warning (_("creating `Top' node")); - execute_string ("@node Top"); - } - begin_insertion (detailmenu); -} - -/* **************************************************************** */ -/* */ -/* Cross Reference Hacking */ -/* */ -/* **************************************************************** */ +/* Cross references. */ /* Return next comma-delimited argument, but do not cross a close-brace - boundary. Clean up whitespace, too. */ + boundary. Clean up whitespace, too. If EXPAND is nonzero, replace + the entire brace-delimited argument list with its expansion before + looking for the next comma. */ char * -get_xref_token () +get_xref_token (expand) + int expand; { char *string; + if (expand) + { + int old_offset = input_text_offset; + int old_lineno = line_number; + + get_until_in_braces ("}", &string); + if (curchar () == '}') /* as opposed to end of text */ + input_text_offset++; + if (input_text_offset > old_offset) + { + int limit = input_text_offset; + + input_text_offset = old_offset; + line_number = old_lineno; + only_macro_expansion++; + replace_with_expansion (input_text_offset, &limit); + only_macro_expansion--; + } + free (string); + } + get_until_in_braces (",", &string); if (curchar () == ',') input_text_offset++; fix_whitespace (string); - return (string); + return string; } -int px_ref_flag = 0; /* Controls initial output string. */ +/* NOTE: If you wonder why the HTML output is produced with such a + peculiar mix of calls to add_word and execute_string, here's the + reason. get_xref_token (1) expands all macros in a reference, but + any other commands, like @value, @@, etc., are left intact. To + expand them, we need to run the arguments through execute_string. + However, characters like <, &, > and others cannot be let into + execute_string, because they will be escaped. See the mess? */ /* Make a cross reference. */ void @@ -5712,15 +2694,20 @@ cm_xref (arg) { if (arg == START) { - char *arg1, *arg2, *arg3, *arg4, *arg5; + char *arg1 = get_xref_token (1); /* expands all macros in xref */ + char *arg2 = get_xref_token (0); + char *arg3 = get_xref_token (0); + char *arg4 = get_xref_token (0); + char *arg5 = get_xref_token (0); + char *tem; - arg1 = get_xref_token (); - arg2 = get_xref_token (); - arg3 = get_xref_token (); - arg4 = get_xref_token (); - arg5 = get_xref_token (); - - add_word_args ("%s", px_ref_flag ? "*note " : "*Note "); + if (html) + { + if (!ref_flag) + add_word_args ("%s", px_ref_flag ? _("see ") : _("See ")); + } + else + add_word_args ("%s", px_ref_flag ? "*note " : "*Note "); if (*arg5 || *arg4) { @@ -5736,7 +2723,30 @@ cm_xref (arg) else node_name = arg2; - execute_string ("%s: (%s)%s", node_name, arg4, arg1); + if (html) + { + /* html fixxme: revisit this; external node name not + much use to us with numbered nodes. */ + add_word (""); + execute_string ("%s", arg1); + add_word (""); + } + else + { + execute_string ("%s:", node_name); + in_fixed_width_font++; + execute_string (" (%s)%s%s", arg4, arg1, px_ref_flag ? "." : ""); + in_fixed_width_font--; + } + /* Free all of the arguments found. */ if (arg1) free (arg1); if (arg2) free (arg2); @@ -5750,17 +2760,56 @@ cm_xref (arg) if (*arg3) { - if (!*arg2) - execute_string ("%s: %s", arg3, arg1); + if (html) + { + add_word (""); + execute_string ("%s", *arg2 ? arg2 : arg3); + add_word (""); + } else - execute_string ("%s: %s", arg2, arg1); + { + execute_string ("%s:", *arg2 ? arg2 : arg3); + in_fixed_width_font++; + execute_string (" %s%s", arg1, px_ref_flag ? "." : ""); + in_fixed_width_font--; + } } else { - if (*arg2) - execute_string ("%s: %s", arg2, arg1); + if (html) + { + add_word (""); + execute_string ("%s", *arg2 ? arg2 : arg1); + add_word (""); + } else - execute_string ("%s::", arg1); + { + if (*arg2) + { + execute_string ("%s:", arg2); + in_fixed_width_font++; + execute_string (" %s%s", arg1, px_ref_flag ? "." : ""); + in_fixed_width_font--; + } + else + { + in_fixed_width_font++; + execute_string ("%s::", arg1); + in_fixed_width_font--; + } + } } /* Free all of the arguments found. */ @@ -5771,29 +2820,21 @@ cm_xref (arg) if (arg5) free (arg5); } else - { - /* Check to make sure that the next non-whitespace character is either - a period or a comma. input_text_offset is pointing at the "}" which - ended the xref or pxref command. */ - int temp = input_text_offset + 1; + { /* Check to make sure that the next non-whitespace character is + valid to follow an xref (so info readers can find the node + names). `input_text_offset' is pointing at the "}" which ended + the xref or ref command. */ + int temp; - if (output_paragraph[output_paragraph_offset - 2] == ':' && - output_paragraph[output_paragraph_offset - 1] == ':') - return; - while (temp < size_of_input_text) + for (temp = input_text_offset + 1; temp < input_text_length; ) { if (cr_or_whitespace (input_text[temp])) temp++; else { - if (input_text[temp] != '.' - && input_text[temp] != ',' - && input_text[temp] != '\t') - { - line_error ( - _("`.' or `,' must follow cross reference, not %c"), - input_text[temp]); - } + if (input_text[temp] != '.' && input_text[temp] != ',') + warning (_("`.' or `,' must follow cross reference, not %c"), + input_text[temp]); break; } } @@ -5810,8 +2851,22 @@ cm_pxref (arg) cm_xref (arg); px_ref_flag--; } - else - add_char ('.'); + /* Note that cm_xref isn't called with arg == END, which disables + the code near the end of cm_xref that checks for `.' or `,' + after the cross-reference. This is because @pxref{} generates + the required character itself, when needed. */ +} + +void +cm_ref (arg) + int arg; +{ + if (arg == START) + { + ref_flag++; + cm_xref (arg); + ref_flag--; + } } void @@ -5820,14 +2875,27 @@ cm_inforef (arg) { if (arg == START) { - char *node = get_xref_token (); - char *pname = get_xref_token (); - char *file = get_xref_token (); + char *node = get_xref_token (1); /* expands all macros in inforef */ + char *pname = get_xref_token (0); + char *file = get_xref_token (0); - if (*pname) - execute_string ("*note %s: (%s)%s", pname, file, node); + if (html) + { + add_word (_("see ")); + /* html fixxme: revisit this */ + add_word (""); + execute_string ("%s", pname); + add_word (""); + } else - execute_string ("*note (%s)%s::", file, node); + { + if (*pname) + execute_string ("*note %s: (%s)%s", pname, file, node); + else + execute_string ("*note (%s)%s::", file, node); + } free (node); free (pname); @@ -5837,76 +2905,88 @@ cm_inforef (arg) /* A URL reference. */ void -cm_uref (arg, start_pos, end_pos) - int arg, start_pos, end_pos; +cm_uref (arg) + int arg; { - if (arg == END) + if (arg == START) { - char *comma; - char *arg = (char *) &output_paragraph[start_pos]; + extern int printing_index; + char *url = get_xref_token (1); /* expands all macros in uref */ + char *desc = get_xref_token (0); + char *replacement = get_xref_token (0); - output_paragraph[end_pos] = 0; - output_column -= end_pos - start_pos; - output_paragraph_offset = start_pos; - - arg = xstrdup (arg); - comma = strchr (arg, ','); /* let's hope for no commas in the url */ - if (comma) - { - *comma = 0; - /* Ignore spaces at beginning of second arg. */ - for (comma++; isspace (*comma); comma++) - ; - add_word (comma); - add_char (' '); - add_char ('('); - add_word (arg); - add_char (')'); + if (html) + { /* never need to show the url */ + add_word (""); + execute_string ("%s", *replacement ? replacement + : (*desc ? desc : url)); + add_word (""); } - else + else if (*replacement) /* do not show the url */ + execute_string ("%s", replacement); + else if (*desc) /* show both text and url */ { - extern int printing_index; - - if (!printing_index) - add_char ('`'); - - add_word (arg); - - if (!printing_index) - add_char ('\''); + execute_string ("%s ", desc); + in_fixed_width_font++; + execute_string ("(%s)", url); + in_fixed_width_font--; } - free (arg); - } + else /* no text at all, so have the url to show */ + { + in_fixed_width_font++; + execute_string ("%s%s%s", + printing_index ? "" : "`", + url, + printing_index ? "" : "'"); + in_fixed_width_font--; + } + if (url) + free (url); + if (desc) + free (desc); + if (replacement) + free (replacement); + } } /* An email reference. */ void -cm_email (arg, start_pos, end_pos) - int arg, start_pos, end_pos; +cm_email (arg) + int arg; { - if (arg == END) + if (arg == START) { - char *comma; - char *arg = (char *) &output_paragraph[start_pos]; + char *addr = get_xref_token (1); /* expands all macros in email */ + char *name = get_xref_token (0); - output_paragraph[end_pos] = 0; - output_column -= end_pos - start_pos; - output_paragraph_offset = start_pos; - - arg = xstrdup (arg); - comma = strchr (arg, ','); - if (comma) + if (html) { - *comma = 0; - for (comma++; isspace (*comma); comma++) - ; - add_word (comma); - add_char (' '); + add_word (""); + execute_string ("%s", *name ? name : addr); + add_word (""); } - add_char ('<'); - add_word (arg); - add_char ('>'); - free (arg); + else + { + execute_string ("%s%s", name, *name ? " " : ""); + in_fixed_width_font++; + execute_string ("<%s>", addr); + in_fixed_width_font--; + } + + if (addr) + free (addr); + if (name) + free (name); } } @@ -5916,264 +2996,84 @@ void cm_image (arg) int arg; { - if (arg == START) + char *name_arg, *rest; + + if (arg == END) + return; + + name_arg = get_xref_token (1); /* expands all macros in image */ + /* We don't (yet) care about any other args, but read them so they + don't end up in the text. */ + rest = get_xref_token (0); + if (rest) + free (rest); + rest = get_xref_token (0); + if (rest) + free (rest); + + if (*name_arg) { - char *name_arg = get_xref_token (); - /* We don't yet care about any other args, but read them so they - don't end up in the text. */ - char *arg = get_xref_token (); - if (arg) free (arg); - arg = get_xref_token (); - if (arg) free (arg); - - if (*name_arg) - { - /* Try to open foo.txt. */ + char *fullname = xmalloc (strlen (name_arg) + 4 + 1); + + if (html) + { /* fixxme It would be nice to insert more useful alt text. */ + sprintf (fullname, "%s.png", name_arg); + if (access (fullname, R_OK) != 0) + { + sprintf (fullname, "%s.jpg", name_arg); + if (access (fullname, R_OK) != 0) + { + line_error (_("No .png or .jpg for `%s'"), name_arg); + return; + } + } + + add_word_args ("\"%s\"", fullname, fullname); + } + else + { /* Try to open foo.txt. */ FILE *image_file; - char *name = xmalloc (strlen (name_arg) + 4); - strcpy (name, name_arg); - strcat (name, ".txt"); - image_file = fopen (name, "r"); + strcpy (fullname, name_arg); + strcat (fullname, ".txt"); + image_file = fopen (fullname, "r"); if (image_file) { int ch; int save_inhibit_indentation = inhibit_paragraph_indentation; int save_filling_enabled = filling_enabled; - + inhibit_paragraph_indentation = 1; filling_enabled = 0; last_char_was_newline = 0; - + /* Maybe we need to remove the final newline if the image file is only one line to allow in-line images. On the other hand, they could just make the file without a final newline. */ while ((ch = getc (image_file)) != EOF) add_char (ch); - + inhibit_paragraph_indentation = save_inhibit_indentation; filling_enabled = save_filling_enabled; - - if (fclose (image_file) != 0) { - perror (name); - } + + if (fclose (image_file) != 0) + perror (fullname); } else - warning (_("@image file `%s' unreadable: %s"), name, - strerror (errno)); + warning (_("@image file `%s' unreadable: %s"), fullname, + strerror (errno)); } - else - line_error (_("@image missing filename argument")); - if (name_arg) free (name_arg); + free (fullname); } -} - -/* **************************************************************** */ -/* */ -/* Insertion Command Stubs */ -/* */ -/* **************************************************************** */ - -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_display () -{ - begin_insertion (display); -} - -void -cm_direntry () -{ - if (no_headers) - command_name_condition (); else - begin_insertion (direntry); -} + line_error (_("@image missing filename argument")); -void -cm_itemize () -{ - begin_insertion (itemize); -} - -void -cm_enumerate () -{ - do_enumeration (enumerate, "1"); -} - -/* Start an enumeration insertion of type TYPE. If the user supplied - no argument on the line, then use DEFAULT_STRING as the initial string. */ -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_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 () -{ - begin_insertion (ifinfo); -} - -void -cm_ifnothtml () -{ - begin_insertion (ifnothtml); -} - -void -cm_ifnottex () -{ - begin_insertion (ifnottex); -} - -/* 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); -} - -/* 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 (&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); + if (name_arg) + free (name_arg); } -/* **************************************************************** */ -/* */ -/* Conditional Handling */ -/* */ -/* **************************************************************** */ +/* Conditionals. */ /* A structure which contains `defined' variables. */ typedef struct defines { @@ -6183,7 +3083,7 @@ typedef struct defines { } DEFINE; /* The linked list of `set' defines. */ -DEFINE *defines = (DEFINE *)NULL; +DEFINE *defines = NULL; /* Add NAME to the list of `set' defines. */ void @@ -6201,7 +3101,7 @@ set (name, value) return; } - temp = (DEFINE *)xmalloc (sizeof (DEFINE)); + temp = xmalloc (sizeof (DEFINE)); temp->next = defines; temp->name = xstrdup (name); temp->value = xstrdup (value); @@ -6213,9 +3113,9 @@ void clear (name) char *name; { - register DEFINE *temp, *last; + DEFINE *temp, *last; - last = (DEFINE *)NULL; + last = NULL; temp = defines; while (temp) @@ -6242,28 +3142,13 @@ char * set_p (name) char *name; { - register DEFINE *temp; + DEFINE *temp; for (temp = defines; temp; temp = temp->next) if (strcmp (temp->name, name) == 0) - return (temp->value); + return temp->value; - return ((char *)NULL); -} - -/* Conditionally parse based on the current command name. */ -void -command_name_condition () -{ - char *discarder; - - discarder = (char *)xmalloc (8 + strlen (command)); - - sprintf (discarder, "\n%cend %s", COMMAND_PREFIX, command); - discard_until (discarder); - discard_until ("\n"); - - free (discarder); + return NULL; } /* Create a variable whose name appears as the first word on this line. */ @@ -6310,7 +3195,7 @@ cm_ifeq () if (array_len (arglist) > 1) { if ((strcasecmp (arglist[0], arglist[1]) == 0) && - (arglist[2] != (char *)NULL)) + (arglist[2])) execute_string ("%s\n", arglist[2]); } @@ -6322,7 +3207,27 @@ void cm_value (arg, start_pos, end_pos) int arg, start_pos, end_pos; { - if (arg == END) + static int value_level = 0, saved_meta_pos = -1; + + /* All the text after @value{ upto the matching } will eventually + disappear from output_paragraph, when this function is called + with ARG == END. If the text produced until then sets + meta_char_pos, we will need to restore it to the value it had + before @value was seen. So we need to save the previous value + of meta_char_pos here. */ + if (arg == START) + { + /* If we are already inside some outer @value, don't overwrite + the value saved in saved_meta_pos. */ + if (!value_level) + saved_meta_pos = meta_char_pos; + value_level++; + /* While the argument of @value is processed, we need to inhibit + textual transformations like "--" into "-", since @set didn't + do that when it grabbed the name of the variable. */ + in_fixed_width_font++; + } + else { char *name = (char *) &output_paragraph[start_pos]; char *value; @@ -6332,10 +3237,26 @@ cm_value (arg, start_pos, end_pos) output_column -= end_pos - start_pos; output_paragraph_offset = start_pos; + /* Restore the previous value of meta_char_pos if the stuff + inside this @value{} moved it. */ + if (saved_meta_pos == -1) /* can't happen inside @value{} */ + abort (); + if (value_level == 1 + && meta_char_pos >= start_pos && meta_char_pos < end_pos) + { + meta_char_pos = saved_meta_pos; + saved_meta_pos = -1; + } + value_level--; + /* No need to decrement in_fixed_width_font, since before + we are called with arg == END, the reader loop already + popped the brace stack, which restored in_fixed_width_font, + among other things. */ + if (value) execute_string ("%s", value); else - add_word_args (_("{No Value For \"%s\"}"), name); + add_word_args (_("{No value for `%s'}"), name); free (name); } @@ -6348,8 +3269,12 @@ handle_variable (action) { char *name; - get_rest_of_line (&name); - backup_input_pointer (); + get_rest_of_line (0, &name); + /* 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 (); handle_variable_internal (action, name); free (name); } @@ -6452,11 +3377,11 @@ handle_variable_internal (action, name) { int level = 0, done = 0; - while (!done && input_text_offset < size_of_input_text) + while (!done && input_text_offset < input_text_length) { char *freeable_line, *line; - get_rest_of_line (&freeable_line); + get_rest_of_line (0, &freeable_line); for (line = freeable_line; whitespace (*line); line++); @@ -6488,7 +3413,7 @@ handle_variable_internal (action, name) } free (freeable_line); } - + if (!done) { int save = line_number; @@ -6497,7 +3422,7 @@ handle_variable_internal (action, name) condition); line_number = save; } - + /* We found the end of a false @ifset/ifclear. If we are in a menu, back up over the newline that ends the ifset, since that newline may also begin the next menu entry. */ @@ -6524,7 +3449,7 @@ typedef struct { int in_use; /* Nonzero means string currently in use. */ } EXECUTION_STRING; -static EXECUTION_STRING **execution_strings = (EXECUTION_STRING **)NULL; +static EXECUTION_STRING **execution_strings = NULL; static int execution_strings_index = 0; static int execution_strings_slots = 0; @@ -6532,8 +3457,8 @@ EXECUTION_STRING * get_execution_string (initial_size) int initial_size; { - register int i = 0; - EXECUTION_STRING *es = (EXECUTION_STRING *)NULL; + int i = 0; + EXECUTION_STRING *es = NULL; if (execution_strings) { @@ -6549,29 +3474,64 @@ get_execution_string (initial_size) { if (execution_strings_index + 1 >= execution_strings_slots) { - execution_strings = (EXECUTION_STRING **)xrealloc + execution_strings = xrealloc (execution_strings, (execution_strings_slots += 3) * sizeof (EXECUTION_STRING *)); for (; i < execution_strings_slots; i++) - execution_strings[i] = (EXECUTION_STRING *)NULL; + execution_strings[i] = NULL; } execution_strings[execution_strings_index] = - (EXECUTION_STRING *)xmalloc (sizeof (EXECUTION_STRING)); + xmalloc (sizeof (EXECUTION_STRING)); es = execution_strings[execution_strings_index]; execution_strings_index++; es->size = 0; - es->string = (char *)NULL; + es->string = NULL; es->in_use = 0; } if (initial_size > es->size) { - es->string = (char *) xrealloc (es->string, initial_size); + es->string = xrealloc (es->string, initial_size); es->size = initial_size; } - return (es); + return es; +} + +/* Given a pointer to TEXT and its desired length NEW_LEN, find TEXT's + entry in the execution_strings[] array and change the .STRING and + .SIZE members of that entry as appropriate. */ +void +maybe_update_execution_strings (text, new_len) + char **text; + unsigned new_len; +{ + int i = 0; + + if (execution_strings) + { + for (i = 0; i < execution_strings_index; i++) + if (execution_strings[i] && (execution_strings[i]->in_use == 1) && + execution_strings[i]->string == *text) + { + /* Don't ever shrink the string storage in execution_strings[]! + execute_string assumes that it is always big enough to store + every possible execution_string, and will break if that's + not true. So we only enlarge the string storage if the + current size isn't big enough. */ + if (execution_strings[i]->size < new_len) + { + execution_strings[i]->string = + *text = xrealloc (*text, new_len + 1); + execution_strings[i]->size = new_len + 1; + } + return; + } + } + /* We should *never* end up here, since if we are inside + execute_string, TEXT is always in execution_strings[]. */ + abort (); } /* Execute the string produced by formatting the ARGs with FORMAT. This @@ -6607,7 +3567,7 @@ execute_string (format, va_alist) input_text_offset = 0; input_text = temp_string; input_filename = xstrdup (input_filename); - size_of_input_text = strlen (temp_string); + input_text_length = strlen (temp_string); executing_string++; reader_loop (); @@ -6619,8 +3579,8 @@ execute_string (format, va_alist) } -/* Return what would be output for STR, i.e., expand Texinfo commands. - If IMPLICIT_CODE is set, expand @code{STR}. */ +/* Return what would be output for STR (in newly-malloced memory), i.e., + expand Texinfo commands. If IMPLICIT_CODE is set, expand @code{STR}. */ char * expansion (str, implicit_code) @@ -6633,6 +3593,29 @@ expansion (str, implicit_code) /* Inhibit any real output. */ int start = output_paragraph_offset; int saved_paragraph_is_open = paragraph_is_open; + int saved_output_column = output_column; + + /* Inhibit indentation and filling, so that extra newlines + are not added to the expansion. (This is undesirable if + we write the expanded text to macro_expansion_output_stream.) */ + int saved_filling_enabled = filling_enabled; + int saved_indented_fill = indented_fill; + int saved_no_indent = no_indent; + int saved_escape_html = escape_html; + int saved_meta_pos = meta_char_pos; + int saved_last_char = last_inserted_character; + int saved_last_nl = last_char_was_newline; + + /* If we are called in the middle of processing a command, we need + to dup and save the global variable `command' (which holds the + name of this command), since the recursive reader loop will free + it from under our feet if it finds any macros in STR. */ + char *saved_command = command ? xstrdup (command) : NULL; + + filling_enabled = 0; + indented_fill = 0; + no_indent = 1; + escape_html = 0; inhibit_output_flushing (); paragraph_is_open = 1; @@ -6644,998 +3627,43 @@ expansion (str, implicit_code) result = xmalloc (1 + length); memcpy (result, (char *) (output_paragraph + start), length); result[length] = 0; - + /* Pretend it never happened. */ + free_and_clear (&command); + command = saved_command; output_paragraph_offset = start; paragraph_is_open = saved_paragraph_is_open; + output_column = saved_output_column; + filling_enabled = saved_filling_enabled; + indented_fill = saved_indented_fill; + no_indent = saved_no_indent; + escape_html = saved_escape_html; + meta_char_pos = saved_meta_pos; + last_inserted_character = saved_last_char; + last_char_was_newline = saved_last_nl; return result; } - -/* @itemx, @item. */ -static int itemx_flag = 0; -void -cm_itemx () -{ - itemx_flag++; - cm_item (); - itemx_flag--; -} - -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 (&rest_of_line); - item_func = current_item_function (); - - /* Okay, do the right thing depending on which insertion function - is active. */ - - switch_top: - switch (stack->insertion) - { - case multitable: - multitable_item (); - /* Ultra special hack. It appears that some people incorrectly - place text directly after the @item, instead of on a new line - by itself. This happens to work in TeX, so I make it work - here. */ - if (*rest_of_line) - { - line_number--; - input_text_offset = original_input_text_offset; - } - break; - - case ifinfo: - case ifset: - case ifclear: - 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 format: - case display: - case group: - line_error (_("The `%c%s' command is meaningless within a `@%s' block"), - COMMAND_PREFIX, command, - insertion_type_pname (current_insertion_type ())); - break; - - case itemize: - case enumerate: - if (itemx_flag) - { - line_error (_("%citemx is not meaningful inside of a `%s' block"), - COMMAND_PREFIX, - insertion_type_pname (current_insertion_type ())); - } - else - { - start_paragraph (); - kill_self_indent (-1); - filling_enabled = indented_fill = 1; - - if (current_insertion_type () == itemize) - { - indent (output_column = current_indent - 2); - - /* I need some way to determine whether this command - takes braces or not. I believe the user can type - either "@bullet" or "@bullet{}". Of course, they - can also type "o" or "#" or whatever else they want. */ - if (item_func && *item_func) - { - if (*item_func == COMMAND_PREFIX) - if (item_func[strlen (item_func) - 1] != '}') - execute_string ("%s{}", item_func); - else - 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: - { - /* 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); - } -} - -/* **************************************************************** */ -/* */ -/* Defun and Friends */ -/* */ -/* **************************************************************** */ - -#define DEFUN_SELF_DELIMITING(c) \ - (((c) == '(') \ - || ((c) == ')') \ - || ((c) == '[') \ - || ((c) == ']')) - -struct token_accumulator -{ - unsigned int length; - unsigned int index; - char **tokens; -}; - -void -initialize_token_accumulator (accumulator) - struct token_accumulator *accumulator; -{ - (accumulator->length) = 0; - (accumulator->index) = 0; - (accumulator->tokens) = NULL; -} - -void -accumulate_token (accumulator, token) - struct token_accumulator *accumulator; - char *token; -{ - if ((accumulator->index) >= (accumulator->length)) - { - (accumulator->length) += 10; - (accumulator->tokens) = (char **) xrealloc - (accumulator->tokens, (accumulator->length * sizeof (char *))); - } - accumulator->tokens[accumulator->index] = token; - accumulator->index += 1; -} +/* Return text (info) expansion of STR no matter what the current output + format is. */ char * -copy_substring (start, end) - char *start; - char *end; +text_expansion (str) + char *str; { - char *result, *scan, *scan_result; - - result = (char *) xmalloc ((end - start) + 1); - scan_result = result; - scan = start; - - while (scan < end) - *scan_result++ = *scan++; - - *scan_result = 0; - return (result); + char *ret; + int save_html = html; + + html = 0; + ret = expansion (str, 0); + html = save_html; + + return ret; } -/* Given `string' pointing at an open brace, skip forward and return a - pointer to just past the matching close brace. */ -int -scan_group_in_string (string_pointer) - char **string_pointer; -{ - register int c; - register char *scan_string; - register unsigned int level = 1; - - scan_string = (*string_pointer) + 1; - - while (1) - { - if (level == 0) - { - (*string_pointer) = scan_string; - return (1); - } - c = (*scan_string++); - if (c == 0) - { - /* Tweak line_number to compensate for fact that - we gobbled the whole line before coming here. */ - line_number -= 1; - line_error (_("Missing `}' in %cdef arg"), COMMAND_PREFIX); - line_number += 1; - (*string_pointer) = (scan_string - 1); - return (0); - } - if (c == '{') - level += 1; - if (c == '}') - level -= 1; - } -} - -/* Return a list of tokens from the contents of `string'. - Commands and brace-delimited groups count as single tokens. - Contiguous whitespace characters are converted to a token - consisting of a single space. */ -char ** -args_from_string (string) - char *string; -{ - struct token_accumulator accumulator; - register char *scan_string = string; - char *token_start, *token_end; - - initialize_token_accumulator (&accumulator); - - while ((*scan_string) != 0) - { - /* Replace arbitrary whitespace by a single space. */ - if (whitespace (*scan_string)) - { - scan_string += 1; - while (whitespace (*scan_string)) - scan_string += 1; - accumulate_token ((&accumulator), (xstrdup (" "))); - continue; - } - - /* Commands count as single tokens. */ - if ((*scan_string) == COMMAND_PREFIX) - { - token_start = scan_string; - scan_string += 1; - if (self_delimiting (*scan_string)) - scan_string += 1; - else - { - register int c; - while (1) - { - c = *scan_string++; - - if ((c == 0) || (c == '{') || (whitespace (c))) - { - scan_string -= 1; - break; - } - } - - if (*scan_string == '{') - { - char *s = scan_string; - (void) scan_group_in_string (&s); - scan_string = s; - } - } - token_end = scan_string; - } - - /* Parentheses and brackets are self-delimiting. */ - else if (DEFUN_SELF_DELIMITING (*scan_string)) - { - token_start = scan_string; - scan_string += 1; - token_end = scan_string; - } - - /* Open brace introduces a group that is a single token. */ - else if (*scan_string == '{') - { - char *s = scan_string; - int balanced = scan_group_in_string (&s); - - token_start = scan_string + 1; - scan_string = s; - token_end = balanced ? (scan_string - 1) : scan_string; - } - - /* Otherwise a token is delimited by whitespace, parentheses, - brackets, or braces. A token is also ended by a command. */ - else - { - token_start = scan_string; - - while (1) - { - register int c; - - c = *scan_string++; - - /* Do not back up if we're looking at a }; since the only - valid }'s are those matched with {'s, we want to give - an error. If we back up, we go into an infinite loop. */ - if (!c || whitespace (c) || DEFUN_SELF_DELIMITING (c) - || c == '{') - { - scan_string--; - break; - } - - /* If we encounter a command embedded within a token, - then end the token. */ - if (c == COMMAND_PREFIX) - { - scan_string--; - break; - } - } - token_end = scan_string; - } - - accumulate_token - (&accumulator, copy_substring (token_start, token_end)); - } - accumulate_token (&accumulator, NULL); - return (accumulator.tokens); -} - -void -process_defun_args (defun_args, auto_var_p) - char **defun_args; - int auto_var_p; -{ - int pending_space = 0; - - while (1) - { - char *defun_arg = *defun_args++; - - if (defun_arg == NULL) - break; - - if (defun_arg[0] == ' ') - { - pending_space = 1; - continue; - } - - if (pending_space) - { - add_char (' '); - pending_space = 0; - } - - if (DEFUN_SELF_DELIMITING (defun_arg[0])) - add_char (defun_arg[0]); - else if (defun_arg[0] == '&') - add_word (defun_arg); - else if (defun_arg[0] == COMMAND_PREFIX) - execute_string ("%s", defun_arg); - else if (auto_var_p) - execute_string ("%cvar{%s}", COMMAND_PREFIX, defun_arg); - else - add_word (defun_arg); - } -} - -char * -next_nonwhite_defun_arg (arg_pointer) - char ***arg_pointer; -{ - char **scan = (*arg_pointer); - char *arg = (*scan++); - - if ((arg != 0) && (*arg == ' ')) - arg = *scan++; - - if (arg == 0) - scan -= 1; - - *arg_pointer = scan; - - return ((arg == 0) ? "" : arg); -} - -/* Make the defun type insertion. - TYPE says which insertion this is. - X_P, if nonzero, says not to start a new insertion. */ -void -defun_internal (type, x_p) - enum insertion_type type; - int x_p; -{ - enum insertion_type base_type; - char **defun_args, **scan_args; - char *category, *defined_name, *type_name, *type_name2; - - { - char *line; - get_rest_of_line (&line); - defun_args = (args_from_string (line)); - free (line); - } - - scan_args = defun_args; - - switch (type) - { - case defun: - category = _("Function"); - base_type = deffn; - break; - case defmac: - category = _("Macro"); - base_type = deffn; - break; - case defspec: - category = _("Special Form"); - base_type = deffn; - break; - case defvar: - category = _("Variable"); - base_type = defvr; - break; - case defopt: - category = _("User Option"); - base_type = defvr; - break; - case deftypefun: - category = _("Function"); - base_type = deftypefn; - break; - case deftypevar: - category = _("Variable"); - base_type = deftypevr; - break; - case defivar: - category = _("Instance Variable"); - base_type = defcv; - break; - case defmethod: - category = _("Method"); - base_type = defop; - break; - case deftypemethod: - category = _("Method"); - base_type = deftypemethod; - break; - default: - category = next_nonwhite_defun_arg (&scan_args); - base_type = type; - break; - } - - if ((base_type == deftypefn) - || (base_type == deftypevr) - || (base_type == defcv) - || (base_type == defop) - || (base_type == deftypemethod)) - type_name = next_nonwhite_defun_arg (&scan_args); - - if (base_type == deftypemethod) - type_name2 = next_nonwhite_defun_arg (&scan_args); - - defined_name = next_nonwhite_defun_arg (&scan_args); - - /* This hack exists solely for the purposes of formatting the texinfo - manual. I couldn't think of a better way. The token might be - a simple @@ followed immediately by more text. If this is the case, - then the next defun arg is part of this one, and we should concatenate - them. */ - if (*scan_args && **scan_args && !whitespace (**scan_args) && - (strcmp (defined_name, "@@") == 0)) - { - char *tem = (char *)xmalloc (3 + strlen (scan_args[0])); - - sprintf (tem, "@@%s", scan_args[0]); - - free (scan_args[0]); - scan_args[0] = tem; - scan_args++; - defined_name = tem; - } - - if (!x_p) - begin_insertion (type); - - /* Write the definition header line. - This should start at the normal indentation. */ - current_indent -= default_indentation_increment; - start_paragraph (); - - switch (base_type) - { - case deffn: - case defvr: - case deftp: - execute_string (" -- %s: %s", category, defined_name); - break; - case deftypefn: - case deftypevr: - execute_string (" -- %s: %s %s", category, type_name, defined_name); - break; - case defcv: - execute_string (" -- %s of %s: %s", category, type_name, defined_name); - break; - case defop: - execute_string (" -- %s on %s: %s", category, type_name, defined_name); - break; - case deftypemethod: - execute_string (" -- %s on %s: %s %s", category, type_name, type_name2, - defined_name); - break; - } - current_indent += default_indentation_increment; - - /* Now process the function arguments, if any. - If these carry onto the next line, they should be indented by two - increments to distinguish them from the body of the definition, - which is indented by one increment. */ - current_indent += default_indentation_increment; - - switch (base_type) - { - case deffn: - case defop: - process_defun_args (scan_args, 1); - break; - - /* Through Makeinfo 1.67 we processed remaining args only for deftp, - deftypefn, and deftypemethod. But the libc manual, for example, - needs to say: - @deftypevar {char *} tzname[2] - And simply allowing the extra text seems far simpler than trying - to invent yet more defn commands. In any case, we should either - output it or give an error, not silently ignore it. */ - default: - process_defun_args (scan_args, 0); - break; - } - current_indent -= default_indentation_increment; - close_single_paragraph (); - - /* Make an entry in the appropriate index. */ - switch (base_type) - { - case deffn: - case deftypefn: - execute_string ("%cfindex %s\n", COMMAND_PREFIX, defined_name); - break; - case defvr: - case deftypevr: - case defcv: - execute_string ("%cvindex %s\n", COMMAND_PREFIX, defined_name); - break; - case defop: - case deftypemethod: - execute_string ("%cfindex %s on %s\n", - COMMAND_PREFIX, defined_name, type_name); - break; - case deftp: - execute_string ("%ctindex %s\n", COMMAND_PREFIX, defined_name); - break; - } - - /* Deallocate the token list. */ - scan_args = defun_args; - while (1) - { - char * arg = (*scan_args++); - if (arg == NULL) - break; - free (arg); - } - free (defun_args); -} - -/* Add an entry for a function, macro, special form, variable, or option. - If the name of the calling command ends in `x', then this is an extra - entry included in the body of an insertion of the same type. */ -void -cm_defun () -{ - int x_p; - enum insertion_type type; - char *temp = xstrdup (command); - - x_p = (command[strlen (command) - 1] == 'x'); - - if (x_p) - temp[strlen (temp) - 1] = 0; - - type = find_type_from_name (temp); - free (temp); - - /* If we are adding to an already existing insertion, then make sure - that we are already in an insertion of type TYPE. */ - if (x_p && - (!insertion_level || insertion_stack->insertion != type)) - { - line_error (_("Must be in a `%s' insertion in order to use `%s'x"), - command, command); - discard_until ("\n"); - return; - } - - defun_internal (type, x_p); -} -/* **************************************************************** */ -/* */ -/* Other Random Commands */ -/* */ -/* **************************************************************** */ - -/* This says to inhibit the indentation of the next paragraph, but - not of following paragraphs. */ -void -cm_noindent () -{ - if (!inhibit_paragraph_indentation) - inhibit_paragraph_indentation = -1; -} - -/* I don't know exactly what to do with this. Should I allow - someone to switch filenames in the middle of output? Since the - file could be partially written, this doesn't seem to make sense. - Another option: ignore it, since they don't *really* want to - switch files. Finally, complain, or at least warn. */ -void -cm_setfilename () -{ - char *filename; - get_rest_of_line (&filename); - /* warning ("`@%s %s' encountered and ignored", command, filename); */ - free (filename); -} - -void -cm_ignore_line () -{ - discard_until ("\n"); -} - -/* @br can be immediately followed by `{}', so we have to read those here. - It should simply close the paragraph. */ -void -cm_br () -{ - if (looking_at ("{}")) - input_text_offset += 2; - - if (curchar () == '\n') - { - input_text_offset++; - line_number++; - } - - close_paragraph (); -} - - /* Insert the number of blank lines passed as argument. */ -void -cm_sp () -{ - int lines; - char *line; - - get_rest_of_line (&line); - - if (sscanf (line, "%d", &lines) != 1) - { - line_error (_("%csp requires a positive numeric argument"), COMMAND_PREFIX); - } - else - { - if (lines < 0) - lines = 0; - - while (lines--) - add_char ('\n'); - } - free (line); -} - -/* @dircategory LINE outputs INFO-DIR-SECTION LINE, - but not if --no-headers. */ - -void -cm_dircategory () -{ - char *line; - - get_rest_of_line (&line);; - - if (!no_headers) - { - insert_string ("INFO-DIR-SECTION "); - insert_string (line); - insert ('\n'); - } - - free (line); -} - -/* Start a new line with just this text on it. - Then center the line of text. - This always ends the current paragraph. */ -void -cm_center () -{ - register int i, start, length; - int fudge_factor = 1; - unsigned char *line; - - close_paragraph (); - filling_enabled = indented_fill = 0; - cm_noindent (); - start = output_paragraph_offset; - inhibit_output_flushing (); - get_rest_of_line ((char **)&line); - execute_string ("%s", (char *)line); - free (line); - uninhibit_output_flushing (); - - i = output_paragraph_offset - 1; - while (i > (start - 1) && output_paragraph[i] == '\n') - i--; - - output_paragraph_offset = ++i; - length = output_paragraph_offset - start; - - if (length < (fill_column - fudge_factor)) - { - line = (unsigned char *)xmalloc (1 + length); - memcpy (line, (char *)(output_paragraph + start), length); - - i = (fill_column - fudge_factor - length) / 2; - output_paragraph_offset = start; - - while (i--) - insert (' '); - - for (i = 0; i < length; i++) - insert (line[i]); - - free (line); - } - - insert ('\n'); - close_paragraph (); - filling_enabled = 1; -} - -/* Show what an expression returns. */ -void -cm_result (arg) - int arg; -{ - if (arg == END) - add_word ("=>"); -} - -/* What an expression expands to. */ -void -cm_expansion (arg) - int arg; -{ - if (arg == END) - add_word ("==>"); -} - -/* Indicates two expressions are equivalent. */ -void -cm_equiv (arg) - int arg; -{ - if (arg == END) - add_word ("=="); -} - -/* What an expression may print. */ -void -cm_print (arg) - int arg; -{ - if (arg == END) - add_word ("-|"); -} - -/* An error signaled. */ -void -cm_error (arg) - int arg; -{ - if (arg == END) - add_word ("error-->"); -} - -/* The location of point in an example of a buffer. */ -void -cm_point (arg) - int arg; -{ - if (arg == END) - add_word ("-!-"); -} - -/* Start a new line with just this text on it. - The text is outdented one level if possible. */ -void -cm_exdent () -{ - char *line; - int i = current_indent; - - if (current_indent) - current_indent -= default_indentation_increment; - - get_rest_of_line (&line); - close_single_paragraph (); - execute_string ("%s", line); - current_indent = i; - free (line); - close_single_paragraph (); -} - - -/* Remember this file, and move onto the next. */ -void -cm_include () -{ - char *filename; - -#if defined (HAVE_MACROS) - if (macro_expansion_output_stream && !executing_string) - me_append_before_this_command (); -#endif /* HAVE_MACROS */ - - close_paragraph (); - get_rest_of_line (&filename); - -#if defined (HAVE_MACROS) - if (macro_expansion_output_stream && !executing_string) - remember_itext (input_text, input_text_offset); -#endif /* HAVE_MACROS */ - - pushfile (); - - /* In verbose mode we print info about including another file. */ - if (verbose_mode) - { - register int i = 0; - register FSTACK *stack = filestack; - - for (i = 0, stack = filestack; stack; stack = stack->next, i++); - - i *= 2; - - printf ("%*s", i, ""); - printf ("%c%s %s\n", COMMAND_PREFIX, command, filename); - fflush (stdout); - } - - if (!find_and_load (filename)) - { - extern int errno; - - popfile (); - line_number--; - - /* Cannot "@include foo", in line 5 of "/wh/bar". */ - line_error ("%c%s %s: %s", COMMAND_PREFIX, command, filename, - strerror (errno)); - - free (filename); - return; - } - else - { -#if defined (HAVE_MACROS) - if (macro_expansion_output_stream && !executing_string) - remember_itext (input_text, input_text_offset); -#endif /* HAVE_MACROS */ - reader_loop (); - } - free (filename); - popfile (); -} - -/* The other side of a malformed expression. */ -void -misplaced_brace () -{ - line_error (_("Misplaced %c"), '}'); -} - -/* Signals end of processing. Easy to make this happen. */ -void -cm_bye () -{ - input_text_offset = size_of_input_text; -} - /* Set the paragraph indentation variable to the value specified in STRING. Values can be: `asis': Don't change existing indentation. @@ -7654,1929 +3682,12 @@ set_paragraph_indent (string) else { if (sscanf (string, "%d", ¶graph_start_indent) != 1) - return (-1); + return -1; else { if (paragraph_start_indent == 0) paragraph_start_indent = -1; } } - return (0); -} - -void -cm_paragraphindent () -{ - char *arg; - - get_rest_of_line (&arg); - if (set_paragraph_indent (arg) != 0) - line_error (_("Bad argument to %c%s"), COMMAND_PREFIX, command); - - free (arg); -} - -/* **************************************************************** */ -/* */ -/* Indexing Stuff */ -/* */ -/* **************************************************************** */ - - -/* An index element... */ -typedef struct index_elt -{ - struct index_elt *next; - char *entry; /* The index entry itself. */ - char *node; /* The node from whence it came. */ - int code; /* Nonzero means add `@code{...}' when - printing this element. */ - int defining_line; /* Line number where this entry was written. */ - char *defining_file; /* Source file for defining_line. */ -} INDEX_ELT; - -/* A list of short-names for each index. - - There are two indices into the the_indices array. - - * read_index is the index that points to the list of index - entries that we will find if we ask for the list of entries for - this name. - - * write_index is the index that points to the list of index entries - that we will add new entries to. - - Initially, read_index and write index are the same, but the - @syncodeindex and @synindex commands can change the list we add - entries to. - - For example, after the commands - - @cindex foo - @defindex ii - @synindex cp ii - @cindex bar - - the cp index will contain the entry `foo', and the new ii - index will contain the entry `bar'. This is consistent with the - way texinfo.tex handles the same situation. - - In addition, for each index, it is remembered whether that index is - a code index or not. Code indices have @code{} inserted around the - first word when they are printed with printindex. */ -typedef struct -{ - char *name; - int read_index; /* index entries for `name' */ - int write_index; /* store index entries here, @synindex can change it */ - int code; -} INDEX_ALIST; - -INDEX_ALIST **name_index_alist = (INDEX_ALIST **) NULL; - -/* An array of pointers. Each one is for a different index. The - "synindex" command changes which array slot is pointed to by a - given "index". */ -INDEX_ELT **the_indices = (INDEX_ELT **) NULL; - -/* The number of defined indices. */ -int defined_indices = 0; - -void -init_indices () -{ - int i; - - /* Create the default data structures. */ - - /* Initialize data space. */ - if (!the_indices) - { - the_indices = (INDEX_ELT **) xmalloc ((1 + defined_indices) * - sizeof (INDEX_ELT *)); - the_indices[defined_indices] = (INDEX_ELT *) NULL; - - name_index_alist = (INDEX_ALIST **) xmalloc ((1 + defined_indices) * - sizeof (INDEX_ALIST *)); - name_index_alist[defined_indices] = (INDEX_ALIST *) NULL; - } - - /* If there were existing indices, get rid of them now. */ - for (i = 0; i < defined_indices; i++) - { - undefindex (name_index_alist[i]->name); - if (name_index_alist[i]) - { /* Suppose we're called with two input files, and the first - does a @synindex pg cp. Then, when we get here to start - the second file, the "pg" element won't get freed by - undefindex (because it's pointing to "cp"). So free it - here; otherwise, when we try to define the pg index again - just below, it will still point to cp. */ - free (name_index_alist[i]->name); - free (name_index_alist[i]); - name_index_alist[i] = (INDEX_ALIST *) NULL; - } - } - - /* Add the default indices. */ - top_defindex ("cp", 0); /* cp is the only non-code index. */ - top_defindex ("fn", 1); - top_defindex ("ky", 1); - top_defindex ("pg", 1); - top_defindex ("tp", 1); - top_defindex ("vr", 1); -} - -/* Find which element in the known list of indices has this name. - Returns -1 if NAME isn't found. */ -int -find_index_offset (name) - char *name; -{ - register int i; - for (i = 0; i < defined_indices; i++) - if (name_index_alist[i] && - strcmp (name, name_index_alist[i]->name) == 0) - return (i); - return (-1); -} - -/* Return a pointer to the entry of (name . index) for this name. - Return NULL if the index doesn't exist. */ -INDEX_ALIST * -find_index (name) - char *name; -{ - int offset = find_index_offset (name); - if (offset > -1) - return (name_index_alist[offset]); - else - return ((INDEX_ALIST *) NULL); -} - -/* Given an index name, return the offset in the_indices of this index, - or -1 if there is no such index. */ -int -translate_index (name) - char *name; -{ - INDEX_ALIST *which = find_index (name); - - if (which) - return (which->read_index); - else - return (-1); -} - -/* Return the index list which belongs to NAME. */ -INDEX_ELT * -index_list (name) - char *name; -{ - int which = translate_index (name); - if (which < 0) - return ((INDEX_ELT *) -1); - else - return (the_indices[which]); -} - -/* Please release me, let me go... */ -void -free_index (index) - INDEX_ELT *index; -{ - INDEX_ELT *temp; - - while ((temp = index) != (INDEX_ELT *) NULL) - { - free (temp->entry); - /* Do not free the node, because we already freed the tag table, - which freed all the node names. */ - /* free (temp->node); */ - index = index->next; - free (temp); - } -} - -/* Flush an index by name. This will delete the list of entries that - would be written by a @printindex command for this index. */ -void -undefindex (name) - char *name; -{ - int i; - int which = find_index_offset (name); - - /* The index might have already been freed if this was the target of - an @synindex. */ - if (which < 0 || !name_index_alist[which]) - return; - - i = name_index_alist[which]->read_index; - - free_index (the_indices[i]); - the_indices[i] = (INDEX_ELT *) NULL; - - free (name_index_alist[which]->name); - free (name_index_alist[which]); - name_index_alist[which] = (INDEX_ALIST *) NULL; -} - -/* Define an index known as NAME. We assign the slot number. - CODE if Nonzero says to make this a code index. */ -void -defindex (name, code) - char *name; - int code; -{ - register int i, slot; - - /* If it already exists, flush it. */ - undefindex (name); - - /* Try to find an empty slot. */ - slot = -1; - for (i = 0; i < defined_indices; i++) - if (!name_index_alist[i]) - { - slot = i; - break; - } - - if (slot < 0) - { - /* No such luck. Make space for another index. */ - slot = defined_indices; - defined_indices++; - - name_index_alist = (INDEX_ALIST **) - xrealloc ((char *)name_index_alist, - (1 + defined_indices) * sizeof (INDEX_ALIST *)); - the_indices = (INDEX_ELT **) - xrealloc ((char *)the_indices, - (1 + defined_indices) * sizeof (INDEX_ELT *)); - } - - /* We have a slot. Start assigning. */ - name_index_alist[slot] = (INDEX_ALIST *) xmalloc (sizeof (INDEX_ALIST)); - name_index_alist[slot]->name = xstrdup (name); - name_index_alist[slot]->read_index = slot; - name_index_alist[slot]->write_index = slot; - name_index_alist[slot]->code = code; - - the_indices[slot] = (INDEX_ELT *) NULL; -} - -/* Add the arguments to the current index command to the index NAME. */ -void -index_add_arg (name) - char *name; -{ - int which; - char *index_entry; - INDEX_ALIST *tem; - - tem = find_index (name); - - which = tem ? tem->write_index : -1; - -#if defined (HAVE_MACROS) - if (macro_expansion_output_stream && !executing_string) - append_to_expansion_output (input_text_offset + 1); -#endif /* HAVE_MACROS */ - - get_rest_of_line (&index_entry); - ignore_blank_line (); - -#if defined (HAVE_MACROS) - if (macro_expansion_output_stream && !executing_string) - { - int op_orig; - - remember_itext (input_text, input_text_offset); - op_orig = output_paragraph_offset; - me_execute_string (index_entry); - me_execute_string ("\n"); - output_paragraph_offset = op_orig; - } -#endif /* HAVE_MACROS */ - - if (which < 0) - { - line_error (_("Unknown index `%s'"), name); - free (index_entry); - } - else - { - INDEX_ELT *new = (INDEX_ELT *) xmalloc (sizeof (INDEX_ELT)); - new->next = the_indices[which]; - new->entry = index_entry; - new->node = current_node; - new->code = tem->code; - new->defining_line = line_number - 1; - new->defining_file = input_filename; - the_indices[which] = new; - } -} - -#define INDEX_COMMAND_SUFFIX "index" - -/* The function which user defined index commands call. */ -void -gen_index () -{ - char *name = xstrdup (command); - if (strlen (name) >= strlen ("index")) - name[strlen (name) - strlen ("index")] = 0; - index_add_arg (name); - free (name); -} - -void -top_defindex (name, code) - char *name; - int code; -{ - char *temp; - - temp = (char *) xmalloc (1 + strlen (name) + strlen ("index")); - sprintf (temp, "%sindex", name); - define_user_command (temp, gen_index, 0); - defindex (name, code); - free (temp); -} - -/* Define a new index command. Arg is name of index. */ -void -cm_defindex () -{ - gen_defindex (0); -} - -void -cm_defcodeindex () -{ - gen_defindex (1); -} - -void -gen_defindex (code) - int code; -{ - char *name; - get_rest_of_line (&name); - - if (find_index (name)) - { - line_error (_("Index `%s' already exists"), name); - free (name); - return; - } - else - { - char *temp = (char *) alloca (1 + strlen (name) + strlen ("index")); - sprintf (temp, "%sindex", name); - define_user_command (temp, gen_index, 0); - defindex (name, code); - free (name); - } -} - -/* Expects 2 args, on the same line. Both are index abbreviations. - Make the first one be a synonym for the second one, i.e. make the - first one have the same index as the second one. */ -void -cm_synindex () -{ - int source, target; - char *abbrev1, *abbrev2; - - skip_whitespace (); - get_until_in_line (0, " ", &abbrev1); - target = find_index_offset (abbrev1); - skip_whitespace (); - get_until_in_line (0, " ", &abbrev2); - source = find_index_offset (abbrev2); - if (source < 0 || target < 0) - { - line_error (_("Unknown index `%s' and/or `%s' in @synindex"), - abbrev1, abbrev2); - } - else - { - name_index_alist[target]->write_index - = name_index_alist[source]->write_index; - } - - free (abbrev1); - free (abbrev2); -} - -void -cm_pindex () /* Pinhead index. */ -{ - index_add_arg ("pg"); -} - -void -cm_vindex () /* Variable index. */ -{ - index_add_arg ("vr"); -} - -void -cm_kindex () /* Key index. */ -{ - index_add_arg ("ky"); -} - -void -cm_cindex () /* Concept index. */ -{ - index_add_arg ("cp"); -} - -void -cm_findex () /* Function index. */ -{ - index_add_arg ("fn"); -} - -void -cm_tindex () /* Data Type index. */ -{ - index_add_arg ("tp"); -} - -/* Sorting the index. */ -int -index_element_compare (element1, element2) - INDEX_ELT **element1, **element2; -{ - return (strcasecmp ((*element1)->entry, (*element2)->entry)); -} - -/* Force all index entries to be unique. */ -void -make_index_entries_unique (array, count) - INDEX_ELT **array; - int count; -{ - register int i, j; - INDEX_ELT **copy; - int counter = 1; - - copy = (INDEX_ELT **)xmalloc ((1 + count) * sizeof (INDEX_ELT *)); - - for (i = 0, j = 0; i < count; i++) - { - if ((i == (count - 1)) || - (array[i]->node != array[i + 1]->node) || - (strcmp (array[i]->entry, array[i + 1]->entry) != 0)) - copy[j++] = array[i]; - else - { - free (array[i]->entry); - free (array[i]); - } - } - copy[j] = (INDEX_ELT *)NULL; - - /* Now COPY contains only unique entries. Duplicated entries in the - original array have been freed. Replace the current array with - the copy, fixing the NEXT pointers. */ - for (i = 0; copy[i] != (INDEX_ELT *)NULL; i++) - { - - copy[i]->next = copy[i + 1]; - - /* Fix entry names which are the same. They point to different nodes, - so we make the entry name unique. */ - if ((copy[i + 1] != (INDEX_ELT *)NULL) && - (strcmp (copy[i]->entry, copy[i + 1]->entry) == 0)) - { - char *new_entry_name; - - new_entry_name = (char *)xmalloc (10 + strlen (copy[i]->entry)); - sprintf (new_entry_name, "%s <%d>", copy[i]->entry, counter); - free (copy[i]->entry); - copy[i]->entry = new_entry_name; - counter++; - } - else - counter = 1; - - array[i] = copy[i]; - } - array[i] = (INDEX_ELT *)NULL; - - /* Free the storage used only by COPY. */ - free (copy); -} - -/* Sort the index passed in INDEX, returning an array of - pointers to elements. The array is terminated with a NULL - pointer. We call qsort because it's supposed to be fast. - I think this looks bad. */ -INDEX_ELT ** -sort_index (index) - INDEX_ELT *index; -{ - INDEX_ELT **array; - INDEX_ELT *temp = index; - int count = 0; - int save_line_number = line_number; - char *save_input_filename = input_filename; - - while (temp != (INDEX_ELT *) NULL) - { - count++; - temp = temp->next; - } - - /* We have the length. Make an array. */ - - array = (INDEX_ELT **) xmalloc ((count + 1) * sizeof (INDEX_ELT *)); - count = 0; - temp = index; - - while (temp != (INDEX_ELT *) NULL) - { - array[count++] = temp; - - /* Set line number and input filename to the source line for this - index entry, as this expansion finds any errors. */ - line_number = array[count - 1]->defining_line; - input_filename = array[count - 1]->defining_file; - - /* If this particular entry should be printed as a "code" index, - then wrap the entry with "@code{...}". */ - array[count - 1]->entry = expansion (temp->entry, index->code); - - temp = temp->next; - } - array[count] = (INDEX_ELT *) NULL; /* terminate the array. */ - line_number = save_line_number; - input_filename = save_input_filename; - - /* Sort the array. */ - qsort (array, count, sizeof (INDEX_ELT *), index_element_compare); - make_index_entries_unique (array, count); - return (array); -} - -/* Nonzero means that we are in the middle of printing an index. */ -int printing_index = 0; - -/* Takes one arg, a short name of an index to print. - Outputs a menu of the sorted elements of the index. */ -void -cm_printindex () -{ - int item; - INDEX_ELT *index; - INDEX_ELT **array; - char *index_name; - unsigned line_length; - char *line; - int saved_inhibit_paragraph_indentation = inhibit_paragraph_indentation; - int saved_filling_enabled = filling_enabled; - - close_paragraph (); - get_rest_of_line (&index_name); - - index = index_list (index_name); - if (index == (INDEX_ELT *)-1) - { - line_error (_("Unknown index `%s' in @printindex"), index_name); - free (index_name); - return; - } - else - free (index_name); - - /* Do this before sorting, so execute_string in index_element_compare - will give the same results as when we actually print. */ - printing_index = 1; - filling_enabled = 0; - inhibit_paragraph_indentation = 1; - array = sort_index (index); - - close_paragraph (); - add_word (_("* Menu:\n\n")); - -#if defined (HAVE_MACROS) - me_inhibit_expansion++; -#endif /* HAVE_MACROS */ - - /* This will probably be enough. */ - line_length = 100; - line = xmalloc (line_length); - - for (item = 0; (index = array[item]); item++) - { - /* A pathological document might have an index entry outside of any - node. Don't crash. Perhaps should warn. */ - char *index_node = index->node ? index->node : "(none)"; - unsigned new_length = strlen (index->entry); - - if (new_length < 37) /* minimum length used below */ - new_length = 37; - new_length += strlen (index_node) + 7; /* * : .\n\0 */ - - if (new_length > line_length) - { - line_length = new_length; - line = xrealloc (line, line_length); - } - - /* Print the entry, nicely formatted. We've already expanded any - commands, including any implicit @code. Thus, can't call - execute_string, since @@ has turned into @. */ - sprintf (line, "* %-37s %s.\n", index->entry, index_node); - line[2 + strlen (index->entry)] = ':'; - insert_string (line); - - /* Previous `output_paragraph' from growing to the size of the - whole index. */ - flush_output (); - } - - free (line); - -#if defined (HAVE_MACROS) - me_inhibit_expansion--; -#endif /* HAVE_MACROS */ - - printing_index = 0; - free (array); - close_single_paragraph (); - filling_enabled = saved_filling_enabled; - inhibit_paragraph_indentation = saved_inhibit_paragraph_indentation; -} - -/* User-defined commands, which happens only from user-defined indexes. */ - -void -define_user_command (name, proc, needs_braces_p) - char *name; - COMMAND_FUNCTION *proc; - int needs_braces_p; -{ - int slot = user_command_array_len; - user_command_array_len++; - - if (!user_command_array) - user_command_array = (COMMAND **) xmalloc (1 * sizeof (COMMAND *)); - - user_command_array = (COMMAND **) xrealloc (user_command_array, - (1 + user_command_array_len) * - sizeof (COMMAND *)); - - user_command_array[slot] = (COMMAND *) xmalloc (sizeof (COMMAND)); - user_command_array[slot]->name = xstrdup (name); - user_command_array[slot]->proc = proc; - user_command_array[slot]->argument_in_braces = needs_braces_p; -} - -/* Some support for footnotes. */ - -/* Footnotes are a new construct in Info. We don't know the best method - of implementing them for sure, so we present two possiblities. - - SeparateNode: - Make them look like followed references, with the reference - destinations in a makeinfo manufactured node or, - - EndNode: - Make them appear at the bottom of the node that they originally - appeared in. */ -#define SeparateNode 0 -#define EndNode 1 - -int footnote_style = EndNode; -int first_footnote_this_node = 1; -int footnote_count = 0; - -/* Set the footnote style based on he style identifier in STRING. */ -int -set_footnote_style (string) - char *string; -{ - if ((strcasecmp (string, "separate") == 0) || - (strcasecmp (string, "MN") == 0)) - footnote_style = SeparateNode; - else if ((strcasecmp (string, "end") == 0) || - (strcasecmp (string, "EN") == 0)) - footnote_style = EndNode; - else - return (-1); - - return (0); -} - -void -cm_footnotestyle () -{ - char *arg; - - get_rest_of_line (&arg); - - /* If set on command line, do not change the footnote style. */ - if (!footnote_style_preset && set_footnote_style (arg) != 0) - line_error ("Bad argument to %c%s", COMMAND_PREFIX, command); - - free (arg); -} - -typedef struct fn -{ - struct fn *next; - char *marker; - char *note; -} FN; - -FN *pending_notes = (FN *) NULL; - -/* A method for remembering footnotes. Note that this list gets output - at the end of the current node. */ -void -remember_note (marker, note) - char *marker, *note; -{ - FN *temp = (FN *) xmalloc (sizeof (FN)); - - temp->marker = xstrdup (marker); - temp->note = xstrdup (note); - temp->next = pending_notes; - pending_notes = temp; - footnote_count++; -} - -/* How to get rid of existing footnotes. */ -void -free_pending_notes () -{ - FN *temp; - - while ((temp = pending_notes) != (FN *) NULL) - { - free (temp->marker); - free (temp->note); - pending_notes = pending_notes->next; - free (temp); - } - first_footnote_this_node = 1; - footnote_count = 0; -} - -/* What to do when you see a @footnote construct. */ - - /* Handle a "footnote". - footnote *{this is a footnote} - where "*" is the (optional) marker character for this note. */ -void -cm_footnote () -{ - char *marker; - char *note; - - get_until ("{", &marker); - canon_white (marker); - - if (macro_expansion_output_stream && !executing_string) - append_to_expansion_output (input_text_offset + 1); /* include the { */ - - /* Read the argument in braces. */ - if (curchar () != '{') - { - line_error (_("`%c%s' needs an argument `{...}', not just `%s'"), - COMMAND_PREFIX, command, marker); - free (marker); - return; - } - else - { - int len; - int braces = 1; - int loc = ++input_text_offset; - - while (braces) - { - if (loc == size_of_input_text) - { - line_error (_("No closing brace for footnote `%s'"), marker); - return; - } - - if (input_text[loc] == '{') - braces++; - else if (input_text[loc] == '}') - braces--; - else if (input_text[loc] == '\n') - line_number++; - - loc++; - } - - len = (loc - input_text_offset) - 1; - note = (char *)xmalloc (len + 1); - strncpy (note, &input_text[input_text_offset], len); - note[len] = 0; - input_text_offset = loc; - } - - /* Must write the macro-expanded argument to the macro expansion - output stream. This is like the case in index_add_arg. */ - if (macro_expansion_output_stream && !executing_string) - { - int op_orig; - - remember_itext (input_text, input_text_offset); - op_orig = output_paragraph_offset; - me_execute_string (note); - /* Calling me_execute_string on a lone } provokes an error, since - as far as the reader knows there is no matching {. We wrote - the { above in the call to append_to_expansion_output. */ - write_region_to_macro_output ("}", 0, 1); - output_paragraph_offset = op_orig; - } - - if (!current_node || !*current_node) - { - line_error (_("Footnote defined without parent node")); - free (marker); - free (note); - return; - } - - if (!*marker) - { - free (marker); - - if (number_footnotes) - { - marker = (char *)xmalloc (10); - sprintf (marker, "%d", current_footnote_number); - current_footnote_number++; - } - else - marker = xstrdup ("*"); - } - - remember_note (marker, note); - - /* Your method should at least insert MARKER. */ - switch (footnote_style) - { - case SeparateNode: - add_word_args ("(%s)", marker); - if (first_footnote_this_node) - { - char *temp_string; - - temp_string = (char *) - xmalloc ((strlen (current_node)) + (strlen (_("-Footnotes"))) + 1); - - add_word_args (" (*note %s-Footnotes::)", current_node); - strcpy (temp_string, current_node); - strcat (temp_string, "-Footnotes"); - remember_node_reference (temp_string, line_number, followed_reference); - free (temp_string); - first_footnote_this_node = 0; - } - break; - - case EndNode: - add_word_args ("(%s)", marker); - break; - - default: - break; - } - free (marker); - free (note); -} - -/* Nonzero means that we are currently in the process of outputting - footnotes. */ -int already_outputting_pending_notes = 0; - -/* Output the footnotes. We are at the end of the current node. */ -void -output_pending_notes () -{ - FN *footnote = pending_notes; - - if (!pending_notes) - return; - - switch (footnote_style) - { - case SeparateNode: - { - char *old_current_node = current_node; - char *old_command = xstrdup (command); - - already_outputting_pending_notes++; - execute_string ("%cnode %s-Footnotes,,,%s\n", - COMMAND_PREFIX, current_node, current_node); - already_outputting_pending_notes--; - current_node = old_current_node; - free (command); - command = old_command; - } - break; - - case EndNode: - close_paragraph (); - in_fixed_width_font++; - execute_string (_("---------- Footnotes ----------\n\n")); - in_fixed_width_font--; - break; - } - - /* Handle the footnotes in reverse order. */ - { - FN **array = (FN **) xmalloc ((footnote_count + 1) * sizeof (FN *)); - - array[footnote_count] = (FN *) NULL; - - while (--footnote_count > -1) - { - array[footnote_count] = footnote; - footnote = footnote->next; - } - - filling_enabled = 1; - indented_fill = 1; - - while ((footnote = array[++footnote_count])) - { - execute_string ("(%s) %s", footnote->marker, footnote->note); - close_paragraph (); - } - close_paragraph (); - free (array); - } -} - -/* **************************************************************** */ -/* */ -/* User definable Macros (text substitution) */ -/* */ -/* **************************************************************** */ - -#if defined (HAVE_MACROS) - -/* Array of macros and definitions. */ -MACRO_DEF **macro_list = (MACRO_DEF **)NULL; - -int macro_list_len = 0; /* Number of elements. */ -int macro_list_size = 0; /* Number of slots in total. */ - -/* Return the macro definition of NAME or NULL if NAME is not defined. */ -MACRO_DEF * -find_macro (name) - char *name; -{ - register int i; - register MACRO_DEF *def; - - def = (MACRO_DEF *)NULL; - for (i = 0; macro_list && (def = macro_list[i]); i++) - { - if ((!def->inhibited) && (strcmp (def->name, name) == 0)) - break; - } - return (def); -} - -/* Add the macro NAME with ARGLIST and BODY to the list of defined macros. - SOURCE_FILE is the name of the file where this definition can be found, - and SOURCE_LINENO is the line number within that file. If a macro already - exists with NAME, then a warning is produced, and that previous - definition is overwritten. */ -void -add_macro (name, arglist, body, source_file, source_lineno, flags) - char *name; - char **arglist; - char *body; - char *source_file; - int source_lineno, flags; -{ - register MACRO_DEF *def; - - def = find_macro (name); - - if (!def) - { - if (macro_list_len + 2 >= macro_list_size) - macro_list = (MACRO_DEF **)xrealloc - (macro_list, ((macro_list_size += 10) * sizeof (MACRO_DEF *))); - - macro_list[macro_list_len] = (MACRO_DEF *)xmalloc (sizeof (MACRO_DEF)); - macro_list[macro_list_len + 1] = (MACRO_DEF *)NULL; - - def = macro_list[macro_list_len]; - macro_list_len += 1; - def->name = name; - } - else - { - char *temp_filename = input_filename; - int temp_line = line_number; - - warning (_("macro `%s' previously defined"), name); - - input_filename = def->source_file; - line_number = def->source_lineno; - warning (_("here is the previous definition of `%s'"), name); - - input_filename = temp_filename; - line_number = temp_line; - - if (def->arglist) - { - register int i; - - for (i = 0; def->arglist[i]; i++) - free (def->arglist[i]); - - free (def->arglist); - } - free (def->source_file); - free (def->body); - } - - def->source_file = xstrdup (source_file); - def->source_lineno = source_lineno; - def->body = body; - def->arglist = arglist; - def->inhibited = 0; - def->flags = flags; -} - -/* Delete the macro with name NAME. The macro is deleted from the list, - but it is also returned. If there was no macro defined, NULL is - returned. */ -MACRO_DEF * -delete_macro (name) - char *name; -{ - register int i; - register MACRO_DEF *def; - - def = (MACRO_DEF *)NULL; - - for (i = 0; macro_list && (def = macro_list[i]); i++) - if (strcmp (def->name, name) == 0) - { - memmove (macro_list + i, macro_list + i + 1, - ((macro_list_len + 1) - i) * sizeof (MACRO_DEF *)); - macro_list_len--; - break; - } - return (def); -} - -/* Return the arglist on the current line. This can behave in two different - ways, depending on the variable BRACES_REQUIRED_FOR_MACRO_ARGS. */ -int braces_required_for_macro_args = 0; - -char ** -get_macro_args (def) - MACRO_DEF *def; -{ - register int i; - char *word; - - /* Quickly check to see if this macro has been invoked with any arguments. - If not, then don't skip any of the following whitespace. */ - for (i = input_text_offset; i < size_of_input_text; i++) - if (!cr_or_whitespace (input_text[i])) - break; - - if (input_text[i] != '{') - { - if (braces_required_for_macro_args) - { - return ((char **)NULL); - } - else - { - /* Braces are not required to fill out the macro arguments. If - this macro takes one argument, it is considered to be the - remainder of the line, sans whitespace. */ - if (def->arglist && def->arglist[0] && !def->arglist[1]) - { - char **arglist; - - get_rest_of_line (&word); - if (input_text[input_text_offset - 1] == '\n') - { - input_text_offset--; - line_number--; - } - /* canon_white (word); */ - arglist = (char **)xmalloc (2 * sizeof (char *)); - arglist[0] = word; - arglist[1] = (char *)NULL; - return (arglist); - } - else - { - /* The macro either took no arguments, or took more than - one argument. In that case, it must be invoked with - arguments surrounded by braces. */ - return ((char **)NULL); - } - } - } - return (get_brace_args (def->flags & ME_QUOTE_ARG)); -} - -/* Substitute actual parameters for named parameters in body. - The named parameters which appear in BODY must by surrounded - reverse slashes, as in \foo\. */ -char * -apply (named, actuals, body) - char **named, **actuals, *body; -{ - register int i; - int new_body_index, new_body_size; - char *new_body, *text; - int length_of_actuals; - - length_of_actuals = array_len (actuals); - new_body_size = strlen (body); - new_body = (char *)xmalloc (1 + new_body_size); - - /* Copy chars from BODY into NEW_BODY. */ - i = 0; new_body_index = 0; - - while (1) - { - if (!body[i]) - break; - - if (body[i] != '\\') - new_body[new_body_index++] = body[i++]; - else - { - /* Snarf parameter name, check against named parameters. */ - char *param; - int param_start, which, len; - - param_start = ++i; - while ((body[i]) && (body[i] != '\\')) - i++; - - len = i - param_start; - param = (char *)xmalloc (1 + len); - memcpy (param, body + param_start, len); - param[len] = 0; - - if (body[i]) /* move past \ */ - i++; - - /* Now check against named parameters. */ - for (which = 0; named && named[which]; which++) - if (strcmp (named[which], param) == 0) - break; - - if (named && named[which]) - { - if (which < length_of_actuals) - text = actuals[which]; - else - text = (char *)NULL; - - if (!text) - text = ""; - - len = strlen (text); - } - else - { /* not a parameter, restore \'s */ - i = body[i] ? (i - 1) : i; - len++; - text = xmalloc (1 + len); - sprintf (text, "\\%s", param); - } - - if ((2 + strlen (param)) < len) - { - new_body_size += len + 1; - new_body = xrealloc (new_body, new_body_size); - } - - free (param); - - strcpy (new_body + new_body_index, text); - new_body_index += len; - - if (!named || !named[which]) - free (text); - } - } - new_body[new_body_index] = 0; - return (new_body); -} - -/* Execute the macro passed in DEF, a pointer to a MACRO_DEF. */ -void -execute_macro (def) - MACRO_DEF *def; -{ - char **arglist; - int num_args; - char *execution_string = (char *)NULL; - - if (macro_expansion_output_stream && !executing_string && !me_inhibit_expansion) - me_append_before_this_command (); - - /* Find out how many arguments this macro definition takes. */ - num_args = array_len (def->arglist); - - /* Gather the arguments present on the line if there are any. */ - arglist = get_macro_args (def); - - if (num_args < array_len (arglist)) - { - free_array (arglist); - line_error (_("Macro `%s' called with too many args"), def->name); - return; - } - - if (def->body) - execution_string = apply (def->arglist, arglist, def->body); - - free_array (arglist); - - if (def->body) - { - if (macro_expansion_output_stream && !executing_string && !me_inhibit_expansion) - { - remember_itext (input_text, input_text_offset); - me_execute_string (execution_string); - } - else - execute_string ("%s", execution_string); - - free (execution_string); - } -} - -/* Read and remember the definition of a macro. */ -void -cm_macro () -{ - register int i; - char *name, **arglist, *body, *line; - int body_size, body_index; - int depth = 1; - int defining_line = line_number; - int flags = 0; - - arglist = (char **)NULL; - body = (char *)NULL; - body_size = 0; - body_index = 0; - - if (macro_expansion_output_stream && !executing_string) - me_append_before_this_command (); - - skip_whitespace (); - - /* Get the name of the macro. This is the set of characters which are - not whitespace and are not `{' immediately following the @macro. */ - { - int start = input_text_offset; - int len; - - for (i = start; - (i < size_of_input_text) && - (input_text[i] != '{') && - (!cr_or_whitespace (input_text[i])); - i++); - - len = i - start; - name = (char *)xmalloc (1 + len); - strncpy (name, input_text + start, len); - name[len] = 0; - input_text_offset = i; - } - - skip_whitespace (); - - /* It is not required that the definition of a macro includes an arglist. - If not, don't try to get the named parameters, just use a null list. */ - if (curchar () == '{') - { - int arglist_index = 0, arglist_size = 0; - int gathering_words = 1; - char *word = (char *)NULL; - int character; - - /* Read the words inside of the braces which determine the arglist. - These words will be replaced within the body of the macro at - execution time. */ - - input_text_offset++; - skip_whitespace_and_newlines (); - - while (gathering_words) - { - int len; - - for (i = input_text_offset; - (character = input_text[i]); - i++) - { - switch (character) - { - case '\n': - line_number++; - case ' ': - case '\t': - case ',': - case '}': - /* Found the end of the current arglist word. Save it. */ - len = i - input_text_offset; - word = (char *)xmalloc (1 + len); - strncpy (word, input_text + input_text_offset, len); - word[len] = 0; - input_text_offset = i; - - /* Advance to the comma or close-brace that signified - the end of the argument. */ - while ((character = curchar ()) - && character != ',' - && character != '}') - { - input_text_offset++; - if (character == '\n') - line_number++; - } - - /* Add the word to our list of words. */ - if ((arglist_index + 2) >= arglist_size) - arglist = (char **)xrealloc - (arglist, (arglist_size += 10) * sizeof (char *)); - - arglist[arglist_index++] = word; - arglist[arglist_index] = (char *)NULL; - break; - } - - if (character == '}') - { - input_text_offset++; - gathering_words = 0; - break; - } - - if (character == ',') - { - input_text_offset++; - skip_whitespace_and_newlines (); - i = input_text_offset - 1; - } - } - } - } - - /* Read the text carefully until we find an "@end macro" which - matches this one. The text in between is the body of the macro. */ - skip_whitespace_and_newlines (); - - while (depth) - { - if ((input_text_offset + 9) > size_of_input_text) - { - int temp_line = line_number; - line_number = defining_line; - line_error (_("%cend macro not found"), COMMAND_PREFIX); - line_number = temp_line; - return; - } - - get_rest_of_line (&line); - - /* Handle commands only meaningful within a macro. */ - if ((*line == COMMAND_PREFIX) && (depth == 1) && - (strncmp (line + 1, "allow-recursion", 15) == 0) && - (line[16] == 0 || whitespace (line[16]))) - { - for (i = 16; whitespace (line[i]); i++); - strcpy (line, line + i); - flags |= ME_RECURSE; - if (!*line) - { - free (line); - continue; - } - } - - if ((*line == COMMAND_PREFIX) && (depth == 1) && - (strncmp (line + 1, "quote-arg", 9) == 0) && - (line[10] == 0 || whitespace (line[10]))) - { - for (i = 10; whitespace (line[i]); i++); - strcpy (line, line + i); - - if (arglist && arglist[0] && !arglist[1]) - { - flags |= ME_QUOTE_ARG; - if (!*line) - { - free (line); - continue; - } - } - else - { - line_error (_("%cquote-arg only useful when the macro takes a single argument"), - COMMAND_PREFIX); - } - } - - if ((*line == COMMAND_PREFIX) && - (strncmp (line + 1, "macro ", 6) == 0)) - depth++; - - if ((*line == COMMAND_PREFIX) && - (strncmp (line + 1, "end macro", 9) == 0)) - depth--; - - if (depth) - { - if ((body_index + strlen (line) + 3) >= body_size) - body = (char *)xrealloc - (body, body_size += 3 + strlen (line)); - strcpy (body + body_index, line); - body_index += strlen (line); - body[body_index++] = '\n'; - body[body_index] = 0; - } - free (line); - } - - /* If it was an empty macro like - @macro foo - @end macro - create an empty body. (Otherwise, the macro is not expanded.) */ - if (!body) - { - body = (char *)malloc(1); - *body = 0; - } - - /* We now have the name, the arglist, and the body. However, BODY - includes the final newline which preceded the `@end macro' text. - Delete it. */ - if (body && strlen (body)) - body[strlen (body) - 1] = 0; - - add_macro (name, arglist, body, input_filename, defining_line, flags); - - if (macro_expansion_output_stream && !executing_string) - remember_itext (input_text, input_text_offset); -} - -void -cm_unmacro () -{ - register int i; - char *line, *name; - MACRO_DEF *def; - - if (macro_expansion_output_stream && !executing_string) - me_append_before_this_command (); - - get_rest_of_line (&line); - - for (i = 0; line[i] && !whitespace (line[i]); i++); - name = (char *)xmalloc (i + 1); - strncpy (name, line, i); - name[i] = 0; - - def = delete_macro (name); - - if (def) - { - free (def->source_file); - free (def->name); - free (def->body); - - if (def->arglist) - { - register int i; - - for (i = 0; def->arglist[i]; i++) - free (def->arglist[i]); - - free (def->arglist); - } - - free (def); - } - - free (line); - free (name); - - if (macro_expansion_output_stream && !executing_string) - remember_itext (input_text, input_text_offset); -} - -/* How to output sections of the input file verbatim. */ - -/* Set the value of POINTER's offset to OFFSET. */ -ITEXT * -remember_itext (pointer, offset) - char *pointer; - int offset; -{ - register int i; - ITEXT *itext = (ITEXT *)NULL; - - /* If we have no info, initialize a blank list. */ - if (!itext_info) - { - itext_info = (ITEXT **)xmalloc ((itext_size = 10) * sizeof (ITEXT *)); - for (i = 0; i < itext_size; i++) - itext_info[i] = (ITEXT *)NULL; - } - - /* If the pointer is already present in the list, then set the offset. */ - for (i = 0; i < itext_size; i++) - if ((itext_info[i] != (ITEXT *)NULL) && - (itext_info[i]->pointer == pointer)) - { - itext = itext_info[i]; - itext_info[i]->offset = offset; - break; - } - - if (i == itext_size) - { - /* Find a blank slot (or create a new one), and remember the - pointer and offset. */ - for (i = 0; i < itext_size; i++) - if (itext_info[i] == (ITEXT *)NULL) - break; - - /* If not found, then add some slots. */ - if (i == itext_size) - { - register int j; - - itext_info = (ITEXT **)xrealloc - (itext_info, (itext_size += 10) * sizeof (ITEXT *)); - - for (j = i; j < itext_size; j++) - itext_info[j] = (ITEXT *)NULL; - } - - /* Now add the pointer and the offset. */ - itext_info[i] = (ITEXT *)xmalloc (sizeof (ITEXT)); - itext_info[i]->pointer = pointer; - itext_info[i]->offset = offset; - itext = itext_info[i]; - } - return (itext); -} - -/* Forget the input text associated with POINTER. */ -void -forget_itext (pointer) - char *pointer; -{ - register int i; - - for (i = 0; i < itext_size; i++) - if (itext_info[i] && (itext_info[i]->pointer == pointer)) - { - free (itext_info[i]); - itext_info[i] = (ITEXT *)NULL; - break; - } -} - -/* Append the text which appeared in input_text from the last offset to - the character just before the command that we are currently executing. */ -void -me_append_before_this_command () -{ - register int i; - - for (i = input_text_offset; i && (input_text[i] != COMMAND_PREFIX); i--); - maybe_write_itext (input_text, i); -} - -/* Similar to execute_string (), but only takes a single string argument, - and remembers the input text location, etc. */ -void -me_execute_string (execution_string) - char *execution_string; -{ - pushfile (); - input_text_offset = 0; - input_text = execution_string; - input_filename = xstrdup (input_filename); - size_of_input_text = strlen (execution_string); - - remember_itext (execution_string, 0); - - me_executing_string++; - reader_loop (); - popfile (); - me_executing_string--; -} - -/* Append the text which appears in input_text from the last offset to - the current OFFSET. */ -void -append_to_expansion_output (offset) - int offset; -{ - register int i; - ITEXT *itext = (ITEXT *)NULL; - - for (i = 0; i < itext_size; i++) - if (itext_info[i] && itext_info[i]->pointer == input_text) - { - itext = itext_info[i]; - break; - } - - if (!itext) - return; - - if (offset > itext->offset) - { - write_region_to_macro_output - (input_text, itext->offset, offset); - remember_itext (input_text, offset); - } -} - -/* Only write this input text iff it appears in our itext list. */ -void -maybe_write_itext (pointer, offset) - char *pointer; - int offset; -{ - register int i; - ITEXT *itext = (ITEXT *)NULL; - - for (i = 0; i < itext_size; i++) - if (itext_info[i] && (itext_info[i]->pointer == pointer)) - { - itext = itext_info[i]; - break; - } - - if (itext && (itext->offset < offset)) - { - write_region_to_macro_output (itext->pointer, itext->offset, offset); - remember_itext (pointer, offset); - } -} - -void -write_region_to_macro_output (string, start, end) - char *string; - int start, end; -{ - if (macro_expansion_output_stream) - fwrite (string + start, 1, end - start, macro_expansion_output_stream); -} - -#endif /* HAVE_MACROS */ - -/* Return the length of the array in ARRAY. */ -int -array_len (array) - char **array; -{ - register int i = 0; - - if (array) - for (i = 0; array[i] != (char *)NULL; i++); - - return (i); -} - -void -free_array (array) - char **array; -{ - if (array) - { - register int i; - - for (i = 0; array[i] != (char *)NULL; i++) - free (array[i]); - - free (array); - } -} - -/* Function is used even when we don't have macros. Although, I have - to admit, it is unlikely that you would have a use for it if you - aren't using macros. */ -char ** -get_brace_args (quote_single) - int quote_single; -{ - char **arglist, *word; - int arglist_index, arglist_size; - int character, escape_seen, start; - int depth = 1; - - /* There is an arglist in braces here, so gather the args inside of it. */ - skip_whitespace_and_newlines (); - input_text_offset++; - arglist = (char **)NULL; - arglist_index = arglist_size = 0; - - get_arg: - skip_whitespace_and_newlines (); - start = input_text_offset; - escape_seen = 0; - - while ((character = curchar ())) - { - if (character == '\\') - { - input_text_offset += 2; - escape_seen = 1; - } - else if (character == '{') - { - depth++; - input_text_offset++; - } - else if ((character == ',' && !quote_single) || - ((character == '}') && depth == 1)) - { - int len = input_text_offset - start; - - if (len || (character != '}')) - { - word = (char *)xmalloc (1 + len); - strncpy (word, input_text + start, len); - word[len] = 0; - - /* Clean up escaped characters. */ - if (escape_seen) - { - register int i; - - for (i = 0; word[i]; i++) - if (word[i] == '\\') - memmove (word + i, word + i + 1, - 1 + strlen (word + i + 1)); - } - - if (arglist_index + 2 >= arglist_size) - arglist = (char **)xrealloc - (arglist, (arglist_size += 10) * sizeof (char *)); - - arglist[arglist_index++] = word; - arglist[arglist_index] = (char *)NULL; - } - - input_text_offset++; - if (character == '}') - break; - else - goto get_arg; - } - else if (character == '}') - { - depth--; - input_text_offset++; - } - else - { - input_text_offset++; - if (character == '\n') line_number++; - } - } - return (arglist); -} - -/* **************************************************************** */ -/* */ -/* Looking For Include Files */ -/* */ -/* **************************************************************** */ - -/* Given a string containing units of information separated by colons, - return the next one pointed to by INDEX, or NULL if there are no more. - Advance INDEX to the character after the colon. */ -char * -extract_colon_unit (string, index) - char *string; - int *index; -{ - int i, start; - - i = *index; - - if (!string || (i >= strlen (string))) - return ((char *)NULL); - - /* Each call to this routine leaves the index pointing at a colon if - there is more to the path. If I is > 0, then increment past the - `:'. If I is 0, then the path has a leading colon. Trailing colons - are handled OK by the `else' part of the if statement; an empty - string is returned in that case. */ - if (i && string[i] == ':') - i++; - - start = i; - - while (string[i] && string[i] != ':') i++; - - *index = i; - - if (i == start) - { - if (string[i]) - (*index)++; - - /* Return "" in the case of a trailing `:'. */ - return (xstrdup ("")); - } - else - { - char *value; - - value = (char *)xmalloc (1 + (i - start)); - strncpy (value, &string[start], (i - start)); - value [i - start] = 0; - - return (value); - } -} - -/* Return the full pathname for FILENAME by searching along PATH. - When found, return the stat () info for FILENAME in FINFO. - If PATH is NULL, only the current directory is searched. - If the file could not be found, return a NULL pointer. */ -char * -get_file_info_in_path (filename, path, finfo) - char *filename, *path; - struct stat *finfo; -{ - char *dir; - int result, index = 0; - - if (path == (char *)NULL) - path = "."; - - /* Handle absolute pathnames. "./foo", "/foo", "../foo". */ - if (*filename == '/' || - (*filename == '.' && - (filename[1] == '/' || - (filename[1] == '.' && filename[2] == '/'))) -#ifdef WIN32 - /* Handle names that look like "d:/foo/bar" */ - || (isalpha (*filename) && filename [1] == ':' - && (filename [2] == '/' || filename [2] == '\\')) -#endif - ) - { - if (stat (filename, finfo) == 0) - return (xstrdup (filename)); - else - return ((char *)NULL); - } - - while ((dir = extract_colon_unit (path, &index))) - { - char *fullpath; - - if (!*dir) - { - free (dir); - dir = xstrdup ("."); - } - - fullpath = (char *)xmalloc (2 + strlen (dir) + strlen (filename)); - sprintf (fullpath, "%s/%s", dir, filename); - free (dir); - - result = stat (fullpath, finfo); - - if (result == 0) - return (fullpath); - else - free (fullpath); - } - return NULL; + return 0; }