diff --git a/contrib/texinfo/info/doc.c b/contrib/texinfo/info/doc.c index 9c3b61593155..673839601123 100644 --- a/contrib/texinfo/info/doc.c +++ b/contrib/texinfo/info/doc.c @@ -1,129 +1,129 @@ /* doc.c -- Generated structure containing function names and doc strings. This file was automatically made from various source files with the command "./makedoc". DO NOT EDIT THIS FILE, only "./makedoc.c". Source files groveled to make this file include: ./session.c - ./echo_area.c + ./echo-area.c ./infodoc.c ./m-x.c ./indices.c ./nodemenu.c ./footnotes.c ./variables.c An entry in the array FUNCTION_DOC_ARRAY is made for each command found in the above files; each entry consists of a function pointer, a string which is the user-visible name of the function, and a string which documents its purpose. */ #include "doc.h" #include "funs.h" FUNCTION_DOC function_doc_array[] = { /* Commands found in "./session.c". */ { info_next_line, "next-line", "Move down to the next line" }, { info_prev_line, "prev-line", "Move up to the previous line" }, { info_end_of_line, "end-of-line", "Move to the end of the line" }, { info_beginning_of_line, "beginning-of-line", "Move to the start of the line" }, { info_forward_char, "forward-char", "Move forward a character" }, { info_backward_char, "backward-char", "Move backward a character" }, { info_forward_word, "forward-word", "Move forward a word" }, { info_backward_word, "backward-word", "Move backward a word" }, { 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_backward, "scroll-backward", "Scroll backward in this window" }, { 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_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_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_last_node, "last-node", "Select the last node in this file" }, { info_first_node, "first-node", "Select the first node in this file" }, - { info_history_node, "history-node", "Select the most recently selected node" }, { info_last_menu_item, "last-menu-item", "Select the last item in this node's menu" }, { info_menu_digit, "menu-digit", "Select this menu item" }, { info_menu_item, "menu-item", "Read a menu item and select its node" }, { info_xref_item, "xref-item", "Read a footnote or cross reference and select its node" }, { 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_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)'" }, + { info_history_node, "history-node", "Select the most recently selected node" }, { 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, "search", "Read a string and search for it" }, { 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_redraw_display, "redraw-display", "Redraw the display" }, { info_quit, "quit", "Quit using Info" }, { info_do_lowercase_version, "do-lowercase-version", "" }, { info_add_digit_to_numeric_arg, "add-digit-to-numeric-arg", "Add this digit to the current numeric argument" }, { info_universal_argument, "universal-argument", "Start (or multiply by 4) the current numeric argument" }, { info_numeric_arg_digit_loop, "numeric-arg-digit-loop", "Internally used by \\[universal-argument]" }, -/* Commands found in "./echo_area.c". */ +/* Commands found in "./echo-area.c". */ { ea_forward, "echo-area-forward", "Move forward a character" }, { ea_backward, "echo-area-backward", "Move backward a character" }, { ea_beg_of_line, "echo-area-beg-of-line", "Move to the start of this line" }, { ea_end_of_line, "echo-area-end-of-line", "Move to the end of this line" }, { ea_forward_word, "echo-area-forward-word", "Move forward a word" }, { ea_backward_word, "echo-area-backward-word", "Move backward a word" }, { ea_delete, "echo-area-delete", "Delete the character under the cursor" }, { ea_rubout, "echo-area-rubout", "Delete the character behind the cursor" }, { ea_abort, "echo-area-abort", "Cancel or quit operation" }, { ea_newline, "echo-area-newline", "Accept (or force completion of) this line" }, { ea_quoted_insert, "echo-area-quoted-insert", "Insert next character verbatim" }, { ea_insert, "echo-area-insert", "Insert this character" }, { ea_tab_insert, "echo-area-tab-insert", "Insert a TAB character" }, { ea_transpose_chars, "echo-area-transpose-chars", "Transpose characters at point" }, { ea_yank, "echo-area-yank", "Yank back the contents of the last kill" }, { ea_yank_pop, "echo-area-yank-pop", "Yank back a previous kill" }, { ea_kill_line, "echo-area-kill-line", "Kill to the end of the line" }, { ea_backward_kill_line, "echo-area-backward-kill-line", "Kill to the beginning of the line" }, { ea_kill_word, "echo-area-kill-word", "Kill the word following the cursor" }, { ea_backward_kill_word, "echo-area-backward-kill-word", "Kill the word preceding the cursor" }, { ea_possible_completions, "echo-area-possible-completions", "List possible completions" }, { ea_complete, "echo-area-complete", "Insert completion" }, { ea_scroll_completions_window, "echo-area-scroll-completions-window", "Scroll the completions window" }, /* Commands found in "./infodoc.c". */ { info_get_help_window, "get-help-window", "Display help message" }, { info_get_info_help_node, "get-info-help-node", "Visit Info node `(info)Help'" }, { describe_key, "describe-key", "Print documentation for KEY" }, { info_where_is, "where-is", "Show what to type to execute a given command" }, /* Commands found in "./m-x.c". */ { describe_command, "describe-command", "Read the name of an Info command and describe it" }, { info_execute_command, "execute-command", "Read a command name in the echo area and execute it" }, { set_screen_height, "set-screen-height", "Set the height of the displayed window" }, /* Commands found in "./indices.c". */ { info_index_search, "index-search", "Look up a string in the index for this file" }, { info_next_index_match, "next-index-match", "Go to the next matching index item from the last `\\[index-search]' command" }, { info_index_apropos, "index-apropos", "Grovel all known info file's indices for a string and build a menu" }, /* Commands found in "./nodemenu.c". */ { list_visited_nodes, "list-visited-nodes", "Make a window containing a menu of all of the currently visited nodes" }, { select_visited_node, "select-visited-node", "Select a node which has been previously visited in a visible window" }, /* Commands found in "./footnotes.c". */ { info_show_footnotes, "show-footnotes", "Show the footnotes associated with this node in another window" }, /* Commands found in "./variables.c". */ { describe_variable, "describe-variable", "Explain the use of a variable" }, { set_variable, "set-variable", "Set the value of an Info variable" }, { (VFunction *)NULL, (char *)NULL, (char *)NULL } }; diff --git a/contrib/texinfo/info/funs.h b/contrib/texinfo/info/funs.h index b91880c48086..32d80d51c037 100644 --- a/contrib/texinfo/info/funs.h +++ b/contrib/texinfo/info/funs.h @@ -1,111 +1,111 @@ /* funs.h -- Generated declarations for Info commands. */ /* Functions declared in "./session.c". */ extern void info_next_line (); extern void info_prev_line (); extern void info_end_of_line (); extern void info_beginning_of_line (); extern void info_forward_char (); extern void info_backward_char (); extern void info_forward_word (); 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_backward (); extern void info_beginning_of_node (); extern void info_end_of_node (); 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_grow_window (); extern void info_tile_windows (); extern void info_toggle_wrap (); extern void info_next_node (); extern void info_prev_node (); extern void info_up_node (); extern void info_last_node (); extern void info_first_node (); -extern void info_history_node (); extern void info_last_menu_item (); extern void info_menu_digit (); extern void info_menu_item (); extern void info_xref_item (); extern void info_find_menu (); extern void info_visit_menu (); extern void info_goto_node (); extern void info_man (); extern void info_top_node (); extern void info_dir_node (); +extern void info_history_node (); extern void info_kill_node (); extern void info_view_file (); extern void info_print_node (); extern void info_search (); extern void isearch_forward (); extern void isearch_backward (); extern void info_move_to_prev_xref (); extern void info_move_to_next_xref (); extern void info_select_reference_this_line (); extern void info_abort_key (); extern void info_move_to_window_line (); extern void info_redraw_display (); extern void info_quit (); extern void info_do_lowercase_version (); extern void info_add_digit_to_numeric_arg (); extern void info_universal_argument (); extern void info_numeric_arg_digit_loop (); -/* Functions declared in "./echo_area.c". */ +/* Functions declared in "./echo-area.c". */ extern void ea_forward (); extern void ea_backward (); extern void ea_beg_of_line (); extern void ea_end_of_line (); extern void ea_forward_word (); extern void ea_backward_word (); extern void ea_delete (); extern void ea_rubout (); extern void ea_abort (); extern void ea_newline (); extern void ea_quoted_insert (); extern void ea_insert (); extern void ea_tab_insert (); extern void ea_transpose_chars (); extern void ea_yank (); extern void ea_yank_pop (); extern void ea_kill_line (); extern void ea_backward_kill_line (); extern void ea_kill_word (); extern void ea_backward_kill_word (); extern void ea_possible_completions (); extern void ea_complete (); extern void ea_scroll_completions_window (); /* Functions declared in "./infodoc.c". */ extern void info_get_help_window (); extern void info_get_info_help_node (); extern void describe_key (); extern void info_where_is (); /* Functions declared in "./m-x.c". */ extern void describe_command (); extern void info_execute_command (); extern void set_screen_height (); /* Functions declared in "./indices.c". */ extern void info_index_search (); extern void info_next_index_match (); extern void info_index_apropos (); /* Functions declared in "./nodemenu.c". */ extern void list_visited_nodes (); extern void select_visited_node (); /* Functions declared in "./footnotes.c". */ extern void info_show_footnotes (); /* Functions declared in "./variables.c". */ extern void describe_variable (); extern void set_variable (); diff --git a/contrib/texinfo/info/infomap.c b/contrib/texinfo/info/infomap.c index e67712fcb308..7591283dc592 100644 --- a/contrib/texinfo/info/infomap.c +++ b/contrib/texinfo/info/infomap.c @@ -1,328 +1,368 @@ -/* infomap.c -- Keymaps for Info. */ +/* infomap.c -- Keymaps for Info. + $Id: infomap.c,v 1.7 1997/07/31 20:37:32 karl Exp $ -/* This file is part of GNU Info, a program for reading online documentation - stored in Info format. - - Copyright (C) 1993 Free Software Foundation, Inc. + Copyright (C) 1993, 97 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Written by Brian Fox (bfox@ai.mit.edu). */ -#include "stdio.h" -#include "ctype.h" +#include "info.h" #include "infomap.h" #include "funs.h" -#include "info.h" - -static void add_function_key(char *, VFunction *, Keymap); - -extern char *term_ku, *term_kd, *term_kr, *term_kl; -extern char *term_kP, *term_kN, *term_kh, *term_kH; +#include "terminal.h" /* Return a new keymap which has all the uppercase letters mapped to run the function info_do_lowercase_version (). */ Keymap keymap_make_keymap () { register int i; Keymap keymap; keymap = (Keymap)xmalloc (256 * sizeof (KEYMAP_ENTRY)); for (i = 0; i < 256; i++) { keymap[i].type = ISFUNC; keymap[i].function = (VFunction *)NULL; } for (i = 'A'; i < ('Z' + 1); i++) { keymap[i].type = ISFUNC; keymap[i].function = info_do_lowercase_version; } return (keymap); } /* Return a new keymap which is a copy of MAP. */ Keymap keymap_copy_keymap (map) Keymap map; { register int i; Keymap keymap; keymap = keymap_make_keymap (); for (i = 0; i < 256; i++) { keymap[i].type = map[i].type; keymap[i].function = map[i].function; } return (keymap); } /* Free the keymap and it's descendents. */ void keymap_discard_keymap (map) Keymap (map); { register int i; if (!map) return; for (i = 0; i < 256; i++) { switch (map[i].type) - { - case ISFUNC: - break; + { + case ISFUNC: + break; - case ISKMAP: - keymap_discard_keymap ((Keymap)map[i].function); - break; + case ISKMAP: + keymap_discard_keymap ((Keymap)map[i].function); + break; - } + } } } +/* Conditionally bind key sequence. */ +int +keymap_bind_keyseq (map, keyseq, keyentry) + Keymap map; + const unsigned char *keyseq; + KEYMAP_ENTRY *keyentry; +{ + register Keymap m = map; + register const unsigned char *s = keyseq; + register int c; + + if (s == NULL || *s == '\0') return 0; + + while ((c = *s++) != '\0') + { + switch (m[c].type) + { + case ISFUNC: + if (!(m[c].function == NULL || + (m != map && m[c].function == info_do_lowercase_version))) + return 0; + + if (*s != '\0') + { + m[c].type = ISKMAP; + m[c].function = (VFunction *)keymap_make_keymap (); + } + break; + + case ISKMAP: + if (*s == '\0') + return 0; + break; + } + if (*s != '\0') + { + m = (Keymap)m[c].function; + } + else + { + m[c] = *keyentry; + } + } + + return 1; +} + /* Initialize the standard info keymaps. */ Keymap info_keymap = (Keymap)NULL; Keymap echo_area_keymap = (Keymap)NULL; void initialize_info_keymaps () { register 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 (); + (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) 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; /* 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; 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 = info_universal_argument; 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; map[DEL].function = ea_rubout; /* Bind the echo area ESC keymap. */ map = (Keymap)echo_area_keymap[ESC].function; map[Control ('g')].function = ea_abort; map[Control ('v')].function = ea_scroll_completions_window; map['b'].function = ea_backward_word; map['d'].function = ea_kill_word; map['f'].function = ea_forward_word; #if defined (NAMED_FUNCTIONS) /* map['x'].function = info_execute_command; */ #endif /* NAMED_FUNCTIONS */ map['y'].function = ea_yank_pop; map['?'].function = ea_possible_completions; map[TAB].function = ea_tab_insert; map[DEL].function = ea_backward_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_select_reference_this_line; map[RET].function = info_select_reference_this_line; map[SPC].function = info_scroll_forward; map[Control ('a')].function = info_beginning_of_line; map[Control ('b')].function = info_backward_char; map[Control ('e')].function = info_end_of_line; map[Control ('f')].function = info_forward_char; map[Control ('g')].function = info_abort_key; map[Control ('h')].function = info_get_help_window; map[Control ('l')].function = info_redraw_display; map[Control ('n')].function = info_next_line; map[Control ('p')].function = info_prev_line; map[Control ('r')].function = isearch_backward; map[Control ('s')].function = isearch_forward; map[Control ('u')].function = info_universal_argument; map[Control ('v')].function = info_scroll_forward; map[','].function = info_next_index_match; for (i = '1'; i < '9' + 1; i++) map[i].function = info_menu_digit; map['0'].function = info_last_menu_item; map['<'].function = info_first_node; map['>'].function = info_last_node; map['?'].function = info_get_help_window; map['['].function = info_global_prev_node; map[']'].function = info_global_next_node; 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_goto_node; map['h'].function = info_get_info_help_node; map['i'].function = info_index_search; map['l'].function = info_history_node; map['m'].function = info_menu_item; map['n'].function = info_next_node; map['p'].function = info_prev_node; map['q'].function = info_quit; map['r'].function = info_xref_item; map['s'].function = info_search; map['t'].function = info_top_node; map['u'].function = info_up_node; map[DEL].function = info_scroll_backward; /* 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[Control ('v')].function = info_scroll_other_window; map['<'].function = info_beginning_of_node; map['>'].function = info_end_of_node; map['b'].function = info_backward_word; map['f'].function = info_forward_word; map['r'].function = info_move_to_window_line; map['v'].function = info_scroll_backward; #if defined (NAMED_FUNCTIONS) map['x'].function = info_execute_command; #endif /* NAMED_FUNCTIONS */ /* 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['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['k'].function = info_kill_node; map['o'].function = info_next_window; map['t'].function = info_tile_windows; map['w'].function = info_toggle_wrap; - /* Add functions for the arrow keys, PageUp, PageDown, Home, HomeDown */ - add_function_key(term_ku, info_prev_line, info_keymap); - add_function_key(term_kd, info_next_line, info_keymap); - add_function_key(term_kl, info_backward_char, info_keymap); - add_function_key(term_kr, info_forward_char, info_keymap); - add_function_key(term_kP, info_scroll_backward, info_keymap); - add_function_key(term_kN, info_scroll_forward, info_keymap); - add_function_key(term_kh, info_beginning_of_node, info_keymap); - add_function_key(term_kH, info_end_of_node, info_keymap); -} + /* 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')]); -static void add_function_key(char *esc_seq, VFunction *func, Keymap map) -{ - char *end_str, *p; - - if (!esc_seq) - return; /* don't add keys which don't exist */ - - end_str = esc_seq + strlen(esc_seq); - - for (p = esc_seq; p < end_str; p++) - { - if (isupper(*p)) - *p = tolower(*p); - switch (map[*p].type) - { - case ISKMAP: /* Go one level down. Also has the effect - that we're not overwriting a previous - binding if we're at the end of p */ - map = (Keymap)map[*p].function; - break; - case ISFUNC: /* two possibilities here: - 1. map[*p].function == NULL means we have - a virgin keymap to fill; - 2. else this entry is already taken */ - if (map[*p].function == NULL) - { - if (p == end_str - 1) - { - map[*p].function = func; - return; - } - map[*p].type = ISKMAP; - map[*p].function = (VFunction *)keymap_make_keymap(); - map = (Keymap)map[*p].function; - } else - return; - break; - default: /* can't happen */ - info_error("unknown keymap type (%d).", map[*p].type); - break; - } - } - return; + 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 */ + + /* 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 */ } diff --git a/contrib/texinfo/info/nodemenu.c b/contrib/texinfo/info/nodemenu.c index 7704170e7d2c..263265747427 100644 --- a/contrib/texinfo/info/nodemenu.c +++ b/contrib/texinfo/info/nodemenu.c @@ -1,329 +1,339 @@ -/* nodemenu.c -- Produce a menu of all visited nodes. */ +/* nodemenu.c -- Produce a menu of all visited nodes. + $Id: nodemenu.c,v 1.7 1997/07/24 21:30:30 karl Exp $ -/* This file is part of GNU Info, a program for reading online documentation - stored in Info format. - - Copyright (C) 1993 Free Software Foundation, Inc. + Copyright (C) 1993, 97 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Written by Brian Fox (bfox@ai.mit.edu). */ #include "info.h" /* Return a line describing the format of a node information line. */ static char * nodemenu_format_info () { - return ("\n\ + return (_("\n\ * Menu:\n\ (File)Node Lines Size Containing File\n\ - ---------- ----- ---- ---------------"); + ---------- ----- ---- ---------------")); } /* Produce a formatted line of information about NODE. Here is what we want the output listing to look like: * Menu: (File)Node Lines Size Containing File ---------- ----- ---- --------------- * (emacs)Buffers:: 48 2230 /usr/gnu/info/emacs/emacs-1 * (autoconf)Writing configure.in:: 123 58789 /usr/gnu/info/autoconf/autoconf-1 -* (dir)Top:: 40 589 /usr/gnu/info/dir +* (dir)Top:: 40 589 /usr/gnu/info/dir */ static char * format_node_info (node) NODE *node; { register int i, len; char *parent, *containing_file; static char *line_buffer = (char *)NULL; if (!line_buffer) line_buffer = (char *)xmalloc (1000); if (node->parent) { parent = filename_non_directory (node->parent); if (!parent) - parent = node->parent; + parent = node->parent; } else parent = (char *)NULL; containing_file = node->filename; if (!parent && !*containing_file) sprintf (line_buffer, "* %s::", node->nodename); else { char *file = (char *)NULL; if (parent) - file = parent; + file = parent; else - file = filename_non_directory (containing_file); + file = filename_non_directory (containing_file); if (!file) - file = containing_file; + file = containing_file; if (!*file) - file = "dir"; + file = "dir"; sprintf (line_buffer, "* (%s)%s::", file, node->nodename); } len = pad_to (36, line_buffer); { int lines = 1; for (i = 0; i < node->nodelen; i++) if (node->contents[i] == '\n') - lines++; + lines++; sprintf (line_buffer + len, "%d", lines); } len = pad_to (44, line_buffer); sprintf (line_buffer + len, "%ld", node->nodelen); if (node->filename && *(node->filename)) { len = pad_to (51, line_buffer); sprintf (line_buffer + len, node->filename); } - return (strdup (line_buffer)); + return xstrdup (line_buffer); } /* Little string comparison routine for qsort (). */ static int compare_strings (string1, string2) char **string1, **string2; { return (strcasecmp (*string1, *string2)); } /* The name of the nodemenu node. */ static char *nodemenu_nodename = "*Node Menu*"; /* Produce an informative listing of all the visited nodes, and return it in a node. If FILTER_FUNC is non-null, it is a function which filters which nodes will appear in the listing. FILTER_FUNC takes an argument of NODE, and returns non-zero if the node should appear in the listing. */ NODE * get_visited_nodes (filter_func) Function *filter_func; { register int i, iw_index; INFO_WINDOW *info_win; NODE *node; char **lines = (char **)NULL; int lines_index = 0, lines_slots = 0; if (!info_windows) return ((NODE *)NULL); - for (iw_index = 0; info_win = info_windows[iw_index]; iw_index++) + for (iw_index = 0; (info_win = info_windows[iw_index]); iw_index++) { for (i = 0; i < info_win->nodes_index; i++) - { - node = info_win->nodes[i]; - - /* We skip mentioning "*Node Menu*" nodes. */ - if (internal_info_node_p (node) && - (strcmp (node->nodename, nodemenu_nodename) == 0)) - continue; - - if (node && (!filter_func || (*filter_func) (node))) - { - char *line; - - line = format_node_info (node); - add_pointer_to_array - (line, lines_index, lines, lines_slots, 20, char *); - } - } + { + node = info_win->nodes[i]; + + /* We skip mentioning "*Node Menu*" nodes. */ + if (internal_info_node_p (node) && + (strcmp (node->nodename, nodemenu_nodename) == 0)) + continue; + + if (node && (!filter_func || (*filter_func) (node))) + { + char *line; + + line = format_node_info (node); + add_pointer_to_array + (line, lines_index, lines, lines_slots, 20, char *); + } + } } /* Sort the array of information lines, if there are any. */ if (lines) { register int j, newlen; char **temp; qsort (lines, lines_index, sizeof (char *), compare_strings); /* Delete duplicates. */ for (i = 0, newlen = 1; i < lines_index - 1; i++) - { - if (strcmp (lines[i], lines[i + 1]) == 0) - { - free (lines[i]); - lines[i] = (char *)NULL; - } - else - newlen++; - } + { + if (strcmp (lines[i], lines[i + 1]) == 0) + { + free (lines[i]); + lines[i] = (char *)NULL; + } + else + newlen++; + } /* We have free ()'d and marked all of the duplicate slots. - Copy the live slots rather than pruning the dead slots. */ + Copy the live slots rather than pruning the dead slots. */ temp = (char **)xmalloc ((1 + newlen) * sizeof (char *)); for (i = 0, j = 0; i < lines_index; i++) - if (lines[i]) - temp[j++] = lines[i]; + if (lines[i]) + temp[j++] = lines[i]; temp[j] = (char *)NULL; free (lines); lines = temp; lines_index = newlen; } initialize_message_buffer (); printf_to_message_buffer ("%s", replace_in_documentation - ("Here is the menu of nodes you have recently visited.\n\ -Select one from this menu, or use `\\[history-node]' in another window.\n")); + (_("Here is the menu of nodes you have recently visited.\n\ +Select one from this menu, or use `\\[history-node]' in another window.\n"))); printf_to_message_buffer ("%s\n", nodemenu_format_info ()); for (i = 0; (lines != (char **)NULL) && (i < lines_index); i++) { printf_to_message_buffer ("%s\n", lines[i]); free (lines[i]); } if (lines) free (lines); node = message_buffer_to_node (); add_gcable_pointer (node->contents); return (node); } DECLARE_INFO_COMMAND (list_visited_nodes, - "Make a window containing a menu of all of the currently visited nodes") + _("Make a window containing a menu of all of the currently visited nodes")) { WINDOW *new; NODE *node; set_remembered_pagetop_and_point (window); /* If a window is visible and showing the buffer list already, re-use it. */ for (new = windows; new; new = new->next) { node = new->node; if (internal_info_node_p (node) && - (strcmp (node->nodename, nodemenu_nodename) == 0)) - break; + (strcmp (node->nodename, nodemenu_nodename) == 0)) + break; } /* If we couldn't find an existing window, try to use the next window in the chain. */ - if (!new && window->next) - new = window->next; + if (!new) + { + if (window->next) + new = window->next; + /* If there is more than one window, wrap around. */ + else if (window != windows) + new = windows; + } /* If we still don't have a window, make a new one to contain the list. */ if (!new) { WINDOW *old_active; old_active = active_window; active_window = window; new = window_make_window ((NODE *)NULL); active_window = old_active; } /* If we couldn't make a new window, use this one. */ if (!new) new = window; /* Lines do not wrap in this window. */ new->flags |= W_NoWrap; node = get_visited_nodes ((Function *)NULL); name_internal_node (node, nodemenu_nodename); +#if 0 /* Even if this is an internal node, we don't want the window system to treat it specially. So we turn off the internalness of it here. */ + /* Why? We depend on internal_info_node_p returning true, so we must + not remove the flag. Otherwise, the *Node Menu* nodes themselves + appear in the node menu. --Andreas Schwab + . */ node->flags &= ~N_IsInternal; +#endif /* If this window is already showing a node menu, reuse the existing node slot. */ { int remember_me = 1; #if defined (NOTDEF) if (internal_info_node_p (new->node) && - (strcmp (new->node->nodename, nodemenu_nodename) == 0)) + (strcmp (new->node->nodename, nodemenu_nodename) == 0)) remember_me = 0; #endif /* NOTDEF */ window_set_node_of_window (new, node); if (remember_me) remember_window_and_node (new, node); } active_window = new; } DECLARE_INFO_COMMAND (select_visited_node, - "Select a node which has been previously visited in a visible window") + _("Select a node which has been previously visited in a visible window")) { char *line; NODE *node; REFERENCE **menu; node = get_visited_nodes ((Function *)NULL); menu = info_menu_of_node (node); free (node); line = - info_read_completing_in_echo_area (window, "Select visited node: ", menu); + info_read_completing_in_echo_area (window, _("Select visited node: "), menu); window = active_window; /* User aborts, just quit. */ if (!line) { info_abort_key (window, 0, 0); info_free_references (menu); return; } if (*line) { REFERENCE *entry; /* Find the selected label in the references. */ entry = info_get_labeled_reference (line, menu); if (!entry) - info_error ("The reference disappeared! (%s).", line); + info_error (_("The reference disappeared! (%s)."), line); else - info_select_reference (window, entry); + info_select_reference (window, entry); } free (line); info_free_references (menu); if (!info_error_was_printed) window_clear_echo_area (); } diff --git a/contrib/texinfo/info/session.c b/contrib/texinfo/info/session.c index 494c4a1e5730..63a6ecdd35f3 100644 --- a/contrib/texinfo/info/session.c +++ b/contrib/texinfo/info/session.c @@ -1,4264 +1,4252 @@ -/* session.c -- The user windowing interface to Info. */ +/* session.c -- The user windowing interface to Info. + $Id: session.c,v 1.13 1998/02/22 22:38:30 karl Exp $ -/* This file is part of GNU Info, a program for reading online documentation - stored in Info format. - - Copyright (C) 1993, 96 Free Software Foundation, Inc. + Copyright (C) 1993, 96, 97 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Written by Brian Fox (bfox@ai.mit.edu). */ #include "info.h" -#if defined (HAVE_SYS_FILE_H) -#include -#endif /* HAVE_SYS_FILE_H */ #include -#include -#include #if defined (HAVE_SYS_TIME_H) # include # define HAVE_STRUCT_TIMEVAL #endif /* HAVE_SYS_TIME_H */ #if defined (HANDLE_MAN_PAGES) # include "man.h" #endif static void info_clear_pending_input (), info_set_pending_input (); static void info_handle_pointer (); /* **************************************************************** */ -/* */ -/* Running an Info Session */ -/* */ +/* */ +/* Running an Info Session */ +/* */ /* **************************************************************** */ /* The place that we are reading input from. */ -static FILE *info_input_stream = (FILE *)NULL; +static FILE *info_input_stream = NULL; /* The last executed command. */ -VFunction *info_last_executed_command = (VFunction *)NULL; +VFunction *info_last_executed_command = NULL; /* Becomes non-zero when 'q' is typed to an Info window. */ int quit_info_immediately = 0; /* Array of structures describing for each window which nodes have been visited in that window. */ -INFO_WINDOW **info_windows = (INFO_WINDOW **)NULL; +INFO_WINDOW **info_windows = NULL; /* Where to add the next window, if we need to add one. */ static int info_windows_index = 0; -/* Number of slots allocated to INFO_WINDOWS. */ +/* Number of slots allocated to `info_windows'. */ static int info_windows_slots = 0; void remember_window_and_node (), forget_window_and_nodes (); void initialize_info_session (), info_session (); void display_startup_message_and_start (); /* Begin an info session finding the nodes specified by FILENAME and NODENAMES. For each loaded node, create a new window. Always split the largest of the available windows. */ void begin_multiple_window_info_session (filename, nodenames) char *filename; char **nodenames; { register int i; WINDOW *window = (WINDOW *)NULL; for (i = 0; nodenames[i]; i++) { NODE *node; node = info_get_node (filename, nodenames[i]); if (!node) - break; + break; /* If this is the first node, initialize the info session. */ if (!window) - { - initialize_info_session (node); - window = active_window; - } + { + initialize_info_session (node, 1); + window = active_window; + } else - { - /* 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; - int max_height = 0; - - for (win = windows; win; win = win->next) - if (win->height > max_height) - { - max_height = win->height; - largest = win; - } - - if (!largest) - { - display_update_display (windows); - info_error (CANT_FIND_WIND); - info_session (); - exit (0); - } - - active_window = largest; - window = window_make_window (node); - if (window) - { - window_tile_windows (TILE_INTERNALS); - remember_window_and_node (window, node); - } - else - { - display_update_display (windows); - info_error (WIN_TOO_SMALL); - info_session (); - exit (0); - } - } + { + /* 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; + int max_height = 0; + + for (win = windows; win; win = win->next) + if (win->height > max_height) + { + max_height = win->height; + largest = win; + } + + if (!largest) + { + display_update_display (windows); + info_error (CANT_FIND_WIND); + info_session (); + exit (0); + } + + active_window = largest; + window = window_make_window (node); + if (window) + { + window_tile_windows (TILE_INTERNALS); + remember_window_and_node (window, node); + } + else + { + display_update_display (windows); + info_error (WIN_TOO_SMALL); + info_session (); + exit (0); + } + } } display_startup_message_and_start (); } /* 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) NODE *initial_node; char *format; void *arg; { - initialize_info_session (initial_node); + initialize_info_session (initial_node, 1); info_error (format, arg, (void *)NULL); info_session (); } /* Start an info session with INITIAL_NODE. */ void begin_info_session (initial_node) NODE *initial_node; { - initialize_info_session (initial_node); + initialize_info_session (initial_node, 1); display_startup_message_and_start (); } void 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. \"\\[get-help-window]\" for help, \"\\[menu-item]\" for menu item.")); window_message_in_echo_area (format, version_string ()); info_session (); } /* Run an info session with an already initialized window and node. */ void info_session () { - terminal_prep_terminal (); display_update_display (windows); - info_last_executed_command = (VFunction *)NULL; + info_last_executed_command = NULL; info_read_and_dispatch (); /* On program exit, leave the cursor at the bottom of the window, and restore the terminal I/O. */ terminal_goto_xy (0, screenheight - 1); terminal_clear_to_eol (); fflush (stdout); terminal_unprep_terminal (); close_dribble_file (); } /* Here is a window-location dependent event loop. Called from the functions info_session (), and from read_xxx_in_echo_area (). */ void info_read_and_dispatch () { unsigned char key; int done; done = 0; while (!done && !quit_info_immediately) { int lk; /* If we haven't just gone up or down a line, there is no - goal column for this window. */ + goal column for this window. */ if ((info_last_executed_command != info_next_line) && - (info_last_executed_command != info_prev_line)) - active_window->goal_column = -1; + (info_last_executed_command != info_prev_line)) + active_window->goal_column = -1; if (echo_area_is_active) - { - lk = echo_area_last_command_was_kill; - echo_area_prep_read (); - } + { + lk = echo_area_last_command_was_kill; + echo_area_prep_read (); + } if (!info_any_buffered_input_p ()) - display_update_display (windows); + display_update_display (windows); display_cursor_at_point (active_window); info_initialize_numeric_arg (); initialize_keyseq (); key = info_get_input_char (); /* No errors yet. We just read a character, that's all. Only clear - the echo_area if it is not currently active. */ + the echo_area if it is not currently active. */ if (!echo_area_is_active) - window_clear_echo_area (); + window_clear_echo_area (); info_error_was_printed = 0; /* Do the selected command. */ info_dispatch_on_key (key, active_window->keymap); if (echo_area_is_active) - { - /* Echo area commands that do killing increment the value of - ECHO_AREA_LAST_COMMAND_WAS_KILL. Thus, if there is no - change in the value of this variable, the last command - executed was not a kill command. */ - if (lk == echo_area_last_command_was_kill) - echo_area_last_command_was_kill = 0; - - if (ea_last_executed_command == ea_newline || - info_aborted_echo_area) - { - ea_last_executed_command = (VFunction *)NULL; - done = 1; - } - - if (info_last_executed_command == info_quit) - quit_info_immediately = 1; - } + { + /* Echo area commands that do killing increment the value of + ECHO_AREA_LAST_COMMAND_WAS_KILL. Thus, if there is no + change in the value of this variable, the last command + executed was not a kill command. */ + if (lk == echo_area_last_command_was_kill) + echo_area_last_command_was_kill = 0; + + if (ea_last_executed_command == ea_newline || + info_aborted_echo_area) + { + ea_last_executed_command = (VFunction *)NULL; + done = 1; + } + + if (info_last_executed_command == info_quit) + quit_info_immediately = 1; + } else if (info_last_executed_command == info_quit) - done = 1; + done = 1; } } /* Found in signals.c */ extern void initialize_info_signal_handler (); /* Initialize the first info session by starting the terminal, window, - and display systems. */ + and display systems. If CLEAR_SCREEN is 0, don't clear the screen. */ void -initialize_info_session (node) +initialize_info_session (node, clear_screen) NODE *node; + int clear_screen; { - char *getenv (), *term_name; - - term_name = getenv ("TERM"); + char *term_name = getenv ("TERM"); terminal_initialize_terminal (term_name); if (terminal_is_dumb_p) { if (!term_name) - term_name = "dumb"; + term_name = "dumb"; info_error (TERM_TOO_DUMB, term_name); exit (1); } - terminal_clear_screen (); + if (clear_screen) + { + terminal_prep_terminal (); + terminal_clear_screen (); + } + initialize_info_keymaps (); window_initialize_windows (screenwidth, screenheight); initialize_info_signal_handler (); display_initialize_display (screenwidth, screenheight); info_set_node_of_window (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). */ window_deletion_notifier = forget_window_and_nodes; - /* If input has not been redirected yet, make it come from STDIN. */ + /* If input has not been redirected yet, make it come from unbuffered + standard input. */ if (!info_input_stream) - info_input_stream = stdin; + { + setbuf(stdin, NULL); + info_input_stream = stdin; + } info_windows_initialized_p = 1; } /* Tell Info that input is coming from the file FILENAME. */ void info_set_input_from_file (filename) char *filename; { FILE *stream; stream = fopen (filename, "r"); if (!stream) return; if ((info_input_stream != (FILE *)NULL) && (info_input_stream != stdin)) fclose (info_input_stream); info_input_stream = stream; if (stream != stdin) display_inhibited = 1; } /* Return the INFO_WINDOW containing WINDOW, or NULL if there isn't one. */ static INFO_WINDOW * get_info_window_of_window (window) WINDOW *window; { register int i; INFO_WINDOW *info_win = (INFO_WINDOW *)NULL; for (i = 0; info_windows && (info_win = info_windows[i]); i++) if (info_win->window == window) break; return (info_win); } /* Reset the remembered pagetop and point of WINDOW to WINDOW's current values if the window and node are the same as the current one being displayed. */ void set_remembered_pagetop_and_point (window) WINDOW *window; { INFO_WINDOW *info_win; info_win = get_info_window_of_window (window); if (!info_win) return; if (info_win->nodes_index && (info_win->nodes[info_win->current] == window->node)) { info_win->pagetops[info_win->current] = window->pagetop; info_win->points[info_win->current] = window->point; } } void remember_window_and_node (window, node) WINDOW *window; NODE *node; { - INFO_WINDOW *info_win; - /* See if we already have this window in our list. */ - info_win = get_info_window_of_window (window); + INFO_WINDOW *info_win = get_info_window_of_window (window); /* If the window wasn't already on our list, then make a new entry. */ if (!info_win) { info_win = (INFO_WINDOW *)xmalloc (sizeof (INFO_WINDOW)); info_win->window = window; info_win->nodes = (NODE **)NULL; info_win->pagetops = (int *)NULL; info_win->points = (long *)NULL; info_win->current = 0; info_win->nodes_index = 0; info_win->nodes_slots = 0; add_pointer_to_array (info_win, info_windows_index, info_windows, - info_windows_slots, 10, INFO_WINDOW *); + info_windows_slots, 10, INFO_WINDOW *); } /* If this node, the current pagetop, and the current point are the - same as the last saved node and pagetop, don't really add this to - the list of history nodes. */ - { - int ni = info_win->nodes_index - 1; - - if ((ni != -1) && - (info_win->nodes[ni]->contents == node->contents) && - (info_win->pagetops[ni] == window->pagetop) && - (info_win->points[ni] == window->point)) - return; - } + same as the current saved node and pagetop, don't really add this to + the list of history nodes. This may happen only at the very + beginning of the program, I'm not sure. --karl */ + if (info_win->nodes + && info_win->current >= 0 + && info_win->nodes[info_win->current]->contents == node->contents + && info_win->pagetops[info_win->current] == window->pagetop + && info_win->points[info_win->current] == window->point) + return; /* Remember this node, the currently displayed pagetop, and the current location of point in this window. Because we are updating pagetops and points as well as nodes, it is more efficient to avoid the add_pointer_to_array macro here. */ if (info_win->nodes_index + 2 >= info_win->nodes_slots) { - info_win->nodes = (NODE **) - xrealloc (info_win->nodes, - (info_win->nodes_slots += 20) * sizeof (NODE *)); - - info_win->pagetops = (int *) - xrealloc (info_win->pagetops, info_win->nodes_slots * sizeof (int)); - - info_win->points = (long *) - xrealloc (info_win->points, info_win->nodes_slots * sizeof (long)); + info_win->nodes_slots += 20; + info_win->nodes = (NODE **) xrealloc (info_win->nodes, + info_win->nodes_slots * sizeof (NODE *)); + info_win->pagetops = (int *) xrealloc (info_win->pagetops, + info_win->nodes_slots * sizeof (int)); + info_win->points = (long *) xrealloc (info_win->points, + info_win->nodes_slots * sizeof (long)); } info_win->nodes[info_win->nodes_index] = node; info_win->pagetops[info_win->nodes_index] = window->pagetop; info_win->points[info_win->nodes_index] = window->point; info_win->current = info_win->nodes_index++; - info_win->nodes[info_win->nodes_index] = (NODE *)NULL; + info_win->nodes[info_win->nodes_index] = NULL; info_win->pagetops[info_win->nodes_index] = 0; info_win->points[info_win->nodes_index] = 0; } #define DEBUG_FORGET_WINDOW_AND_NODES #if defined (DEBUG_FORGET_WINDOW_AND_NODES) static void consistency_check_info_windows () { register int i; - INFO_WINDOW *info_win; for (i = 0; i < info_windows_index; i++) { WINDOW *win; for (win = windows; win; win = win->next) - if (win == info_windows[i]->window) - break; + if (win == info_windows[i]->window) + break; if (!win) - abort (); + abort (); } } #endif /* DEBUG_FORGET_WINDOW_AND_NODES */ /* Remove WINDOW and its associated list of nodes from INFO_WINDOWS. */ void forget_window_and_nodes (window) WINDOW *window; { register int i; INFO_WINDOW *info_win = (INFO_WINDOW *)NULL; for (i = 0; info_windows && (info_win = info_windows[i]); i++) if (info_win->window == window) break; /* If we found the window to forget, then do so. */ if (info_win) { while (i < info_windows_index) - { - info_windows[i] = info_windows[i + 1]; - i++; - } + { + info_windows[i] = info_windows[i + 1]; + i++; + } info_windows_index--; info_windows[info_windows_index] = (INFO_WINDOW *)NULL; if (info_win->nodes) - { - /* Free the node structures which held onto internal node contents - here. This doesn't free the contents; we have a garbage collector - which does that. */ - for (i = 0; info_win->nodes[i]; i++) - if (internal_info_node_p (info_win->nodes[i])) - free (info_win->nodes[i]); - free (info_win->nodes); - - maybe_free (info_win->pagetops); - maybe_free (info_win->points); - } + { + /* Free the node structures which held onto internal node contents + here. This doesn't free the contents; we have a garbage collector + which does that. */ + for (i = 0; info_win->nodes[i]; i++) + if (internal_info_node_p (info_win->nodes[i])) + free (info_win->nodes[i]); + free (info_win->nodes); + + maybe_free (info_win->pagetops); + maybe_free (info_win->points); + } free (info_win); } #if defined (DEBUG_FORGET_WINDOW_AND_NODES) consistency_check_info_windows (); #endif /* DEBUG_FORGET_WINDOW_AND_NODES */ } /* 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. */ void info_set_node_of_window (window, node) WINDOW *window; NODE *node; { /* Put this node into the window. */ window_set_node_of_window (window, node); /* Remember this node and window in our list of info windows. */ remember_window_and_node (window, node); /* If doing auto-footnote display/undisplay, show the footnotes belonging to this window's node. */ if (auto_footnotes_p) info_get_or_remove_footnotes (window); } /* **************************************************************** */ -/* */ -/* Info Movement Commands */ -/* */ +/* */ +/* Info Movement Commands */ +/* */ /* **************************************************************** */ /* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen to do so. */ void set_window_pagetop (window, desired_top) WINDOW *window; int desired_top; { int point_line, old_pagetop; if (desired_top < 0) desired_top = 0; else if (desired_top > window->line_count) desired_top = window->line_count - 1; if (window->pagetop == desired_top) return; old_pagetop = window->pagetop; window->pagetop = desired_top; /* Make sure that point appears in this window. */ point_line = window_line_of_point (window); if ((point_line < window->pagetop) || ((point_line - window->pagetop) > window->height - 1)) window->point = window->line_starts[window->pagetop] - window->node->contents; window->flags |= W_UpdateWindow; /* Find out which direction to scroll, and scroll the window in that direction. Do this only if there would be a savings in redisplay time. This is true if the amount to scroll is less than the height of the window, and if the number of lines scrolled would be greater than 10 % of the window's height. */ if (old_pagetop < desired_top) { int start, end, amount; amount = desired_top - old_pagetop; if ((amount >= window->height) || - (((window->height - amount) * 10) < window->height)) - return; + (((window->height - amount) * 10) < window->height)) + return; start = amount + window->first_row; end = window->height + window->first_row; display_scroll_display (start, end, -amount); } else { int start, end, amount; amount = old_pagetop - desired_top; if ((amount >= window->height) || - (((window->height - amount) * 10) < window->height)) - return; + (((window->height - amount) * 10) < window->height)) + return; start = window->first_row; end = (window->first_row + window->height) - amount; display_scroll_display (start, end, amount); } } /* Immediately make WINDOW->point visible on the screen, and move the terminal cursor there. */ static void info_show_point (window) WINDOW *window; { int old_pagetop; old_pagetop = window->pagetop; window_adjust_pagetop (window); if (old_pagetop != window->pagetop) { int new_pagetop; new_pagetop = window->pagetop; window->pagetop = old_pagetop; set_window_pagetop (window, new_pagetop); } if (window->flags & W_UpdateWindow) display_update_one_window (window); display_cursor_at_point (window); } /* Move WINDOW->point from OLD line index to NEW line index. */ static void move_to_new_line (old, new, window) int old, new; WINDOW *window; { if (old == -1) { info_error (CANT_FIND_POINT); } else { int goal; if (new >= window->line_count || new < 0) - return; + return; goal = window_get_goal_column (window); window->goal_column = goal; window->point = window->line_starts[new] - window->node->contents; window->point += window_chars_to_goal (window->line_starts[new], goal); info_show_point (window); } } /* Move WINDOW's point down to the next line if possible. */ -DECLARE_INFO_COMMAND (info_next_line, "Move down to the next line") +DECLARE_INFO_COMMAND (info_next_line, _("Move down to the next line")) { int old_line, new_line; if (count < 0) info_prev_line (window, -count, key); else { old_line = window_line_of_point (window); new_line = old_line + count; move_to_new_line (old_line, new_line, window); } } /* Move WINDOW's point up to the previous line if possible. */ -DECLARE_INFO_COMMAND (info_prev_line, "Move up to the previous line") +DECLARE_INFO_COMMAND (info_prev_line, _("Move up to the previous line")) { int old_line, new_line; if (count < 0) info_next_line (window, -count, key); else { old_line = window_line_of_point (window); new_line = old_line - count; move_to_new_line (old_line, new_line, window); } } /* Move WINDOW's point to the end of the true line. */ -DECLARE_INFO_COMMAND (info_end_of_line, "Move to the end of the line") +DECLARE_INFO_COMMAND (info_end_of_line, _("Move to the end of the line")) { register int point, len; register char *buffer; buffer = window->node->contents; len = window->node->nodelen; for (point = window->point; (point < len) && (buffer[point] != '\n'); point++); if (point != window->point) { window->point = point; info_show_point (window); } } /* Move WINDOW's point to the beginning of the true line. */ -DECLARE_INFO_COMMAND (info_beginning_of_line, "Move to the start of the line") +DECLARE_INFO_COMMAND (info_beginning_of_line, _("Move to the start of the line")) { register int point; register char *buffer; buffer = window->node->contents; point = window->point; for (; (point) && (buffer[point - 1] != '\n'); point--); /* If at a line start alreay, do nothing. */ if (point != window->point) { window->point = point; info_show_point (window); } } /* Move point forward in the node. */ -DECLARE_INFO_COMMAND (info_forward_char, "Move forward a character") +DECLARE_INFO_COMMAND (info_forward_char, _("Move forward a character")) { if (count < 0) info_backward_char (window, -count, key); else { window->point += count; if (window->point >= window->node->nodelen) - window->point = window->node->nodelen - 1; + window->point = window->node->nodelen - 1; info_show_point (window); } } /* Move point backward in the node. */ -DECLARE_INFO_COMMAND (info_backward_char, "Move backward a character") +DECLARE_INFO_COMMAND (info_backward_char, _("Move backward a character")) { if (count < 0) info_forward_char (window, -count, key); else { window->point -= count; if (window->point < 0) - window->point = 0; + window->point = 0; info_show_point (window); } } #define alphabetic(c) (islower (c) || isupper (c) || isdigit (c)) /* Move forward a word in this node. */ -DECLARE_INFO_COMMAND (info_forward_word, "Move forward a word") +DECLARE_INFO_COMMAND (info_forward_word, _("Move forward a word")) { long point; char *buffer; int end, c; if (count < 0) { info_backward_word (window, -count, key); return; } point = window->point; buffer = window->node->contents; end = window->node->nodelen; while (count) { if (point + 1 >= end) - return; + return; /* If we are not in a word, move forward until we are in one. - Then, move forward until we hit a non-alphabetic character. */ + Then, move forward until we hit a non-alphabetic character. */ c = buffer[point]; if (!alphabetic (c)) - { - while (++point < end) - { - c = buffer[point]; - if (alphabetic (c)) - break; - } - } + { + while (++point < end) + { + c = buffer[point]; + if (alphabetic (c)) + break; + } + } if (point >= end) return; while (++point < end) - { - c = buffer[point]; - if (!alphabetic (c)) - break; - } + { + c = buffer[point]; + if (!alphabetic (c)) + break; + } --count; } window->point = point; info_show_point (window); } -DECLARE_INFO_COMMAND (info_backward_word, "Move backward a word") +DECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word")) { long point; char *buffer; int c; if (count < 0) { info_forward_word (window, -count, key); return; } buffer = window->node->contents; point = window->point; while (count) { if (point == 0) - break; + break; /* Like info_forward_word (), except that we look at the - characters just before point. */ + characters just before point. */ c = buffer[point - 1]; if (!alphabetic (c)) - { - while (--point) - { - c = buffer[point - 1]; - if (alphabetic (c)) - break; - } - } + { + while (--point) + { + c = buffer[point - 1]; + if (alphabetic (c)) + break; + } + } while (point) - { - c = buffer[point - 1]; - if (!alphabetic (c)) - break; - else - --point; - } + { + c = buffer[point - 1]; + if (!alphabetic (c)) + break; + else + --point; + } --count; } window->point = point; 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" : ""); + sprintf (td_buffer, "%s%s", counter_names[i], count > 2 ? _(" times") : ""); else - sprintf (td_buffer, "%d times", count); + 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: - IS_Continuous Try to get first menu item, or failing that, the - "Next:" pointer, or failing that, the "Up:" and - "Next:" of the up. - IS_NextOnly Try to get "Next:" menu item. - IS_PageOnly Simply give up at the bottom of a node. */ + IS_Continuous Try to get first menu item, or failing that, the + "Next:" pointer, or failing that, the "Up:" and + "Next:" of the up. + IS_NextOnly Try to get "Next:" menu item. + IS_PageOnly Simply give up at the bottom of a node. */ int info_scroll_behaviour = IS_Continuous; /* Choices used by the completer when reading a value for the user-visible variable "scroll-behaviour". */ char *info_scroll_choices[] = { "Continuous", "Next Only", "Page Only", (char *)NULL }; /* Move to 1st menu item, Next, Up/Next, or error in this window. */ static void forward_move_node_structure (window, behaviour) WINDOW *window; int behaviour; { switch (behaviour) { case IS_PageOnly: info_error (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 (_("No \"Next\" pointer for this node.")); 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; case IS_Continuous: { - /* First things first. If this node contains a menu, move down - into the menu. */ - { - REFERENCE **menu; - - menu = info_menu_of_node (window->node); - - if (menu) - { - info_free_references (menu); - window_message_in_echo_area ("Selecting first menu item..."); - info_menu_digit (window, 1, '1'); - return; - } - } - - /* 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) - { - window_message_in_echo_area ("Selecting \"Next\" node..."); - info_handle_pointer ("Next", window); - return; - } - - /* Okay, there wasn't a "Next:" for this node. Move "Up:" until we - can move "Next:". If that isn't possible, complain that there - are no more nodes. */ - { - int up_counter, old_current; - INFO_WINDOW *info_win; - - /* Remember the current node and location. */ - info_win = get_info_window_of_window (window); - old_current = info_win->current; - - /* Back up through the "Up:" pointers until we have found a "Next:" - that isn't the same as the first menu item found in that node. */ - up_counter = 0; - while (!info_error_was_printed) - { - info_up_label_of_node (window->node); - if (info_label_was_found) - { - info_handle_pointer ("Up", window); - if (info_error_was_printed) - continue; - - up_counter++; - - info_next_label_of_node (window->node); - - /* If no "Next" pointer, keep backing up. */ - if (!info_label_was_found) - continue; - - /* If this node's first menu item is the same as this node's - Next pointer, keep backing up. */ - if (!info_parsed_filename) - { - REFERENCE **menu; - char *next_nodename; - - /* Remember the name of the Next node, since reading - the menu can overwrite the contents of the - info_parsed_xxx strings. */ - next_nodename = strdup (info_parsed_nodename); - - menu = info_menu_of_node (window->node); - if (menu && - (strcmp - (menu[0]->nodename, next_nodename) == 0)) - { - info_free_references (menu); - free (next_nodename); - continue; - } - else - { - /* Restore the world to where it was before - reading the menu contents. */ - info_free_references (menu); - free (next_nodename); - info_next_label_of_node (window->node); - } - } - - /* 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)); - - info_handle_pointer ("Next", window); - return; - } - else - { - /* No more "Up" pointers. Print an error, and call it - quits. */ - register int i; - - for (i = 0; i < up_counter; i++) - { - info_win->nodes_index--; - free (info_win->nodes[info_win->nodes_index]); - info_win->nodes[info_win->nodes_index] = (NODE *)NULL; - } - info_win->current = old_current; - window->node = info_win->nodes[old_current]; - window->pagetop = info_win->pagetops[old_current]; - window->point = info_win->points[old_current]; - recalculate_line_starts (window); - window->flags |= W_UpdateWindow; - info_error ("No more nodes."); - } - } - } - break; + /* First things first. If this node contains a menu, move down + into the menu. */ + { + REFERENCE **menu; + + menu = info_menu_of_node (window->node); + + if (menu) + { + info_free_references (menu); + window_message_in_echo_area (_("Selecting first menu item...")); + info_menu_digit (window, 1, '1'); + return; + } + } + + /* 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) + { + window_message_in_echo_area (_("Selecting \"Next\" node...")); + info_handle_pointer (_("Next"), window); + return; + } + + /* Okay, there wasn't a "Next:" for this node. Move "Up:" until we + can move "Next:". If that isn't possible, complain that there + are no more nodes. */ + { + int up_counter, old_current; + INFO_WINDOW *info_win; + + /* Remember the current node and location. */ + info_win = get_info_window_of_window (window); + old_current = info_win->current; + + /* Back up through the "Up:" pointers until we have found a "Next:" + that isn't the same as the first menu item found in that node. */ + up_counter = 0; + while (!info_error_was_printed) + { + info_up_label_of_node (window->node); + if (info_label_was_found) + { + info_handle_pointer (_("Up"), window); + if (info_error_was_printed) + continue; + + up_counter++; + + info_next_label_of_node (window->node); + + /* If no "Next" pointer, keep backing up. */ + if (!info_label_was_found) + continue; + + /* If this node's first menu item is the same as this node's + Next pointer, keep backing up. */ + if (!info_parsed_filename) + { + REFERENCE **menu; + char *next_nodename; + + /* Remember the name of the Next node, since reading + the menu can overwrite the contents of the + info_parsed_xxx strings. */ + next_nodename = xstrdup (info_parsed_nodename); + + menu = info_menu_of_node (window->node); + if (menu && + (strcmp + (menu[0]->nodename, next_nodename) == 0)) + { + info_free_references (menu); + free (next_nodename); + continue; + } + else + { + /* Restore the world to where it was before + reading the menu contents. */ + info_free_references (menu); + free (next_nodename); + info_next_label_of_node (window->node); + } + } + + /* 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)); + + info_handle_pointer (_("Next"), window); + return; + } + else + { + /* No more "Up" pointers. Print an error, and call it + quits. */ + register int i; + + for (i = 0; i < up_counter; i++) + { + info_win->nodes_index--; + free (info_win->nodes[info_win->nodes_index]); + info_win->nodes[info_win->nodes_index] = (NODE *)NULL; + } + info_win->current = old_current; + window->node = info_win->nodes[old_current]; + window->pagetop = info_win->pagetops[old_current]; + window->point = info_win->points[old_current]; + recalculate_line_starts (window); + window->flags |= W_UpdateWindow; + info_error (_("No more nodes.")); + } + } + } + break; } } } /* Move Prev, Up or error in WINDOW depending on BEHAVIOUR. */ static void backward_move_node_structure (window, behaviour) WINDOW *window; int behaviour; { switch (behaviour) { case IS_PageOnly: info_error (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) - { - info_up_label_of_node (window->node); - if (!info_parsed_nodename && !info_parsed_filename) - info_error ("No \"Prev\" or \"Up\" for this node."); - else - { - window_message_in_echo_area ("Moving \"Up\" in this window."); - info_handle_pointer ("Up", window); - } - } + { + info_up_label_of_node (window->node); + if (!info_parsed_nodename && !info_parsed_filename) + info_error (_("No \"Prev\" or \"Up\" for this node.")); + else + { + window_message_in_echo_area (_("Moving \"Up\" in this window.")); + info_handle_pointer (_("Up"), window); + } + } else - { - REFERENCE **menu; - int inhibit_menu_traversing = 0; - - /* Watch out! If this node's Prev is the same as the Up, then - move Up. Otherwise, we could move Prev, and then to the last - menu item in the Prev. This would cause the user to loop - through a subsection of the info file. */ - if (!info_parsed_filename && info_parsed_nodename) - { - char *pnode; - - pnode = strdup (info_parsed_nodename); - info_up_label_of_node (window->node); - - if (!info_parsed_filename && info_parsed_nodename && - strcmp (info_parsed_nodename, pnode) == 0) - { - /* The nodes are the same. Inhibit moving to the last - menu item. */ - free (pnode); - inhibit_menu_traversing = 1; - } - else - { - free (pnode); - info_prev_label_of_node (window->node); - } - } - - /* 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); - - if (!inhibit_menu_traversing) - { - while (!info_error_was_printed && - (menu = info_menu_of_node (window->node))) - { - info_free_references (menu); - window_message_in_echo_area - ("Moving to \"Prev\"'s last menu item."); - info_menu_digit (window, 1, '0'); - } - } - } + { + REFERENCE **menu; + int inhibit_menu_traversing = 0; + + /* Watch out! If this node's Prev is the same as the Up, then + move Up. Otherwise, we could move Prev, and then to the last + menu item in the Prev. This would cause the user to loop + through a subsection of the info file. */ + if (!info_parsed_filename && info_parsed_nodename) + { + char *pnode; + + pnode = xstrdup (info_parsed_nodename); + info_up_label_of_node (window->node); + + if (!info_parsed_filename && info_parsed_nodename && + strcmp (info_parsed_nodename, pnode) == 0) + { + /* The nodes are the same. Inhibit moving to the last + menu item. */ + free (pnode); + inhibit_menu_traversing = 1; + } + else + { + free (pnode); + info_prev_label_of_node (window->node); + } + } + + /* 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); + + if (!inhibit_menu_traversing) + { + while (!info_error_was_printed && + (menu = info_menu_of_node (window->node))) + { + info_free_references (menu); + window_message_in_echo_area + (_("Moving to \"Prev\"'s last menu item.")); + info_menu_digit (window, 1, '0'); + } + } + } break; } } /* Move continuously forward through the node structure of this info file. */ DECLARE_INFO_COMMAND (info_global_next_node, - "Move forwards or down through node structure") + _("Move forwards or down through node structure")) { if (count < 0) info_global_prev_node (window, -count, key); else { while (count && !info_error_was_printed) - { - forward_move_node_structure (window, IS_Continuous); - count--; - } + { + forward_move_node_structure (window, IS_Continuous); + count--; + } } } /* Move continuously backward through the node structure of this info file. */ DECLARE_INFO_COMMAND (info_global_prev_node, - "Move backwards or up through node structure") + _("Move backwards or up through node structure")) { if (count < 0) info_global_next_node (window, -count, key); else { while (count && !info_error_was_printed) - { - backward_move_node_structure (window, IS_Continuous); - count--; - } + { + backward_move_node_structure (window, IS_Continuous); + count--; + } } } /* Show the next screen of WINDOW's node. */ -DECLARE_INFO_COMMAND (info_scroll_forward, "Scroll forward in this window") +DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window")) { if (count < 0) info_scroll_backward (window, -count, key); else { int desired_top; /* Without an explicit numeric argument, scroll the bottom two - 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. */ + 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) - { - desired_top = window->pagetop + (window->height - 2); - - /* If there are no more lines to scroll here, error, or get - another node, depending on INFO_SCROLL_BEHAVIOUR. */ - if (desired_top > window->line_count) - { - int behaviour = info_scroll_behaviour; - - /* Here is a hack. If the key being used is not SPC, do the - PageOnly behaviour. */ - if (key != SPC && key != DEL) - behaviour = IS_PageOnly; - - forward_move_node_structure (window, behaviour); - return; - } - } + { + desired_top = window->pagetop + (window->height - 2); + + /* If there are no more lines to scroll here, error, or get + another node, depending on INFO_SCROLL_BEHAVIOUR. */ + if (desired_top > window->line_count) + { + int behaviour = info_scroll_behaviour; + + /* Here is a hack. If the key being used is not SPC, do the + PageOnly behaviour. */ + if (key != SPC && key != DEL) + behaviour = IS_PageOnly; + + forward_move_node_structure (window, behaviour); + return; + } + } else - desired_top = window->pagetop + count; + desired_top = window->pagetop + count; if (desired_top >= window->line_count) - desired_top = window->line_count - 2; + desired_top = window->line_count - 2; if (window->pagetop > desired_top) - return; + return; else - set_window_pagetop (window, desired_top); + set_window_pagetop (window, desired_top); } } /* Show the previous screen of WINDOW's node. */ -DECLARE_INFO_COMMAND (info_scroll_backward, "Scroll backward in this window") +DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window")) { if (count < 0) info_scroll_forward (window, -count, key); else { int desired_top; /* 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. */ + to the bottom of this window, or move to the previous, or Up'th + node. */ if (!info_explicit_arg && count == 1) - { - desired_top = window->pagetop - (window->height - 2); - - if ((desired_top < 0) && (window->pagetop == 0)) - { - int behaviour = info_scroll_behaviour; - - /* Same kind of hack as in info_scroll_forward. If the key - used to invoke this command is not DEL, do only the PageOnly - behaviour. */ - if (key != DEL && key != SPC) - behaviour = IS_PageOnly; - - backward_move_node_structure (window, behaviour); - return; - } - } + { + desired_top = window->pagetop - (window->height - 2); + + if ((desired_top < 0) && (window->pagetop == 0)) + { + int behaviour = info_scroll_behaviour; + + /* Same kind of hack as in info_scroll_forward. If the key + used to invoke this command is not DEL, do only the PageOnly + behaviour. */ + if (key != DEL && key != SPC) + behaviour = IS_PageOnly; + + backward_move_node_structure (window, behaviour); + return; + } + } else - desired_top = window->pagetop - count; + desired_top = window->pagetop - count; if (desired_top < 0) - desired_top = 0; + desired_top = 0; set_window_pagetop (window, desired_top); } } /* Move to the beginning of the node. */ -DECLARE_INFO_COMMAND (info_beginning_of_node, "Move to the start of this node") +DECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node")) { window->pagetop = window->point = 0; window->flags |= W_UpdateWindow; } /* Move to the end of the node. */ -DECLARE_INFO_COMMAND (info_end_of_node, "Move to the end of this node") +DECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node")) { window->point = window->node->nodelen - 1; info_show_point (window); } /* **************************************************************** */ -/* */ -/* Commands for Manipulating Windows */ -/* */ +/* */ +/* Commands for Manipulating Windows */ +/* */ /* **************************************************************** */ /* Make the next window in the chain be the active window. */ -DECLARE_INFO_COMMAND (info_next_window, "Select the next window") +DECLARE_INFO_COMMAND (info_next_window, _("Select the next window")) { if (count < 0) { info_prev_window (window, -count, key); return; } /* If no other window, error now. */ if (!windows->next && !echo_area_is_active) { info_error (ONE_WINDOW); return; } while (count--) { if (window->next) - window = window->next; + window = window->next; else - { - if (window == the_echo_area || !echo_area_is_active) - window = windows; - else - window = the_echo_area; - } + { + if (window == the_echo_area || !echo_area_is_active) + window = windows; + else + window = the_echo_area; + } } if (active_window != window) { if (auto_footnotes_p) - info_get_or_remove_footnotes (window); + info_get_or_remove_footnotes (window); window->flags |= W_UpdateWindow; active_window = window; } } /* Make the previous window in the chain be the active window. */ -DECLARE_INFO_COMMAND (info_prev_window, "Select the previous window") +DECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window")) { if (count < 0) { info_next_window (window, -count, key); return; } /* Only one window? */ if (!windows->next && !echo_area_is_active) { info_error (ONE_WINDOW); return; } while (count--) { /* If we are in the echo area, or if the echo area isn't active and we - are in the first window, find the last window in the chain. */ + are in the first window, find the last window in the chain. */ if (window == the_echo_area || - (window == windows && !echo_area_is_active)) - { - register WINDOW *win, *last; + (window == windows && !echo_area_is_active)) + { + register WINDOW *win, *last; - for (win = windows; win; win = win->next) - last = win; + for (win = windows; win; win = win->next) + last = win; - window = last; - } + window = last; + } else - { - if (window == windows) - window = the_echo_area; - else - window = window->prev; - } + { + if (window == windows) + window = the_echo_area; + else + window = window->prev; + } } if (active_window != window) { if (auto_footnotes_p) - info_get_or_remove_footnotes (window); + info_get_or_remove_footnotes (window); window->flags |= W_UpdateWindow; active_window = window; } } /* Split WINDOW into two windows, both showing the same node. If we are automatically tiling windows, re-tile after the split. */ -DECLARE_INFO_COMMAND (info_split_window, "Split the current window") +DECLARE_INFO_COMMAND (info_split_window, _("Split the current window")) { WINDOW *split, *old_active; int pagetop; /* Remember the current pagetop of the window being split. If it doesn't change, we can scroll its contents around after the split. */ pagetop = window->pagetop; /* Make the new window. */ old_active = active_window; active_window = window; split = window_make_window (window->node); active_window = old_active; if (!split) { info_error (WIN_TOO_SMALL); } else { #if defined (SPLIT_BEFORE_ACTIVE) /* Try to scroll the old window into its new postion. */ if (pagetop == window->pagetop) - { - int start, end, amount; - - start = split->first_row; - end = start + window->height; - amount = split->height + 1; - display_scroll_display (start, end, amount); - } + { + int start, end, amount; + + start = split->first_row; + end = start + window->height; + amount = split->height + 1; + display_scroll_display (start, end, amount); + } #else /* !SPLIT_BEFORE_ACTIVE */ /* Make sure point still appears in the active window. */ info_show_point (window); #endif /* !SPLIT_BEFORE_ACTIVE */ /* If the window just split was one internal to Info, try to display - something else in it. */ + something else in it. */ if (internal_info_node_p (split->node)) - { - register int i, j; - INFO_WINDOW *iw; - NODE *node = (NODE *)NULL; - char *filename; - - for (i = 0; iw = info_windows[i]; i++) - { - for (j = 0; j < iw->nodes_index; j++) - if (!internal_info_node_p (iw->nodes[j])) - { - if (iw->nodes[j]->parent) - filename = iw->nodes[j]->parent; - else - filename = iw->nodes[j]->filename; - - node = info_get_node (filename, iw->nodes[j]->nodename); - if (node) - { - window_set_node_of_window (split, node); - i = info_windows_index - 1; - break; - } - } - } - } + { + register int i, j; + INFO_WINDOW *iw; + NODE *node = (NODE *)NULL; + char *filename; + + for (i = 0; (iw = info_windows[i]); i++) + { + for (j = 0; j < iw->nodes_index; j++) + if (!internal_info_node_p (iw->nodes[j])) + { + if (iw->nodes[j]->parent) + filename = iw->nodes[j]->parent; + else + filename = iw->nodes[j]->filename; + + node = info_get_node (filename, iw->nodes[j]->nodename); + if (node) + { + window_set_node_of_window (split, node); + i = info_windows_index - 1; + break; + } + } + } + } split->pagetop = window->pagetop; if (auto_tiling_p) - window_tile_windows (DONT_TILE_INTERNALS); + window_tile_windows (DONT_TILE_INTERNALS); else - window_adjust_pagetop (split); + window_adjust_pagetop (split); remember_window_and_node (split, split->node); } } /* Delete WINDOW, forgetting the list of last visited nodes. If we are automatically displaying footnotes, show or remove the footnotes window. If we are automatically tiling windows, re-tile after the deletion. */ -DECLARE_INFO_COMMAND (info_delete_window, "Delete the current window") +DECLARE_INFO_COMMAND (info_delete_window, _("Delete the current window")) { if (!windows->next) { info_error (CANT_KILL_LAST); } else if (window->flags & W_WindowIsPerm) { - info_error ("Cannot delete a permanent window"); + info_error (_("Cannot delete a permanent window")); } else { info_delete_window_internal (window); if (auto_footnotes_p) - info_get_or_remove_footnotes (active_window); + info_get_or_remove_footnotes (active_window); if (auto_tiling_p) - window_tile_windows (DONT_TILE_INTERNALS); + window_tile_windows (DONT_TILE_INTERNALS); } } /* Do the physical deletion of WINDOW, and forget this window and associated nodes. */ void info_delete_window_internal (window) WINDOW *window; { if (windows->next && ((window->flags & W_WindowIsPerm) == 0)) { /* We not only delete the window from the display, we forget it from - our list of remembered windows. */ + our list of remembered windows. */ forget_window_and_nodes (window); window_delete_window (window); if (echo_area_is_active) - echo_area_inform_of_deleted_window (window); + echo_area_inform_of_deleted_window (window); } } /* Just keep WINDOW, deleting all others. */ -DECLARE_INFO_COMMAND (info_keep_one_window, "Delete all other windows") +DECLARE_INFO_COMMAND (info_keep_one_window, _("Delete all other windows")) { - int num_deleted; /* The number of windows we deleted. */ + int num_deleted; /* The number of windows we deleted. */ int pagetop, start, end; /* Remember a few things about this window. We may be able to speed up redisplay later by scrolling its contents. */ pagetop = window->pagetop; start = window->first_row; end = start + window->height; num_deleted = 0; while (1) { WINDOW *win; /* Find an eligible window and delete it. If no eligible windows - are found, we are done. A window is eligible for deletion if - is it not permanent, and it is not WINDOW. */ + are found, we are done. A window is eligible for deletion if + is it not permanent, and it is not WINDOW. */ for (win = windows; win; win = win->next) - if (win != window && ((win->flags & W_WindowIsPerm) == 0)) - break; + if (win != window && ((win->flags & W_WindowIsPerm) == 0)) + break; if (!win) - break; + break; info_delete_window_internal (win); num_deleted++; } /* Scroll the contents of this window into the right place so that the user doesn't have to wait any longer than necessary for redisplay. */ if (num_deleted) { int amount; amount = (window->first_row - start); amount -= (window->pagetop - pagetop); display_scroll_display (start, end, amount); } window->flags |= W_UpdateWindow; } /* Scroll the "other" window of WINDOW. */ -DECLARE_INFO_COMMAND (info_scroll_other_window, "Scroll the other window") +DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window")) { WINDOW *other; /* If only one window, give up. */ if (!windows->next) { info_error (ONE_WINDOW); return; } other = window->next; if (!other) other = window->prev; info_scroll_forward (other, count, key); } /* Change the size of WINDOW by AMOUNT. */ -DECLARE_INFO_COMMAND (info_grow_window, "Grow (or shrink) this window") +DECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window")) { window_change_window_height (window, count); } /* When non-zero, tiling takes place automatically when info_split_window is called. */ int auto_tiling_p = 0; /* Tile all of the visible windows. */ DECLARE_INFO_COMMAND (info_tile_windows, - "Divide the available screen space among the visible windows") + _("Divide the available screen space among the visible windows")) { window_tile_windows (TILE_INTERNALS); } /* Toggle the state of this window's wrapping of lines. */ DECLARE_INFO_COMMAND (info_toggle_wrap, - "Toggle the state of line wrapping in the current window") + _("Toggle the state of line wrapping in the current window")) { window_toggle_wrap (window); } /* **************************************************************** */ -/* */ -/* Info Node Commands */ -/* */ +/* */ +/* Info Node Commands */ +/* */ /* **************************************************************** */ /* 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 info_select_reference (window, entry) WINDOW *window; REFERENCE *entry; { NODE *node; char *filename, *nodename, *file_system_error; file_system_error = (char *)NULL; filename = entry->filename; if (!filename) filename = window->node->parent; if (!filename) filename = window->node->filename; if (filename) - filename = strdup (filename); + filename = xstrdup (filename); if (entry->nodename) - nodename = strdup (entry->nodename); + nodename = xstrdup (entry->nodename); else - nodename = strdup ("Top"); + nodename = xstrdup ("Top"); node = info_get_node (filename, nodename); /* Try something a little weird. If the node couldn't be found, and the reference was of the form "foo::", see if the entry->label can be found as a file, with a node of "Top". */ if (!node) { if (info_recent_file_error) - file_system_error = strdup (info_recent_file_error); + file_system_error = xstrdup (info_recent_file_error); if (entry->nodename && (strcmp (entry->nodename, entry->label) == 0)) - { - node = info_get_node (entry->label, "Top"); - if (!node && info_recent_file_error) - { - maybe_free (file_system_error); - file_system_error = strdup (info_recent_file_error); - } - } + { + node = info_get_node (entry->label, "Top"); + if (!node && info_recent_file_error) + { + maybe_free (file_system_error); + file_system_error = xstrdup (info_recent_file_error); + } + } } if (!node) { if (file_system_error) - info_error (file_system_error); + info_error (file_system_error); else - info_error (CANT_FIND_NODE, nodename); + info_error (CANT_FIND_NODE, nodename); } maybe_free (file_system_error); maybe_free (filename); maybe_free (nodename); if (node) { set_remembered_pagetop_and_point (window); info_set_node_of_window (window, node); } } /* Parse the node specification in LINE using WINDOW to default the filename. Select the parsed node in WINDOW and remember it, or error if the node couldn't be found. */ static void info_parse_and_select (line, window) char *line; WINDOW *window; { REFERENCE entry; info_parse_node (line, DONT_SKIP_NEWLINES); entry.nodename = info_parsed_nodename; entry.filename = info_parsed_filename; entry.label = "*info-parse-and-select*"; info_select_reference (window, &entry); } /* Given that the values of INFO_PARSED_FILENAME and INFO_PARSED_NODENAME are previously filled, try to get the node represented by them into WINDOW. The node should have been pointed to by the LABEL pointer of WINDOW->node. */ static void info_handle_pointer (label, window) char *label; WINDOW *window; { if (info_parsed_filename || info_parsed_nodename) { char *filename, *nodename; NODE *node; filename = nodename = (char *)NULL; if (info_parsed_filename) - filename = strdup (info_parsed_filename); + filename = xstrdup (info_parsed_filename); else - { - if (window->node->parent) - filename = strdup (window->node->parent); - else if (window->node->filename) - filename = strdup (window->node->filename); - } + { + if (window->node->parent) + filename = xstrdup (window->node->parent); + else if (window->node->filename) + filename = xstrdup (window->node->filename); + } if (info_parsed_nodename) - nodename = strdup (info_parsed_nodename); + nodename = xstrdup (info_parsed_nodename); else - nodename = strdup ("Top"); + nodename = xstrdup ("Top"); node = info_get_node (filename, nodename); if (node) - { - INFO_WINDOW *info_win; - - info_win = get_info_window_of_window (window); - if (info_win) - { - 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_WINDOW *info_win; + + info_win = get_info_window_of_window (window); + if (info_win) + { + 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); + } else - { - if (info_recent_file_error) - info_error (info_recent_file_error); - else - info_error (CANT_FILE_NODE, filename, nodename); - } + { + if (info_recent_file_error) + info_error (info_recent_file_error); + else + info_error (CANT_FILE_NODE, filename, nodename); + } free (filename); free (nodename); } else { info_error (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. */ -DECLARE_INFO_COMMAND (info_last_node, "Select the last node in this file") +DECLARE_INFO_COMMAND (info_last_node, _("Select the last node in this file")) { register int i; FILE_BUFFER *fb = file_buffer_of_window (window); NODE *node = (NODE *)NULL; if (fb && fb->tags) { for (i = 0; fb->tags[i]; i++); node = info_get_node (fb->filename, fb->tags[i - 1]->nodename); } if (!node) - info_error ("This window has no additional nodes"); + info_error (_("This window has no additional nodes")); else { set_remembered_pagetop_and_point (window); info_set_node_of_window (window, node); } } /* Make WINDOW display the first node of this info file. */ -DECLARE_INFO_COMMAND (info_first_node, "Select the first node in this file") +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 (fb && fb->tags) node = info_get_node (fb->filename, fb->tags[0]->nodename); if (!node) - info_error ("This window has no additional nodes"); + info_error (_("This window has no additional nodes")); else { set_remembered_pagetop_and_point (window); info_set_node_of_window (window, node); } } -/* Make WINDOW display the previous node displayed in this window. */ -DECLARE_INFO_COMMAND (info_history_node, - "Select the most recently selected node") -{ - INFO_WINDOW *info_win; - - /* Find the INFO_WINDOW which contains WINDOW. */ - info_win = get_info_window_of_window (window); - - if (!info_win) - { - info_error ("Requested window is not present!"); - return; - } - - set_remembered_pagetop_and_point (window); - if (!info_win->current) - { - if (info_win->nodes_index > 1) - { - window_message_in_echo_area - ("Now wrapped around to beginning of history."); - info_win->current = info_win->nodes_index; - } - else - { - info_error ("No earlier nodes in this window."); - return; - } - } - - info_win->current--; - window_set_node_of_window (window, info_win->nodes[info_win->current]); - window->pagetop = info_win->pagetops[info_win->current]; - window->point = info_win->points[info_win->current]; - window->flags |= W_UpdateWindow; - if (auto_footnotes_p) - info_get_or_remove_footnotes (window); -} - /* Select the last menu item in WINDOW->node. */ DECLARE_INFO_COMMAND (info_last_menu_item, - "Select the last item in this node's menu") + _("Select the last item in this node's menu")) { info_menu_digit (window, 1, '0'); } /* Use KEY (a digit) to select the Nth menu item in WINDOW->node. */ -DECLARE_INFO_COMMAND (info_menu_digit, "Select this menu item") +DECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item")) { register int i, item; register REFERENCE *entry, **menu; menu = info_menu_of_node (window->node); if (!menu) { info_error (NO_MENU_NODE); return; } /* We have the menu. See if there are this many items in it. */ item = key - '0'; /* Special case. Item "0" is the last item in this menu. */ if (item == 0) for (i = 0; menu[i + 1]; i++); else { - for (i = 0; entry = menu[i]; i++) - if (i == item - 1) - break; + for (i = 0; (entry = menu[i]); i++) + if (i == item - 1) + break; } if (menu[i]) info_select_reference (window, menu[i]); else - info_error ("There aren't %d items in this menu.", item); + info_error (_("There aren't %d items in this menu."), item); info_free_references (menu); return; } /* Read a menu or followed reference from the user defaulting to the reference found on the current line, and select that node. The reading is done with completion. BUILDER is the function used to build the list of references. ASK_P is non-zero if the user should be prompted, or zero to select the default item. */ static void info_menu_or_ref_item (window, count, key, builder, ask_p) WINDOW *window; int count; unsigned char key; REFERENCE **(*builder) (); int ask_p; { REFERENCE **menu, *entry, *defentry = (REFERENCE *)NULL; char *line; menu = (*builder) (window->node); if (!menu) { if (builder == info_menu_of_node) - info_error (NO_MENU_NODE); + info_error (NO_MENU_NODE); else - info_error (NO_XREF_NODE); + info_error (NO_XREF_NODE); return; } /* Default the selected reference to the one which is on the line that point is in. */ { REFERENCE **refs = (REFERENCE **)NULL; int point_line; point_line = window_line_of_point (window); if (point_line != -1) { - SEARCH_BINDING binding; - - binding.buffer = window->node->contents; - binding.start = window->line_starts[point_line] - binding.buffer; - if (window->line_starts[point_line + 1]) - binding.end = window->line_starts[point_line + 1] - binding.buffer; - else - binding.end = window->node->nodelen; - binding.flags = 0; - - if (builder == info_menu_of_node) - { - if (point_line) - { - binding.start--; - refs = info_menu_items (&binding); - } - } - else - { + SEARCH_BINDING binding; + + binding.buffer = window->node->contents; + binding.start = window->line_starts[point_line] - binding.buffer; + if (window->line_starts[point_line + 1]) + binding.end = window->line_starts[point_line + 1] - binding.buffer; + else + binding.end = window->node->nodelen; + binding.flags = 0; + + if (builder == info_menu_of_node) + { + if (point_line) + { + binding.start--; + refs = info_menu_items (&binding); + } + } + else + { #if defined (HANDLE_MAN_PAGES) - if (window->node->flags & N_IsManPage) - refs = manpage_xrefs_in_binding (window->node, &binding); - else + if (window->node->flags & N_IsManPage) + refs = manpage_xrefs_in_binding (window->node, &binding); + else #endif /* HANDLE_MAN_PAGES */ - refs = info_xrefs (&binding); - } - - if (refs) - { - if ((strcmp (refs[0]->label, "Menu") != 0) || - (builder == info_xrefs_of_node)) - { - int which = 0; - - /* Find the closest reference to point. */ - if (builder == info_xrefs_of_node) - { - int closest = -1; - - for (; refs[which]; which++) - { - if ((window->point >= refs[which]->start) && - (window->point <= refs[which]->end)) - { - closest = which; - break; - } - else if (window->point < refs[which]->start) - { - break; - } - } - if (closest == -1) - which--; - else - which = closest; - } - - defentry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); - defentry->label = strdup (refs[which]->label); - defentry->filename = refs[which]->filename; - defentry->nodename = refs[which]->nodename; - - if (defentry->filename) - defentry->filename = strdup (defentry->filename); - if (defentry->nodename) - defentry->nodename = strdup (defentry->nodename); - } - info_free_references (refs); - } + refs = info_xrefs (&binding); + } + + if (refs) + { + if ((strcmp (refs[0]->label, "Menu") != 0) || + (builder == info_xrefs_of_node)) + { + int which = 0; + + /* Find the closest reference to point. */ + if (builder == info_xrefs_of_node) + { + int closest = -1; + + for (; refs[which]; which++) + { + if ((window->point >= refs[which]->start) && + (window->point <= refs[which]->end)) + { + closest = which; + break; + } + else if (window->point < refs[which]->start) + { + break; + } + } + if (closest == -1) + which--; + else + which = closest; + } + + defentry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); + defentry->label = xstrdup (refs[which]->label); + defentry->filename = refs[which]->filename; + defentry->nodename = refs[which]->nodename; + + if (defentry->filename) + defentry->filename = xstrdup (defentry->filename); + if (defentry->nodename) + defentry->nodename = xstrdup (defentry->nodename); + } + info_free_references (refs); + } } } /* If we are going to ask the user a question, do it now. */ if (ask_p) { char *prompt; /* Build the prompt string. */ if (defentry) - prompt = (char *)xmalloc (20 + strlen (defentry->label)); + prompt = (char *)xmalloc (20 + strlen (defentry->label)); else - prompt = (char *)xmalloc (20); + prompt = (char *)xmalloc (20); if (builder == info_menu_of_node) - { - if (defentry) - sprintf (prompt, "Menu item (%s): ", defentry->label); - else - sprintf (prompt, "Menu item: "); - } + { + if (defentry) + sprintf (prompt, _("Menu item (%s): "), defentry->label); + else + sprintf (prompt, _("Menu item: ")); + } else - { - if (defentry) - sprintf (prompt, "Follow xref (%s): ", defentry->label); - else - sprintf (prompt, "Follow xref: "); - } + { + if (defentry) + sprintf (prompt, _("Follow xref (%s): "), defentry->label); + else + sprintf (prompt, _("Follow xref: ")); + } line = info_read_completing_in_echo_area (window, prompt, menu); free (prompt); window = active_window; /* User aborts, just quit. */ if (!line) - { - maybe_free (defentry); - info_free_references (menu); - info_abort_key (window, 0, 0); - return; - } + { + maybe_free (defentry); + info_free_references (menu); + info_abort_key (window, 0, 0); + return; + } /* If we had a default and the user accepted it, use that. */ if (!*line) - { - free (line); - if (defentry) - line = strdup (defentry->label); - else - line = (char *)NULL; - } + { + free (line); + if (defentry) + line = xstrdup (defentry->label); + else + line = (char *)NULL; + } } else { /* Not going to ask any questions. If we have a default entry, use - that, otherwise return. */ + that, otherwise return. */ if (!defentry) - return; + return; else - line = strdup (defentry->label); + line = xstrdup (defentry->label); } if (line) { /* Find the selected label in the references. */ entry = info_get_labeled_reference (line, menu); if (!entry && defentry) - info_error ("The reference disappeared! (%s).", line); + info_error (_("The reference disappeared! (%s)."), line); else - { - NODE *orig; - - orig = window->node; - info_select_reference (window, entry); - if ((builder == info_xrefs_of_node) && (window->node != orig)) - { - long offset; - long start; - - if (window->line_count > 0) - start = window->line_starts[1] - window->node->contents; - else - start = 0; - - offset = - info_target_search_node (window->node, entry->label, start); - - if (offset != -1) - { - window->point = offset; - window_adjust_pagetop (window); - } - } - } + { + NODE *orig; + + orig = window->node; + info_select_reference (window, entry); + if ((builder == info_xrefs_of_node) && (window->node != orig)) + { + long offset; + long start; + + if (window->line_count > 0) + start = window->line_starts[1] - window->node->contents; + else + start = 0; + + offset = + info_target_search_node (window->node, entry->label, start); + + if (offset != -1) + { + window->point = offset; + window_adjust_pagetop (window); + } + } + } free (line); if (defentry) - { - free (defentry->label); - maybe_free (defentry->filename); - maybe_free (defentry->nodename); - free (defentry); - } + { + free (defentry->label); + maybe_free (defentry->filename); + maybe_free (defentry->nodename); + free (defentry); + } } info_free_references (menu); if (!info_error_was_printed) window_clear_echo_area (); } /* Read a line (with completion) which is the name of a menu item, and select that item. */ -DECLARE_INFO_COMMAND (info_menu_item, "Read a menu item and select its node") +DECLARE_INFO_COMMAND (info_menu_item, _("Read a menu item and select its node")) { info_menu_or_ref_item (window, count, key, info_menu_of_node, 1); } /* Read a line (with completion) which is the name of a reference to follow, and select the node. */ DECLARE_INFO_COMMAND - (info_xref_item, "Read a footnote or cross reference and select its node") + (info_xref_item, _("Read a footnote or cross reference and select its node")) { info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1); } /* Position the cursor at the start of this node's menu. */ -DECLARE_INFO_COMMAND (info_find_menu, "Move to the start of this node's menu") +DECLARE_INFO_COMMAND (info_find_menu, _("Move to the start of this node's menu")) { SEARCH_BINDING binding; long position; binding.buffer = window->node->contents; binding.start = 0; binding.end = window->node->nodelen; binding.flags = S_FoldCase | S_SkipDest; position = search (INFO_MENU_LABEL, &binding); if (position == -1) info_error (NO_MENU_NODE); else { window->point = position; window_adjust_pagetop (window); window->flags |= W_UpdateWindow; } } /* Visit as many menu items as is possible, each in a separate window. */ DECLARE_INFO_COMMAND (info_visit_menu, - "Visit as many menu items at once as possible") + _("Visit as many menu items at once as possible")) { register int i; REFERENCE *entry, **menu; menu = info_menu_of_node (window->node); if (!menu) info_error (NO_MENU_NODE); for (i = 0; (!info_error_was_printed) && (entry = menu[i]); i++) { WINDOW *new; new = window_make_window (window->node); window_tile_windows (TILE_INTERNALS); if (!new) - info_error (WIN_TOO_SMALL); + info_error (WIN_TOO_SMALL); else - { - active_window = new; - info_select_reference (new, entry); - } + { + active_window = new; + info_select_reference (new, entry); + } } } /* Read a line of input which is a node name, and go to that node. */ -DECLARE_INFO_COMMAND (info_goto_node, "Read a node name and select it") +DECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it")) { char *line; - NODE *node; #define GOTO_COMPLETES #if defined (GOTO_COMPLETES) /* Build a completion list of all of the known nodes. */ { register int fbi, i; FILE_BUFFER *current; REFERENCE **items = (REFERENCE **)NULL; int items_index = 0; int items_slots = 0; current = file_buffer_of_window (window); for (fbi = 0; info_loaded_files && info_loaded_files[fbi]; fbi++) { - FILE_BUFFER *fb; - REFERENCE *entry; - int this_is_the_current_fb; - - fb = info_loaded_files[fbi]; - this_is_the_current_fb = (current == fb); - - entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); - entry->filename = entry->nodename = (char *)NULL; - entry->label = (char *)xmalloc (4 + strlen (fb->filename)); - sprintf (entry->label, "(%s)*", fb->filename); - - add_pointer_to_array - (entry, items_index, items, items_slots, 10, REFERENCE *); - - if (fb->tags) - { - for (i = 0; fb->tags[i]; i++) - { - 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); - - 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 = strdup (fb->tags[i]->nodename); - add_pointer_to_array (entry, items_index, items, - items_slots, 100, REFERENCE *); - } - } - } + FILE_BUFFER *fb; + REFERENCE *entry; + int this_is_the_current_fb; + + fb = info_loaded_files[fbi]; + this_is_the_current_fb = (current == fb); + + entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); + entry->filename = entry->nodename = (char *)NULL; + entry->label = (char *)xmalloc (4 + strlen (fb->filename)); + sprintf (entry->label, "(%s)*", fb->filename); + + add_pointer_to_array + (entry, items_index, items, items_slots, 10, REFERENCE *); + + if (fb->tags) + { + for (i = 0; fb->tags[i]; i++) + { + 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); + + 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. */ if (!line) { info_abort_key (window, 0, 0); return; } canonicalize_whitespace (line); if (*line) info_parse_and_select (line, window); free (line); if (!info_error_was_printed) window_clear_echo_area (); } #if defined (HANDLE_MAN_PAGES) -DECLARE_INFO_COMMAND (info_man, "Read a manpage reference and select it") +DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it")) { char *line; - NODE *node; - line = info_read_in_echo_area (window, "Get Manpage: "); + line = info_read_in_echo_area (window, _("Get Manpage: ")); if (!line) { info_abort_key (window, 0, 0); return; } canonicalize_whitespace (line); if (*line) { char *goto_command; goto_command = (char *)xmalloc - (4 + strlen (MANPAGE_FILE_BUFFER_NAME) + strlen (line)); + (4 + strlen (MANPAGE_FILE_BUFFER_NAME) + strlen (line)); sprintf (goto_command, "(%s)%s", MANPAGE_FILE_BUFFER_NAME, line); info_parse_and_select (goto_command, window); free (goto_command); } free (line); if (!info_error_was_printed) window_clear_echo_area (); } #endif /* HANDLE_MAN_PAGES */ /* Move to the "Top" node in this file. */ -DECLARE_INFO_COMMAND (info_top_node, "Select the node `Top' 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". */ -DECLARE_INFO_COMMAND (info_dir_node, "Select the node `(dir)'") +DECLARE_INFO_COMMAND (info_dir_node, _("Select the node `(dir)'")) { info_parse_and_select ("(dir)Top", window); } -/* Try to delete the current node appearing in this window, showing the most - recently selected node in this window. */ -DECLARE_INFO_COMMAND (info_kill_node, "Kill this node") + +/* Read the name of a node to kill. The list of available nodes comes + from the nodes appearing in the current window configuration. */ +static char * +read_nodename_to_kill (window) + WINDOW *window; { - register int iw, i; - register INFO_WINDOW *info_win; - char *nodename = (char *)NULL; - NODE *temp = (NODE *)NULL; + int iw; + char *nodename; + INFO_WINDOW *info_win; + REFERENCE **menu = NULL; + int menu_index = 0, menu_slots = 0; + char *default_nodename = xstrdup (active_window->node->nodename); + char *prompt = xmalloc (40 + strlen (default_nodename)); - /* Read the name of a node to kill. The list of available nodes comes - from the nodes appearing in the current window configuration. */ - { - REFERENCE **menu = (REFERENCE **)NULL; - int menu_index = 0, menu_slots = 0; - char *default_nodename, *prompt; + sprintf (prompt, _("Kill node (%s): "), default_nodename); - for (iw = 0; info_win = info_windows[iw]; iw++) - { - REFERENCE *entry; + for (iw = 0; (info_win = info_windows[iw]); iw++) + { + REFERENCE *entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); + entry->label = xstrdup (info_win->window->node->nodename); + entry->filename = entry->nodename = (char *)NULL; - entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); - entry->label = strdup (info_win->window->node->nodename); - entry->filename = entry->nodename = (char *)NULL; + add_pointer_to_array (entry, menu_index, menu, menu_slots, 10, + REFERENCE *); + } - add_pointer_to_array - (entry, menu_index, menu, menu_slots, 10, REFERENCE *); - } + nodename = info_read_completing_in_echo_area (window, prompt, menu); + free (prompt); + info_free_references (menu); + if (nodename && !*nodename) + { + free (nodename); + nodename = default_nodename; + } + else + free (default_nodename); - default_nodename = strdup (active_window->node->nodename); - prompt = (char *)xmalloc (40 + strlen (default_nodename)); - sprintf (prompt, "Kill node (%s): ", default_nodename); + return nodename; +} - nodename = info_read_completing_in_echo_area (window, prompt, menu); - free (prompt); - info_free_references (menu); - if (nodename && !*nodename) - { - free (nodename); - nodename = default_nodename; - } - else - free (default_nodename); - } +/* Delete NODENAME from this window, showing the most + recently selected node in this window. */ +static void +kill_node (window, nodename) + WINDOW *window; + char *nodename; +{ + int iw, i; + INFO_WINDOW *info_win; + NODE *temp; + /* If there is no nodename to kill, quit now. */ if (!nodename) { info_abort_key (window, 0, 0); return; } /* If there is a nodename, find it in our window list. */ - for (iw = 0; info_win = info_windows[iw]; iw++) + for (iw = 0; (info_win = info_windows[iw]); iw++) if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0) break; if (!info_win) { if (*nodename) - info_error ("Cannot kill the node `%s'", nodename); + info_error (_("Cannot kill node `%s'"), nodename); else - window_clear_echo_area (); + window_clear_echo_area (); return; } /* If there are no more nodes left anywhere to view, complain and exit. */ if (info_windows_index == 1 && info_windows[0]->nodes_index == 1) { - info_error ("Cannot kill the last node"); + info_error (_("Cannot kill the last node")); return; } - /* 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. */ + /* 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++]; /* There is one less node in this window's history list. */ info_win->nodes_index--; /* Make this window show the most recent history node. */ info_win->current = info_win->nodes_index - 1; /* If there aren't any nodes left in this window, steal one from the next window. */ if (info_win->current < 0) { INFO_WINDOW *stealer; int which, pagetop; long point; if (info_windows[iw + 1]) - stealer = info_windows[iw + 1]; + stealer = info_windows[iw + 1]; else - stealer = info_windows[0]; + stealer = info_windows[0]; /* If the node being displayed in the next window is not the most - recently loaded one, get the most recently loaded one. */ + recently loaded one, get the most recently loaded one. */ if ((stealer->nodes_index - 1) != stealer->current) - which = stealer->nodes_index - 1; + which = stealer->nodes_index - 1; /* Else, if there is another node behind the stealers current node, - use that one. */ + use that one. */ else if (stealer->current > 0) - which = stealer->current - 1; + which = stealer->current - 1; /* Else, just use the node appearing in STEALER's window. */ else - which = stealer->current; + which = stealer->current; /* Copy this node. */ { - NODE *copy; - - temp = stealer->nodes[which]; - point = stealer->points[which]; - pagetop = stealer->pagetops[which]; - - copy = (NODE *)xmalloc (sizeof (NODE)); - copy->filename = temp->filename; - copy->parent = temp->parent; - copy->nodename = temp->nodename; - copy->contents = temp->contents; - copy->nodelen = temp->nodelen; - copy->flags = temp->flags; - - temp = copy; + NODE *copy = xmalloc (sizeof (NODE)); + + temp = stealer->nodes[which]; + point = stealer->points[which]; + pagetop = stealer->pagetops[which]; + + copy->filename = temp->filename; + copy->parent = temp->parent; + copy->nodename = temp->nodename; + copy->contents = temp->contents; + copy->nodelen = temp->nodelen; + copy->flags = temp->flags; + + temp = copy; } window_set_node_of_window (info_win->window, temp); window->point = point; window->pagetop = pagetop; remember_window_and_node (info_win->window, temp); } else { temp = info_win->nodes[info_win->current]; window_set_node_of_window (info_win->window, temp); } + if (!info_error_was_printed) window_clear_echo_area (); + + if (auto_footnotes_p) + info_get_or_remove_footnotes (window); +} + +/* Kill current node, thus going back one in the node history. I (karl) + do not think this is completely correct yet, because of the + window-changing stuff in kill_node, but it's a lot better than the + previous implementation, which did not account for nodes being + visited twice at all. */ +DECLARE_INFO_COMMAND (info_history_node, + _("Select the most recently selected node")) +{ + kill_node (window, active_window->node->nodename); +} + +/* Kill named node. */ +DECLARE_INFO_COMMAND (info_kill_node, _("Kill this node")) +{ + char *nodename = read_nodename_to_kill (window); + kill_node (window, nodename); } + /* Read the name of a file and select the entire file. */ -DECLARE_INFO_COMMAND (info_view_file, "Read the name of a file and select it") +DECLARE_INFO_COMMAND (info_view_file, _("Read the name of a file and select it")) { char *line; - line = info_read_in_echo_area (window, "Find file: "); + line = info_read_in_echo_area (window, _("Find file: ")); if (!line) { info_abort_key (active_window, 1, 0); return; } if (*line) { NODE *node; node = info_get_node (line, "*"); if (!node) - { - if (info_recent_file_error) - info_error (info_recent_file_error); - else - info_error ("Cannot find \"%s\".", line); - } + { + if (info_recent_file_error) + info_error (info_recent_file_error); + else + info_error (_("Cannot find \"%s\"."), line); + } else - { - set_remembered_pagetop_and_point (active_window); - info_set_node_of_window (window, node); - } + { + set_remembered_pagetop_and_point (active_window); + info_set_node_of_window (window, node); + } free (line); } if (!info_error_was_printed) window_clear_echo_area (); } /* **************************************************************** */ -/* */ -/* Dumping and Printing Nodes */ -/* */ +/* */ +/* Dumping and Printing Nodes */ +/* */ /* **************************************************************** */ #define VERBOSE_NODE_DUMPING static void write_node_to_stream (); static void dump_node_to_stream (); static void initialize_dumping (); /* Dump the nodes specified by FILENAME and NODENAMES to the file named in OUTPUT_FILENAME. If DUMP_SUBNODES is non-zero, recursively dump the nodes which appear in the menu of each node dumped. */ void dump_nodes_to_file (filename, nodenames, output_filename, dump_subnodes) char *filename; char **nodenames; char *output_filename; int dump_subnodes; { register int i; FILE *output_stream; /* Get the stream to print the nodes to. Special case of an output filename of "-" means to dump the nodes to stdout. */ if (strcmp (output_filename, "-") == 0) output_stream = stdout; else output_stream = fopen (output_filename, "w"); if (!output_stream) { - info_error ("Could not create output file \"%s\".", output_filename); + info_error (_("Could not create output file \"%s\"."), output_filename); return; } /* Print each node to stream. */ initialize_dumping (); for (i = 0; nodenames[i]; i++) dump_node_to_stream (filename, nodenames[i], output_stream, dump_subnodes); if (output_stream != stdout) fclose (output_stream); #if defined (VERBOSE_NODE_DUMPING) - info_error ("Done."); + info_error (_("Done.")); #endif /* VERBOSE_NODE_DUMPING */ } /* A place to remember already dumped nodes. */ static char **dumped_already = (char **)NULL; static int dumped_already_index = 0; static int dumped_already_slots = 0; static void initialize_dumping () { dumped_already_index = 0; } /* Get and print the node specified by FILENAME and NODENAME to STREAM. If DUMP_SUBNODES is non-zero, recursively dump the nodes which appear in the menu of each node dumped. */ static void dump_node_to_stream (filename, nodename, stream, dump_subnodes) char *filename, *nodename; FILE *stream; int dump_subnodes; { register int i; NODE *node; node = info_get_node (filename, nodename); if (!node) { if (info_recent_file_error) - info_error (info_recent_file_error); + info_error (info_recent_file_error); else - { - if (filename && *nodename != '(') - info_error - (CANT_FILE_NODE, filename_non_directory (filename), nodename); - else - info_error (CANT_FIND_NODE, nodename); - } + { + if (filename && *nodename != '(') + info_error + (CANT_FILE_NODE, filename_non_directory (filename), nodename); + else + info_error (CANT_FIND_NODE, nodename); + } return; } /* If we have already dumped this node, don't dump it again. */ for (i = 0; i < dumped_already_index; i++) if (strcmp (node->nodename, dumped_already[i]) == 0) { - free (node); - return; + free (node); + return; } add_pointer_to_array (node->nodename, dumped_already_index, dumped_already, - dumped_already_slots, 50, char *); + dumped_already_slots, 50, char *); #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); + 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->nodename); #endif /* VERBOSE_NODE_DUMPING */ write_node_to_stream (node, stream); /* If we are dumping subnodes, get the list of menu items in this node, and dump each one recursively. */ if (dump_subnodes) { REFERENCE **menu = (REFERENCE **)NULL; /* If this node is an Index, do not dump the menu references. */ if (string_in_line ("Index", node->nodename) == -1) - menu = info_menu_of_node (node); + menu = info_menu_of_node (node); if (menu) - { - for (i = 0; menu[i]; i++) - { - /* We don't dump Info files which are different than the - current one. */ - if (!menu[i]->filename) - dump_node_to_stream - (filename, menu[i]->nodename, stream, dump_subnodes); - } - info_free_references (menu); - } + { + for (i = 0; menu[i]; i++) + { + /* We don't dump Info files which are different than the + current one. */ + if (!menu[i]->filename) + dump_node_to_stream + (filename, menu[i]->nodename, stream, dump_subnodes); + } + info_free_references (menu); + } } free (node); } /* Dump NODE to FILENAME. If DUMP_SUBNODES is non-zero, recursively dump the nodes which appear in the menu of each node dumped. */ void dump_node_to_file (node, filename, dump_subnodes) NODE *node; char *filename; int dump_subnodes; { FILE *output_stream; char *nodes_filename; /* Get the stream to print this node to. Special case of an output filename of "-" means to dump the nodes to stdout. */ if (strcmp (filename, "-") == 0) output_stream = stdout; else output_stream = fopen (filename, "w"); if (!output_stream) { - info_error ("Could not create output file \"%s\".", filename); + info_error (_("Could not create output file \"%s\"."), filename); return; } if (node->parent) nodes_filename = node->parent; else nodes_filename = node->filename; initialize_dumping (); dump_node_to_stream (nodes_filename, node->nodename, output_stream, dump_subnodes); if (output_stream != stdout) fclose (output_stream); #if defined (VERBOSE_NODE_DUMPING) - info_error ("Done."); + info_error (_("Done.")); #endif /* VERBOSE_NODE_DUMPING */ } #if !defined (DEFAULT_INFO_PRINT_COMMAND) # define DEFAULT_INFO_PRINT_COMMAND "lpr" #endif /* !DEFAULT_INFO_PRINT_COMMAND */ DECLARE_INFO_COMMAND (info_print_node, - "Pipe the contents of this node through INFO_PRINT_COMMAND") + _("Pipe the contents of this node through INFO_PRINT_COMMAND")) { print_node (window->node); } /* Print NODE on a printer piping it into INFO_PRINT_COMMAND. */ void print_node (node) NODE *node; { - char *print_command, *getenv (); FILE *printer_pipe; - - print_command = getenv ("INFO_PRINT_COMMAND"); + char *print_command = getenv ("INFO_PRINT_COMMAND"); if (!print_command || !*print_command) print_command = DEFAULT_INFO_PRINT_COMMAND; printer_pipe = popen (print_command, "w"); 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); + 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->nodename); #endif /* VERBOSE_NODE_DUMPING */ write_node_to_stream (node, printer_pipe); pclose (printer_pipe); #if defined (VERBOSE_NODE_DUMPING) - info_error ("Done."); + info_error (_("Done.")); #endif /* VERBOSE_NODE_DUMPING */ } static void write_node_to_stream (node, stream) NODE *node; FILE *stream; { fwrite (node->contents, 1, node->nodelen, stream); } /* **************************************************************** */ -/* */ -/* Info Searching Commands */ -/* */ +/* */ +/* Info Searching Commands */ +/* */ /* **************************************************************** */ /* Variable controlling the garbage collection of files briefly visited during searches. Such files are normally gc'ed, unless they were compressed to begin with. If this variable is non-zero, it says to gc even those file buffer contents which had to be uncompressed. */ int gc_compressed_files = 0; static void info_gc_file_buffers (); static char *search_string = (char *)NULL; static int search_string_index = 0; static int search_string_size = 0; static int isearch_is_active = 0; /* Return the file buffer which belongs to WINDOW's node. */ FILE_BUFFER * file_buffer_of_window (window) WINDOW *window; { /* If this window has no node, then it has no file buffer. */ if (!window->node) return ((FILE_BUFFER *)NULL); if (window->node->parent) return (info_find_file (window->node->parent)); if (window->node->filename) return (info_find_file (window->node->filename)); return ((FILE_BUFFER *)NULL); } /* Search for STRING in NODE starting at START. Return -1 if the string was not found, or the location of the string if it was. If WINDOW is passed as non-null, set the window's node to be NODE, its point to be the found string, and readjust the window's pagetop. Final argument 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) char *string; NODE *node; long start; WINDOW *window; int dir; { SEARCH_BINDING binding; long offset; binding.buffer = node->contents; binding.start = start; binding.end = node->nodelen; binding.flags = S_FoldCase; if (dir < 0) { binding.end = 0; binding.flags |= S_SkipDest; } if (binding.start < 0) return (-1); /* For incremental searches, we always wish to skip past the string. */ if (isearch_is_active) binding.flags |= S_SkipDest; offset = search (string, &binding); if (offset != -1 && window) { set_remembered_pagetop_and_point (window); if (window->node != node) - window_set_node_of_window (window, node); + window_set_node_of_window (window, node); window->point = offset; window_adjust_pagetop (window); } return (offset); } /* Search NODE, looking for the largest possible match of STRING. Start the search at START. Return the absolute position of the match, or -1, if no part of the string could be found. */ long info_target_search_node (node, string, start) NODE *node; char *string; long start; { register int i; long offset; char *target; - target = strdup (string); + target = xstrdup (string); i = strlen (target); /* Try repeatedly searching for this string while removing words from the end of it. */ while (i) { target[i] = '\0'; offset = info_search_in_node (target, node, start, (WINDOW *)NULL, 1); if (offset != -1) - break; + break; /* Delete the last word from TARGET. */ for (; i && (!whitespace (target[i]) && (target[i] != ',')); i--); } free (target); return (offset); } /* Search for STRING starting in WINDOW at point. If the string is found in this node, set point to that position. Otherwise, get the file buffer 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) char *string; WINDOW *window; int dir; { register int i; FILE_BUFFER *file_buffer; char *initial_nodename; long ret, start = 0; 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); + (string, window->node, window->point + dir, window, dir); } else { ret = info_search_in_node - (string, window->node, window->point, window, dir); + (string, window->node, window->point, window, dir); } maybe_free (last_searched_for_string); - last_searched_for_string = strdup (string); + last_searched_for_string = xstrdup (string); if (ret != -1) { /* We won! */ if (!echo_area_is_active && !isearch_is_active) - window_clear_echo_area (); + window_clear_echo_area (); return (0); } /* The string wasn't found in the current node. Search through the window's file buffer, iff the current node is not "*". */ if (!file_buffer || (strcmp (initial_nodename, "*") == 0)) return (-1); /* If this file has tags, search through every subfile, starting at this node's subfile and node. Otherwise, search through the file's node list. */ if (file_buffer->tags) { register int current_tag, number_of_tags; char *last_subfile; TAG *tag; /* Find number of tags and current tag. */ last_subfile = (char *)NULL; for (i = 0; file_buffer->tags[i]; i++) - if (strcmp (initial_nodename, file_buffer->tags[i]->nodename) == 0) - { - current_tag = i; - last_subfile = file_buffer->tags[i]->filename; - } + if (strcmp (initial_nodename, file_buffer->tags[i]->nodename) == 0) + { + current_tag = i; + last_subfile = file_buffer->tags[i]->filename; + } number_of_tags = i; /* If there is no last_subfile, our tag wasn't found. */ if (!last_subfile) - return (-1); + return (-1); /* Search through subsequent nodes, wrapping around to the top - of the info file until we find the string or return to this - window's node and point. */ + of the info file until we find the string or return to this + window's node and point. */ while (1) - { - NODE *node; - - /* Allow C-g to quit the search, failing it if pressed. */ - return_if_control_g (-1); - - current_tag += dir; - - if (current_tag < 0) - current_tag = number_of_tags - 1; - else if (current_tag == number_of_tags) - current_tag = 0; - - tag = file_buffer->tags[current_tag]; - - if (!echo_area_is_active && (last_subfile != tag->filename)) - { - window_message_in_echo_area - ("Searching subfile \"%s\"...", - filename_non_directory (tag->filename)); - - last_subfile = tag->filename; - } - - node = info_get_node (file_buffer->filename, tag->nodename); - - if (!node) - { - /* If not doing i-search... */ - if (!echo_area_is_active) - { - if (info_recent_file_error) - info_error (info_recent_file_error); - else - info_error (CANT_FILE_NODE, - filename_non_directory (file_buffer->filename), - tag->nodename); - } - return (-1); - } - - if (dir < 0) - start = tag->nodelen; - - ret = - info_search_in_node (string, node, start, window, dir); - - /* Did we find the string in this node? */ - if (ret != -1) - { - /* Yes! We win. */ - remember_window_and_node (window, node); - if (!echo_area_is_active) - window_clear_echo_area (); - return (0); - } - - /* No. Free this node, and make sure that we haven't passed - our starting point. */ - free (node); - - if (strcmp (initial_nodename, tag->nodename) == 0) - return (-1); - } + { + NODE *node; + + /* Allow C-g to quit the search, failing it if pressed. */ + return_if_control_g (-1); + + current_tag += dir; + + if (current_tag < 0) + current_tag = number_of_tags - 1; + else if (current_tag == number_of_tags) + current_tag = 0; + + tag = file_buffer->tags[current_tag]; + + if (!echo_area_is_active && (last_subfile != tag->filename)) + { + window_message_in_echo_area + (_("Searching subfile \"%s\"..."), + filename_non_directory (tag->filename)); + + last_subfile = tag->filename; + } + + node = info_get_node (file_buffer->filename, tag->nodename); + + if (!node) + { + /* If not doing i-search... */ + if (!echo_area_is_active) + { + if (info_recent_file_error) + info_error (info_recent_file_error); + else + info_error (CANT_FILE_NODE, + filename_non_directory (file_buffer->filename), + tag->nodename); + } + return (-1); + } + + if (dir < 0) + start = tag->nodelen; + + ret = + info_search_in_node (string, node, start, window, dir); + + /* Did we find the string in this node? */ + if (ret != -1) + { + /* Yes! We win. */ + remember_window_and_node (window, node); + if (!echo_area_is_active) + window_clear_echo_area (); + return (0); + } + + /* No. Free this node, and make sure that we haven't passed + our starting point. */ + free (node); + + if (strcmp (initial_nodename, tag->nodename) == 0) + return (-1); + } } return (-1); } -DECLARE_INFO_COMMAND (info_search, "Read a string and search for it") +DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it")) { char *line, *prompt; int result, old_pagetop; int direction; if (count < 0) direction = -1; else direction = 1; /* Read a string from the user, defaulting the search to SEARCH_STRING. */ if (!search_string) { search_string = (char *)xmalloc (search_string_size = 100); search_string[0] = '\0'; } prompt = (char *)xmalloc (50 + strlen (search_string)); - sprintf (prompt, "%s for string [%s]: ", - direction < 0 ? "Search backward" : "Search", - 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) { 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))); + search_string = (char *) + xrealloc (search_string, (search_string_size += 50 + strlen (line))); strcpy (search_string, line); search_string_index = strlen (line); free (line); } old_pagetop = active_window->pagetop; result = info_search_internal (search_string, active_window, direction); if (result != 0 && !info_error_was_printed) - info_error ("Search failed."); + info_error (_("Search failed.")); else if (old_pagetop != active_window->pagetop) { int new_pagetop; new_pagetop = active_window->pagetop; active_window->pagetop = old_pagetop; set_window_pagetop (active_window, new_pagetop); if (auto_footnotes_p) - info_get_or_remove_footnotes (active_window); + info_get_or_remove_footnotes (active_window); } /* Perhaps free the unreferenced file buffers that were searched, but not retained. */ info_gc_file_buffers (); } /* **************************************************************** */ -/* */ -/* Incremental Searching */ -/* */ +/* */ +/* Incremental Searching */ +/* */ /* **************************************************************** */ static void incremental_search (); DECLARE_INFO_COMMAND (isearch_forward, - "Search interactively for a string as you type it") + _("Search interactively for a string as you type it")) { incremental_search (window, count, key); } DECLARE_INFO_COMMAND (isearch_backward, - "Search interactively for a string as you type it") + _("Search interactively for a string as you type it")) { incremental_search (window, -count, key); } /* Incrementally search for a string as it is typed. */ /* The last accepted incremental search string. */ static char *last_isearch_accepted = (char *)NULL; /* The current incremental search string. */ static char *isearch_string = (char *)NULL; static int isearch_string_index = 0; static int isearch_string_size = 0; static unsigned char isearch_terminate_search_key = ESC; /* Structure defining the current state of an incremental search. */ typedef struct { - WINDOW_STATE_DECL; /* The node, pagetop and point. */ - int search_index; /* Offset of the last char in the search string. */ - int direction; /* The direction that this search is heading in. */ - int failing; /* Whether or not this search failed. */ + WINDOW_STATE_DECL; /* The node, pagetop and point. */ + int search_index; /* Offset of the last char in the search string. */ + int direction; /* The direction that this search is heading in. */ + int failing; /* Whether or not this search failed. */ } SEARCH_STATE; /* Array of search states. */ static SEARCH_STATE **isearch_states = (SEARCH_STATE **)NULL; static int isearch_states_index = 0; static int isearch_states_slots = 0; /* Push the state of this search. */ static void push_isearch (window, search_index, direction, failing) WINDOW *window; int search_index, direction, failing; { SEARCH_STATE *state; state = (SEARCH_STATE *)xmalloc (sizeof (SEARCH_STATE)); window_get_state (window, state); state->search_index = search_index; state->direction = direction; state->failing = failing; add_pointer_to_array (state, isearch_states_index, isearch_states, - isearch_states_slots, 20, SEARCH_STATE *); + isearch_states_slots, 20, SEARCH_STATE *); } /* Pop the state of this search to WINDOW, SEARCH_INDEX, and DIRECTION. */ static void pop_isearch (window, search_index, direction, failing) WINDOW *window; int *search_index, *direction, *failing; { SEARCH_STATE *state; if (isearch_states_index) { isearch_states_index--; state = isearch_states[isearch_states_index]; window_set_state (window, state); *search_index = state->search_index; *direction = state->direction; *failing = state->failing; free (state); isearch_states[isearch_states_index] = (SEARCH_STATE *)NULL; } } /* Free the memory used by isearch_states. */ static void free_isearch_states () { register int i; for (i = 0; i < isearch_states_index; i++) { free (isearch_states[i]); isearch_states[i] = (SEARCH_STATE *)NULL; } isearch_states_index = 0; } /* Display the current search in the echo area. */ static void show_isearch_prompt (dir, string, failing_p) int dir; unsigned char *string; int failing_p; { register int i; char *prefix, *prompt, *p_rep; int prompt_len, p_rep_index, p_rep_size; if (dir < 0) - prefix = "I-search backward: "; + prefix = _("I-search backward: "); else - prefix = "I-search: "; + prefix = _("I-search: "); p_rep_index = p_rep_size = 0; p_rep = (char *)NULL; for (i = 0; string[i]; i++) { char *rep; switch (string[i]) - { - case ' ': rep = " "; break; - case LFD: rep = "\\n"; break; - case TAB: rep = "\\t"; break; - default: - rep = pretty_keyname (string[i]); - } + { + case ' ': rep = " "; break; + case LFD: rep = "\\n"; break; + case TAB: rep = "\\t"; break; + default: + rep = pretty_keyname (string[i]); + } if ((p_rep_index + strlen (rep) + 1) >= p_rep_size) - p_rep = (char *)xrealloc (p_rep, p_rep_size += 100); + p_rep = (char *)xrealloc (p_rep, p_rep_size += 100); strcpy (p_rep + p_rep_index, rep); p_rep_index += strlen (rep); } prompt_len = strlen (prefix) + p_rep_index + 20; prompt = (char *)xmalloc (prompt_len); - sprintf (prompt, "%s%s%s", failing_p ? "Failing " : "", prefix, - p_rep ? p_rep : ""); + sprintf (prompt, "%s%s%s", failing_p ? _("Failing ") : "", prefix, + p_rep ? p_rep : ""); window_message_in_echo_area ("%s", prompt); maybe_free (p_rep); free (prompt); display_cursor_at_point (active_window); } static void incremental_search (window, count, ignore) WINDOW *window; int count; unsigned char ignore; { unsigned char key; int last_search_result, search_result, dir; SEARCH_STATE mystate, orig_state; if (count < 0) dir = -1; else dir = 1; last_search_result = search_result = 0; window_get_state (window, &orig_state); isearch_string_index = 0; if (!isearch_string_size) isearch_string = (char *)xmalloc (isearch_string_size = 50); /* Show the search string in the echo area. */ isearch_string[isearch_string_index] = '\0'; show_isearch_prompt (dir, isearch_string, search_result); isearch_is_active = 1; while (isearch_is_active) { VFunction *func = (VFunction *)NULL; int quoted = 0; /* If a recent display was interrupted, then do the redisplay now if - it is convenient. */ + it is convenient. */ if (!info_any_buffered_input_p () && display_was_interrupted_p) - { - display_update_one_window (window); - display_cursor_at_point (active_window); - } + { + display_update_one_window (window); + display_cursor_at_point (active_window); + } /* Read a character and dispatch on it. */ key = info_get_input_char (); window_get_state (window, &mystate); if (key == DEL) - { - /* User wants to delete one level of search? */ - if (!isearch_states_index) - { - terminal_ring_bell (); - continue; - } - else - { - pop_isearch - (window, &isearch_string_index, &dir, &search_result); - isearch_string[isearch_string_index] = '\0'; - show_isearch_prompt (dir, isearch_string, search_result); - goto after_search; - } - } + { + /* User wants to delete one level of search? */ + if (!isearch_states_index) + { + terminal_ring_bell (); + continue; + } + else + { + pop_isearch + (window, &isearch_string_index, &dir, &search_result); + isearch_string[isearch_string_index] = '\0'; + show_isearch_prompt (dir, isearch_string, search_result); + goto after_search; + } + } else if (key == Control ('q')) - { - key = info_get_input_char (); - quoted = 1; - } + { + key = info_get_input_char (); + quoted = 1; + } /* We are about to search again, or quit. Save the current search. */ push_isearch (window, isearch_string_index, dir, search_result); if (quoted) - goto insert_and_search; + goto insert_and_search; if (!Meta_p (key) || (ISO_Latin_p && key < 160)) - { - func = window->keymap[key].function; - - /* If this key invokes an incremental search, then this means that - we will either search again in the same direction, search - again in the reverse direction, or insert the last search - string that was accepted through incremental searching. */ - if (func == isearch_forward || func == isearch_backward) - { - if ((func == isearch_forward && dir > 0) || - (func == isearch_backward && dir < 0)) - { - /* If the user has typed no characters, then insert the - last successful search into the current search string. */ - if (isearch_string_index == 0) - { - /* Of course, there must be something to insert. */ - if (last_isearch_accepted) - { - if (strlen (last_isearch_accepted) + 1 >= - isearch_string_size) - isearch_string = (char *) - xrealloc (isearch_string, - isearch_string_size += 10 + - strlen (last_isearch_accepted)); - strcpy (isearch_string, last_isearch_accepted); - isearch_string_index = strlen (isearch_string); - goto search_now; - } - else - continue; - } - else - { - /* Search again in the same direction. This means start - from a new place if the last search was successful. */ - if (search_result == 0) - window->point += dir; - } - } - else - { - /* Reverse the direction of the search. */ - dir = -dir; - } - } - else if (isprint (key) || func == (VFunction *)NULL) - { - insert_and_search: - - if (isearch_string_index + 2 >= isearch_string_size) - isearch_string = (char *)xrealloc - (isearch_string, isearch_string_size += 100); - - isearch_string[isearch_string_index++] = key; - isearch_string[isearch_string_index] = '\0'; - goto search_now; - } - else if (func == info_abort_key) - { - /* If C-g pressed, and the search is failing, pop the search - stack back to the last unfailed search. */ - if (isearch_states_index && (search_result != 0)) - { - terminal_ring_bell (); - while (isearch_states_index && (search_result != 0)) - pop_isearch - (window, &isearch_string_index, &dir, &search_result); - isearch_string[isearch_string_index] = '\0'; - show_isearch_prompt (dir, isearch_string, search_result); - continue; - } - else - goto exit_search; - } - else - goto exit_search; - } + { + func = window->keymap[key].function; + + /* If this key invokes an incremental search, then this means that + we will either search again in the same direction, search + again in the reverse direction, or insert the last search + string that was accepted through incremental searching. */ + if (func == isearch_forward || func == isearch_backward) + { + if ((func == isearch_forward && dir > 0) || + (func == isearch_backward && dir < 0)) + { + /* If the user has typed no characters, then insert the + last successful search into the current search string. */ + if (isearch_string_index == 0) + { + /* Of course, there must be something to insert. */ + if (last_isearch_accepted) + { + if (strlen (last_isearch_accepted) + 1 >= + isearch_string_size) + isearch_string = (char *) + xrealloc (isearch_string, + isearch_string_size += 10 + + strlen (last_isearch_accepted)); + strcpy (isearch_string, last_isearch_accepted); + isearch_string_index = strlen (isearch_string); + goto search_now; + } + else + continue; + } + else + { + /* Search again in the same direction. This means start + from a new place if the last search was successful. */ + if (search_result == 0) + window->point += dir; + } + } + else + { + /* Reverse the direction of the search. */ + dir = -dir; + } + } + else if (isprint (key) || func == (VFunction *)NULL) + { + insert_and_search: + + if (isearch_string_index + 2 >= isearch_string_size) + isearch_string = (char *)xrealloc + (isearch_string, isearch_string_size += 100); + + isearch_string[isearch_string_index++] = key; + isearch_string[isearch_string_index] = '\0'; + goto search_now; + } + else if (func == info_abort_key) + { + /* If C-g pressed, and the search is failing, pop the search + stack back to the last unfailed search. */ + if (isearch_states_index && (search_result != 0)) + { + terminal_ring_bell (); + while (isearch_states_index && (search_result != 0)) + pop_isearch + (window, &isearch_string_index, &dir, &search_result); + isearch_string[isearch_string_index] = '\0'; + show_isearch_prompt (dir, isearch_string, search_result); + continue; + } + else + goto exit_search; + } + else + goto exit_search; + } else - { - exit_search: - /* The character is not printable, or it has a function which is - non-null. Exit the search, remembering the search string. If - the key is not the same as the isearch_terminate_search_key, - then push it into pending input. */ - if (isearch_string_index && func != info_abort_key) - { - maybe_free (last_isearch_accepted); - last_isearch_accepted = strdup (isearch_string); - } - - if (key != isearch_terminate_search_key) - info_set_pending_input (key); - - if (func == info_abort_key) - { - if (isearch_states_index) - window_set_state (window, &orig_state); - } - - if (!echo_area_is_active) - window_clear_echo_area (); - - if (auto_footnotes_p) - info_get_or_remove_footnotes (active_window); - - isearch_is_active = 0; - continue; - } + { + exit_search: + /* The character is not printable, or it has a function which is + non-null. Exit the search, remembering the search string. If + the key is not the same as the isearch_terminate_search_key, + then push it into pending input. */ + if (isearch_string_index && func != info_abort_key) + { + maybe_free (last_isearch_accepted); + last_isearch_accepted = xstrdup (isearch_string); + } + + if (key != isearch_terminate_search_key) + info_set_pending_input (key); + + if (func == info_abort_key) + { + if (isearch_states_index) + window_set_state (window, &orig_state); + } + + if (!echo_area_is_active) + window_clear_echo_area (); + + if (auto_footnotes_p) + info_get_or_remove_footnotes (active_window); + + isearch_is_active = 0; + continue; + } /* Search for the contents of isearch_string. */ search_now: show_isearch_prompt (dir, isearch_string, search_result); 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, - isearch_string, isearch_string_index) == 0)) || - ((dir > 0) && - ((window->point - isearch_string_index) >= 0) && - (strncasecmp (window->node->contents + - (window->point - (isearch_string_index - 1)), - isearch_string, isearch_string_index) == 0))) - { - if (dir > 0) - window->point++; - } - else - search_result = info_search_internal (isearch_string, window, dir); - } + { + /* 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, + isearch_string, isearch_string_index) == 0)) || + ((dir > 0) && + ((window->point - isearch_string_index) >= 0) && + (strncasecmp (window->node->contents + + (window->point - (isearch_string_index - 1)), + isearch_string, isearch_string_index) == 0))) + { + if (dir > 0) + window->point++; + } + else + search_result = info_search_internal (isearch_string, window, dir); + } /* If this search failed, and we didn't already have a failed search, - then ring the terminal bell. */ + then ring the terminal bell. */ if (search_result != 0 && last_search_result == 0) - terminal_ring_bell (); + terminal_ring_bell (); after_search: show_isearch_prompt (dir, isearch_string, search_result); if (search_result == 0) - { - if ((mystate.node == window->node) && - (mystate.pagetop != window->pagetop)) - { - int newtop = window->pagetop; - window->pagetop = mystate.pagetop; - set_window_pagetop (window, newtop); - } - display_update_one_window (window); - display_cursor_at_point (window); - } + { + if ((mystate.node == window->node) && + (mystate.pagetop != window->pagetop)) + { + int newtop = window->pagetop; + window->pagetop = mystate.pagetop; + set_window_pagetop (window, newtop); + } + display_update_one_window (window); + display_cursor_at_point (window); + } last_search_result = search_result; } /* Free the memory used to remember each search state. */ free_isearch_states (); /* Perhaps GC some file buffers. */ info_gc_file_buffers (); /* After searching, leave the window in the correct state. */ if (!echo_area_is_active) window_clear_echo_area (); } /* GC some file buffers. A file buffer can be gc-ed if there we have no nodes in INFO_WINDOWS that reference this file buffer's contents. Garbage collecting a file buffer means to free the file buffers contents. */ static void info_gc_file_buffers () { register int fb_index, iw_index, i; register FILE_BUFFER *fb; register INFO_WINDOW *iw; if (!info_loaded_files) return; - for (fb_index = 0; fb = info_loaded_files[fb_index]; fb_index++) + for (fb_index = 0; (fb = info_loaded_files[fb_index]); fb_index++) { int fb_referenced_p = 0; /* If already gc-ed, do nothing. */ if (!fb->contents) - continue; + continue; /* If this file had to be uncompressed, check to see if we should - gc it. This means that the user-variable "gc-compressed-files" - is non-zero. */ + gc it. This means that the user-variable "gc-compressed-files" + is non-zero. */ if ((fb->flags & N_IsCompressed) && !gc_compressed_files) - continue; + continue; /* If this file's contents are not gc-able, move on. */ if (fb->flags & N_CannotGC) - continue; + continue; /* Check each INFO_WINDOW to see if it has any nodes which reference - this file. */ - for (iw_index = 0; iw = info_windows[iw_index]; iw_index++) - { - 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)) - { - fb_referenced_p = 1; - break; - } - } - } + this file. */ + for (iw_index = 0; (iw = info_windows[iw_index]); iw_index++) + { + 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)) + { + fb_referenced_p = 1; + break; + } + } + } /* If this file buffer wasn't referenced, free its contents. */ if (!fb_referenced_p) - { - free (fb->contents); - fb->contents = (char *)NULL; - } + { + free (fb->contents); + fb->contents = (char *)NULL; + } } } /* **************************************************************** */ -/* */ -/* Traversing and Selecting References */ -/* */ +/* */ +/* Traversing and Selecting References */ +/* */ /* **************************************************************** */ /* Move to the next or previous cross reference in this node. */ static void info_move_to_xref (window, count, key, dir) WINDOW *window; int count; unsigned char key; int dir; { long firstmenu, firstxref; long nextmenu, nextxref; long placement = -1; long start = 0; NODE *node = window->node; if (dir < 0) start = node->nodelen; /* This search is only allowed to fail if there is no menu or cross reference in the current node. Otherwise, the first menu or xref found is moved to. */ firstmenu = info_search_in_node (INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir); /* FIRSTMENU may point directly to the line defining the menu. Skip that and go directly to the first item. */ if (firstmenu != -1) { char *text = node->contents + firstmenu; 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); + firstmenu = info_search_in_node + (INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, (WINDOW *)NULL, dir); } firstxref = info_search_in_node (INFO_XREF_LABEL, node, start, (WINDOW *)NULL, dir); #if defined (HANDLE_MAN_PAGES) if ((firstxref == -1) && (node->flags & N_IsManPage)) { firstxref = locate_manpage_xref (node, start, dir); } #endif /* HANDLE_MAN_PAGES */ if (firstmenu == -1 && firstxref == -1) { - info_error ("No cross references in this node."); + info_error (_("No cross references in this node.")); return; } /* There is at least one cross reference or menu entry in this node. Try hard to find the next available one. */ nextmenu = info_search_in_node (INFO_MENU_ENTRY_LABEL, node, window->point + dir, (WINDOW *)NULL, dir); nextxref = info_search_in_node (INFO_XREF_LABEL, node, window->point + dir, (WINDOW *)NULL, dir); #if defined (HANDLE_MAN_PAGES) if ((nextxref == -1) && (node->flags & N_IsManPage) && (firstxref != -1)) nextxref = locate_manpage_xref (node, window->point + dir, dir); #endif /* HANDLE_MAN_PAGES */ /* Ignore "Menu:" as a menu item. */ if (nextmenu != -1) { char *text = node->contents + nextmenu; 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); + nextmenu = info_search_in_node + (INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, (WINDOW *)NULL, dir); } /* If there is both a next menu entry, and a next xref entry, choose the one which occurs first. Otherwise, select the one which actually appears in this node following point. */ if (nextmenu != -1 && nextxref != -1) { if (((dir == 1) && (nextmenu < nextxref)) || - ((dir == -1) && (nextmenu > nextxref))) - placement = nextmenu + 1; + ((dir == -1) && (nextmenu > nextxref))) + placement = nextmenu + 1; else - placement = nextxref; + placement = nextxref; } else if (nextmenu != -1) placement = nextmenu + 1; else if (nextxref != -1) placement = nextxref; /* If there was neither a menu or xref entry appearing in this node after point, choose the first menu or xref entry appearing in this node. */ if (placement == -1) { if (firstmenu != -1 && firstxref != -1) - { - if (((dir == 1) && (firstmenu < firstxref)) || - ((dir == -1) && (firstmenu > firstxref))) - placement = firstmenu + 1; - else - placement = firstxref; - } + { + if (((dir == 1) && (firstmenu < firstxref)) || + ((dir == -1) && (firstmenu > firstxref))) + placement = firstmenu + 1; + else + placement = firstxref; + } else if (firstmenu != -1) - placement = firstmenu + 1; + placement = firstmenu + 1; else - placement = firstxref; + placement = firstxref; } window->point = placement; window_adjust_pagetop (window); window->flags |= W_UpdateWindow; } DECLARE_INFO_COMMAND (info_move_to_prev_xref, - "Move to the previous cross reference") + _("Move to the previous cross reference")) { if (count < 0) info_move_to_prev_xref (window, -count, key); else info_move_to_xref (window, count, key, -1); } DECLARE_INFO_COMMAND (info_move_to_next_xref, - "Move to the next cross reference") + _("Move to the next cross reference")) { if (count < 0) info_move_to_next_xref (window, -count, key); else info_move_to_xref (window, count, key, 1); } /* Select the menu item or reference that appears on this line. */ DECLARE_INFO_COMMAND (info_select_reference_this_line, - "Select reference or menu item appearing on this line") + _("Select reference or menu item appearing on this line")) { char *line; NODE *orig; line = window->line_starts[window_line_of_point (window)]; orig = window->node; /* If this line contains a menu item, select that one. */ if (strncmp ("* ", line, 2) == 0) info_menu_or_ref_item (window, count, key, info_menu_of_node, 0); else info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 0); } /* **************************************************************** */ -/* */ -/* Miscellaneous Info Commands */ -/* */ +/* */ +/* Miscellaneous Info Commands */ +/* */ /* **************************************************************** */ /* What to do when C-g is pressed in a window. */ -DECLARE_INFO_COMMAND (info_abort_key, "Cancel current operation") +DECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation")) { /* If error printing doesn't oridinarily ring the bell, do it now, since C-g always rings the bell. Otherwise, let the error printer do it. */ if (!info_error_rings_bell_p) terminal_ring_bell (); - info_error ("Quit"); + info_error (_("Quit")); info_initialize_numeric_arg (); info_clear_pending_input (); info_last_executed_command = (VFunction *)NULL; } /* 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 to the cursor to a specific line of the window")) { int line; /* With no numeric argument of any kind, default to the center line. */ if (!info_explicit_arg && count == 1) line = (window->height / 2) + window->pagetop; else { if (count < 0) - line = (window->height + count) + window->pagetop; + line = (window->height + count) + window->pagetop; else - line = window->pagetop + count; + line = window->pagetop + count; } /* If the line doesn't appear in this window, make it do so. */ if ((line - window->pagetop) >= window->height) line = window->pagetop + (window->height - 1); /* If the line is too small, make it fit. */ if (line < window->pagetop) line = window->pagetop; /* If the selected line is past the bottom of the node, force it back. */ if (line >= window->line_count) line = window->line_count - 1; window->point = (window->line_starts[line] - window->node->contents); } /* Clear the screen and redraw its contents. Given a numeric argument, move the line the cursor is on to the COUNT'th line of the window. */ -DECLARE_INFO_COMMAND (info_redraw_display, "Redraw the display") +DECLARE_INFO_COMMAND (info_redraw_display, _("Redraw the display")) { if ((!info_explicit_arg && count == 1) || echo_area_is_active) { terminal_clear_screen (); display_clear_display (the_display); window_mark_chain (windows, W_UpdateWindow); display_update_display (windows); } else { int desired_line, point_line; int new_pagetop; point_line = window_line_of_point (window) - window->pagetop; if (count < 0) - desired_line = window->height + count; + desired_line = window->height + count; else - desired_line = count; + desired_line = count; if (desired_line < 0) - desired_line = 0; + desired_line = 0; if (desired_line >= window->height) - desired_line = window->height - 1; + desired_line = window->height - 1; if (desired_line == point_line) - return; + return; new_pagetop = window->pagetop + (point_line - desired_line); set_window_pagetop (window, new_pagetop); } } /* This command does nothing. It is the fact that a key is bound to it that has meaning. See the code at the top of info_session (). */ -DECLARE_INFO_COMMAND (info_quit, "Quit using Info") +DECLARE_INFO_COMMAND (info_quit, _("Quit using Info")) {} /* **************************************************************** */ -/* */ -/* Reading Keys and Dispatching on Them */ -/* */ +/* */ +/* Reading Keys and Dispatching on Them */ +/* */ /* **************************************************************** */ /* Declaration only. Special cased in info_dispatch_on_key (). */ DECLARE_INFO_COMMAND (info_do_lowercase_version, "") {} static void dispatch_error (keyseq) char *keyseq; { char *rep; rep = pretty_keyseq (keyseq); if (!echo_area_is_active) - info_error ("Unknown command (%s).", rep); + info_error (_("Unknown command (%s)."), rep); else { char *temp; - temp = (char *)xmalloc (1 + strlen (rep) + strlen ("\"\" is invalid")); + temp = (char *)xmalloc (1 + strlen (rep) + strlen (_("\"\" is invalid"))); - sprintf (temp, "\"%s\" is invalid", rep); + sprintf (temp, _("\"%s\" is invalid"), rep); terminal_ring_bell (); inform_in_echo_area (temp); free (temp); } } /* Keeping track of key sequences. */ static char *info_keyseq = (char *)NULL; static char keyseq_rep[100]; static int info_keyseq_index = 0; static int info_keyseq_size = 0; static int info_keyseq_displayed_p = 0; /* Initialize the length of the current key sequence. */ void initialize_keyseq () { info_keyseq_index = 0; info_keyseq_displayed_p = 0; } /* Add CHARACTER to the current key sequence. */ void add_char_to_keyseq (character) char character; { if (info_keyseq_index + 2 >= info_keyseq_size) info_keyseq = (char *)xrealloc (info_keyseq, info_keyseq_size += 10); info_keyseq[info_keyseq_index++] = character; info_keyseq[info_keyseq_index] = '\0'; } /* Return the pretty printable string which represents KEYSEQ. */ char * pretty_keyseq (keyseq) char *keyseq; { register int i; keyseq_rep[0] = '\0'; for (i = 0; keyseq[i]; i++) { sprintf (keyseq_rep + strlen (keyseq_rep), "%s%s", - strlen (keyseq_rep) ? " " : "", - pretty_keyname (keyseq[i])); + strlen (keyseq_rep) ? " " : "", + pretty_keyname (keyseq[i])); } return (keyseq_rep); } /* Display the current value of info_keyseq. If argument EXPECTING is non-zero, input is expected to be read after the key sequence is displayed, so add an additional prompting character to the sequence. */ void display_info_keyseq (expecting_future_input) int expecting_future_input; { char *rep; rep = pretty_keyseq (info_keyseq); if (expecting_future_input) strcat (rep, "-"); if (echo_area_is_active) inform_in_echo_area (rep); else { window_message_in_echo_area (rep); display_cursor_at_point (active_window); } info_keyseq_displayed_p = 1; } /* Called by interactive commands to read a keystroke. */ unsigned char info_get_another_input_char () { - int ready = 0; + int ready = !info_keyseq_displayed_p; /* ready if new and pending key */ /* If there isn't any input currently available, then wait a moment looking for input. If we don't get it fast enough, prompt a little bit with the current key sequence. */ - if (!info_keyseq_displayed_p && - !info_any_buffered_input_p () && - !info_input_pending_p ()) + if (!info_keyseq_displayed_p) { + ready = 1; + if (!info_any_buffered_input_p () && + !info_input_pending_p ()) + { #if defined (FD_SET) - struct timeval timer; - fd_set readfds; - - FD_ZERO (&readfds); - FD_SET (fileno (info_input_stream), &readfds); - timer.tv_sec = 0; - timer.tv_usec = 0; - ready = select (1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer); + struct timeval timer; + fd_set readfds; + + FD_ZERO (&readfds); + FD_SET (fileno (info_input_stream), &readfds); + timer.tv_sec = 1; + timer.tv_usec = 750; + ready = select (fileno(info_input_stream)+1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer); +#else + ready = 0; #endif /* FD_SET */ + } } if (!ready) display_info_keyseq (1); return (info_get_input_char ()); } /* Do the command associated with KEY in MAP. If the associated command is really a keymap, then read another key, and dispatch into that map. */ void info_dispatch_on_key (key, map) unsigned char key; Keymap map; { if (Meta_p (key) && (!ISO_Latin_p || map[key].function != ea_insert)) { if (map[ESC].type == ISKMAP) - { - map = (Keymap)map[ESC].function; - add_char_to_keyseq (ESC); - key = UnMeta (key); - info_dispatch_on_key (key, map); - } + { + map = (Keymap)map[ESC].function; + add_char_to_keyseq (ESC); + key = UnMeta (key); + info_dispatch_on_key (key, map); + } else - { - dispatch_error (info_keyseq); - } + { + dispatch_error (info_keyseq); + } return; } switch (map[key].type) { case ISFUNC: { - VFunction *func; - - func = map[key].function; - if (func != (VFunction *)NULL) - { - /* Special case info_do_lowercase_version (). */ - if (func == info_do_lowercase_version) - { - info_dispatch_on_key (tolower (key), map); - return; - } - - add_char_to_keyseq (key); - - if (info_keyseq_displayed_p) - display_info_keyseq (0); - - { - WINDOW *where; - - where = active_window; - (*map[key].function) - (active_window, info_numeric_arg * info_numeric_arg_sign, key); - - /* If we have input pending, then the last command was a prefix - command. Don't change the value of the last function vars. - Otherwise, remember the last command executed in the var - appropriate to the window in which it was executed. */ - if (!info_input_pending_p ()) - { - if (where == the_echo_area) - ea_last_executed_command = map[key].function; - else - info_last_executed_command = map[key].function; - } - } - } - else - { - add_char_to_keyseq (key); - dispatch_error (info_keyseq); - return; - } + VFunction *func; + + func = map[key].function; + if (func != (VFunction *)NULL) + { + /* Special case info_do_lowercase_version (). */ + if (func == info_do_lowercase_version) + { + info_dispatch_on_key (tolower (key), map); + return; + } + + add_char_to_keyseq (key); + + if (info_keyseq_displayed_p) + display_info_keyseq (0); + + { + WINDOW *where; + + where = active_window; + (*map[key].function) + (active_window, info_numeric_arg * info_numeric_arg_sign, key); + + /* If we have input pending, then the last command was a prefix + command. Don't change the value of the last function vars. + Otherwise, remember the last command executed in the var + appropriate to the window in which it was executed. */ + if (!info_input_pending_p ()) + { + if (where == the_echo_area) + ea_last_executed_command = map[key].function; + else + info_last_executed_command = map[key].function; + } + } + } + else + { + add_char_to_keyseq (key); + dispatch_error (info_keyseq); + return; + } } break; case ISKMAP: add_char_to_keyseq (key); if (map[key].function != (VFunction *)NULL) - { - unsigned char newkey; + { + unsigned char newkey; - newkey = info_get_another_input_char (); - info_dispatch_on_key (newkey, (Keymap)map[key].function); - } + newkey = info_get_another_input_char (); + info_dispatch_on_key (newkey, (Keymap)map[key].function); + } else - { - dispatch_error (info_keyseq); - return; - } + { + dispatch_error (info_keyseq); + return; + } break; } } /* **************************************************************** */ -/* */ -/* Numeric Arguments */ -/* */ +/* */ +/* Numeric Arguments */ +/* */ /* **************************************************************** */ /* Handle C-u style numeric args, as well as M--, and M-digits. */ /* Non-zero means that an explicit argument has been passed to this command, as in C-u C-v. */ int info_explicit_arg = 0; /* The sign of the numeric argument. */ int info_numeric_arg_sign = 1; /* The value of the argument itself. */ int info_numeric_arg = 1; /* Add the current digit to the argument in progress. */ DECLARE_INFO_COMMAND (info_add_digit_to_numeric_arg, - "Add this digit to the current numeric argument") + _("Add this digit to the current numeric argument")) { info_numeric_arg_digit_loop (window, 0, key); } /* C-u, universal argument. Multiply the current argument by 4. Read a key. If the key has nothing to do with arguments, then dispatch on it. If the key is the abort character then abort. */ DECLARE_INFO_COMMAND (info_universal_argument, - "Start (or multiply by 4) the current numeric argument") + _("Start (or multiply by 4) the current numeric argument")) { info_numeric_arg *= 4; info_numeric_arg_digit_loop (window, 0, 0); } /* Create a default argument. */ void info_initialize_numeric_arg () { info_numeric_arg = info_numeric_arg_sign = 1; info_explicit_arg = 0; } DECLARE_INFO_COMMAND (info_numeric_arg_digit_loop, - "Internally used by \\[universal-argument]") + _("Internally used by \\[universal-argument]")) { unsigned char pure_key; Keymap keymap = window->keymap; while (1) { if (key) - pure_key = key; + pure_key = key; else - { - if (display_was_interrupted_p && !info_any_buffered_input_p ()) - display_update_display (windows); + { + if (display_was_interrupted_p && !info_any_buffered_input_p ()) + display_update_display (windows); - if (active_window != the_echo_area) - display_cursor_at_point (active_window); + if (active_window != the_echo_area) + display_cursor_at_point (active_window); - pure_key = key = info_get_another_input_char (); + pure_key = key = info_get_another_input_char (); - if (Meta_p (key)) - add_char_to_keyseq (ESC); + if (Meta_p (key)) + add_char_to_keyseq (ESC); - add_char_to_keyseq (UnMeta (key)); - } + add_char_to_keyseq (UnMeta (key)); + } if (Meta_p (key)) - key = UnMeta (key); + key = UnMeta (key); if (keymap[key].type == ISFUNC && - keymap[key].function == info_universal_argument) - { - info_numeric_arg *= 4; - key = 0; - continue; - } + keymap[key].function == info_universal_argument) + { + info_numeric_arg *= 4; + key = 0; + continue; + } if (isdigit (key)) - { - if (info_explicit_arg) - info_numeric_arg = (info_numeric_arg * 10) + (key - '0'); - else - info_numeric_arg = (key - '0'); - info_explicit_arg = 1; - } + { + if (info_explicit_arg) + info_numeric_arg = (info_numeric_arg * 10) + (key - '0'); + else + info_numeric_arg = (key - '0'); + info_explicit_arg = 1; + } else - { - if (key == '-' && !info_explicit_arg) - { - info_numeric_arg_sign = -1; - info_numeric_arg = 1; - } - else - { - info_keyseq_index--; - info_dispatch_on_key (pure_key, keymap); - return; - } - } + { + if (key == '-' && !info_explicit_arg) + { + info_numeric_arg_sign = -1; + info_numeric_arg = 1; + } + else + { + info_keyseq_index--; + info_dispatch_on_key (pure_key, keymap); + return; + } + } key = 0; } } /* **************************************************************** */ -/* */ -/* Input Character Buffering */ -/* */ +/* */ +/* Input Character Buffering */ +/* */ /* **************************************************************** */ /* Character waiting to be read next. */ static int pending_input_character = 0; /* How to make there be no pending input. */ static void info_clear_pending_input () { pending_input_character = 0; } /* How to set the pending input character. */ static void info_set_pending_input (key) unsigned char key; { pending_input_character = key; } /* How to see if there is any pending input. */ unsigned char info_input_pending_p () { return (pending_input_character); } /* Largest number of characters that we can read in advance. */ #define MAX_INFO_INPUT_BUFFERING 512 static int pop_index = 0, push_index = 0; static unsigned char info_input_buffer[MAX_INFO_INPUT_BUFFERING]; /* Add KEY to the buffer of characters to be read. */ static void info_push_typeahead (key) unsigned char key; { /* Flush all pending input in the case of C-g pressed. */ if (key == Control ('g')) { push_index = pop_index; info_set_pending_input (Control ('g')); } else { info_input_buffer[push_index++] = key; if (push_index >= sizeof (info_input_buffer)) - push_index = 0; + push_index = 0; } } /* Return the amount of space available in INFO_INPUT_BUFFER for new chars. */ static int info_input_buffer_space_available () { if (pop_index > push_index) return (pop_index - push_index); else return (sizeof (info_input_buffer) - (push_index - pop_index)); } /* Get a key from the buffer of characters to be read. Return the key in KEY. Result is non-zero if there was a key, or 0 if there wasn't. */ static int info_get_key_from_typeahead (key) unsigned char *key; { if (push_index == pop_index) return (0); *key = info_input_buffer[pop_index++]; if (pop_index >= sizeof (info_input_buffer)) pop_index = 0; return (1); } int info_any_buffered_input_p () { info_gather_typeahead (); return (push_index != pop_index); } -/* Push KEY into the *front* of the input buffer. Returns non-zero if - successful, zero if there is no space left in the buffer. */ -static int -info_replace_key_to_typeahead (key) - unsigned char key; -{ - if (info_input_buffer_space_available ()) - { - pop_index--; - if (pop_index < 0) - pop_index = sizeof (info_input_buffer) - 1; - info_input_buffer[pop_index] = key; - return (1); - } - return (0); -} - /* If characters are available to be read, then read them and stuff them into info_input_buffer. Otherwise, do nothing. */ void info_gather_typeahead () { register int i = 0; int tty, space_avail; long chars_avail; unsigned char input[MAX_INFO_INPUT_BUFFERING]; tty = fileno (info_input_stream); chars_avail = 0; space_avail = info_input_buffer_space_available (); /* If we can just find out how many characters there are to read, do so. */ #if defined (FIONREAD) { ioctl (tty, FIONREAD, &chars_avail); if (chars_avail > space_avail) chars_avail = space_avail; if (chars_avail) - read (tty, &input[0], chars_avail); + chars_avail = read (tty, &input[0], chars_avail); } #else /* !FIONREAD */ # if defined (O_NDELAY) { int flags; flags = fcntl (tty, F_GETFL, 0); fcntl (tty, F_SETFL, (flags | O_NDELAY)); chars_avail = read (tty, &input[0], space_avail); fcntl (tty, F_SETFL, flags); if (chars_avail == -1) chars_avail = 0; } # endif /* O_NDELAY */ #endif /* !FIONREAD */ while (i < chars_avail) { info_push_typeahead (input[i]); i++; } } /* How to read a single character. */ unsigned char info_get_input_char () { unsigned char keystroke; info_gather_typeahead (); if (pending_input_character) { keystroke = pending_input_character; pending_input_character = 0; } else if (info_get_key_from_typeahead (&keystroke) == 0) { int rawkey; + unsigned char c; + int tty = fileno (info_input_stream); + + /* Using stream I/O causes FIONREAD etc to fail to work + so unless someone can find a portable way of finding + out how many characters are currently buffered, we + should stay with away from stream I/O. + --Egil Kvaleberg , January 1997. */ +#ifdef EINTR + /* Keep reading if we got EINTR, so that we don't just exit. + --Andreas Schwab , + 22 Dec 1997. */ + { + int n; + do + n = read (tty, &c, 1); + while (n == -1 && errno == EINTR); + rawkey = n == 1 ? c : EOF; + } +#else + rawkey = (read (tty, &c, 1) == 1) ? c : EOF; +#endif - rawkey = getc (info_input_stream); keystroke = rawkey; if (rawkey == EOF) - { - if (info_input_stream != stdin) - { - fclose (info_input_stream); - info_input_stream = stdin; - display_inhibited = 0; - display_update_display (windows); - display_cursor_at_point (active_window); - rawkey = getc (info_input_stream); - keystroke = rawkey; - } - - if (rawkey == EOF) - { - terminal_unprep_terminal (); - close_dribble_file (); - exit (0); - } - } + { + if (info_input_stream != stdin) + { + fclose (info_input_stream); + info_input_stream = stdin; + display_inhibited = 0; + display_update_display (windows); + display_cursor_at_point (active_window); + rawkey = (read (tty, &c, 1) == 1) ? c : EOF; + keystroke = rawkey; + } + + if (rawkey == EOF) + { + terminal_unprep_terminal (); + close_dribble_file (); + exit (0); + } + } } if (info_dribble_file) dribble (keystroke); - return (keystroke); + return keystroke; } diff --git a/contrib/texinfo/info/signals.c b/contrib/texinfo/info/signals.c index 4f48e89a661b..e5a427cb657f 100644 --- a/contrib/texinfo/info/signals.c +++ b/contrib/texinfo/info/signals.c @@ -1,185 +1,181 @@ /* signals.c -- Install and maintain Info signal handlers. */ /* This file is part of GNU Info, a program for reading online documentation stored in Info format. Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Written by Brian Fox (bfox@ai.mit.edu). */ #include "info.h" #include "signals.h" /* **************************************************************** */ -/* */ -/* Pretending That We Have POSIX Signals */ -/* */ +/* */ +/* Pretending That We Have POSIX Signals */ +/* */ /* **************************************************************** */ #if !defined (HAVE_SIGPROCMASK) && defined (HAVE_SIGSETMASK) /* Perform OPERATION on NEWSET, perhaps leaving information in OLDSET. */ static void sigprocmask (operation, newset, oldset) int operation, *newset, *oldset; { switch (operation) { case SIG_UNBLOCK: sigsetmask (sigblock (0) & ~(*newset)); break; case SIG_BLOCK: *oldset = sigblock (*newset); break; case SIG_SETMASK: sigsetmask (*newset); break; default: abort (); } } #endif /* !HAVE_SIGPROCMASK && HAVE_SIGSETMASK */ /* **************************************************************** */ -/* */ -/* Signal Handling for Info */ -/* */ +/* */ +/* Signal Handling for Info */ +/* */ /* **************************************************************** */ -typedef void SigHandlerType; -typedef SigHandlerType SigHandler (); +typedef RETSIGTYPE signal_handler (); -static SigHandlerType info_signal_handler (); -static SigHandler *old_TSTP, *old_TTOU, *old_TTIN; -static SigHandler *old_WINCH, *old_INT, *old_CONT; +static RETSIGTYPE info_signal_handler (); +static signal_handler *old_TSTP, *old_TTOU, *old_TTIN; +static signal_handler *old_WINCH, *old_INT; void initialize_info_signal_handler () { #if defined (SIGTSTP) - old_TSTP = (SigHandler *) signal (SIGTSTP, info_signal_handler); - old_TTOU = (SigHandler *) signal (SIGTTOU, info_signal_handler); - old_TTIN = (SigHandler *) signal (SIGTTIN, info_signal_handler); + old_TSTP = (signal_handler *) signal (SIGTSTP, info_signal_handler); + old_TTOU = (signal_handler *) signal (SIGTTOU, info_signal_handler); + old_TTIN = (signal_handler *) signal (SIGTTIN, info_signal_handler); #endif /* SIGTSTP */ #if defined (SIGWINCH) - old_WINCH = (SigHandler *) signal (SIGWINCH, info_signal_handler); -#if defined (SIGCONT) - old_CONT = (SigHandler *) signal (SIGCONT, info_signal_handler); -#endif /* SIGCONT */ -#endif /* SIGWINCH */ + old_WINCH = (signal_handler *) signal (SIGWINCH, info_signal_handler); +#endif #if defined (SIGINT) - old_INT = (SigHandler *) signal (SIGINT, info_signal_handler); + old_INT = (signal_handler *) signal (SIGINT, info_signal_handler); #endif } static void redisplay_after_signal () { terminal_clear_screen (); display_clear_display (the_display); window_mark_chain (windows, W_UpdateWindow); display_update_display (windows); display_cursor_at_point (active_window); fflush (stdout); } -static SigHandlerType +static RETSIGTYPE info_signal_handler (sig) int sig; { - SigHandler **old_signal_handler; + signal_handler **old_signal_handler; switch (sig) { #if defined (SIGTSTP) case SIGTSTP: case SIGTTOU: case SIGTTIN: #endif #if defined (SIGINT) case SIGINT: #endif { #if defined (SIGTSTP) - if (sig == SIGTSTP) - old_signal_handler = &old_TSTP; - if (sig == SIGTTOU) - old_signal_handler = &old_TTOU; - if (sig == SIGTTIN) - old_signal_handler = &old_TTIN; + if (sig == SIGTSTP) + old_signal_handler = &old_TSTP; + if (sig == SIGTTOU) + old_signal_handler = &old_TTOU; + if (sig == SIGTTIN) + old_signal_handler = &old_TTIN; #endif /* SIGTSTP */ - if (sig == SIGINT) - old_signal_handler = &old_INT; - - /* For stop signals, restore the terminal IO, leave the cursor - at the bottom of the window, and stop us. */ - terminal_goto_xy (0, screenheight - 1); - terminal_clear_to_eol (); - fflush (stdout); - terminal_unprep_terminal (); - signal (sig, *old_signal_handler); - UNBLOCK_SIGNAL (sig); - kill (getpid (), sig); - - /* The program is returning now. Restore our signal handler, - turn on terminal handling, redraw the screen, and place the - cursor where it belongs. */ - terminal_prep_terminal (); - *old_signal_handler = (SigHandler *) signal (sig, info_signal_handler); - redisplay_after_signal (); - fflush (stdout); + if (sig == SIGINT) + old_signal_handler = &old_INT; + + /* For stop signals, restore the terminal IO, leave the cursor + at the bottom of the window, and stop us. */ + terminal_goto_xy (0, screenheight - 1); + terminal_clear_to_eol (); + fflush (stdout); + terminal_unprep_terminal (); + signal (sig, *old_signal_handler); + UNBLOCK_SIGNAL (sig); + kill (getpid (), sig); + + /* The program is returning now. Restore our signal handler, + turn on terminal handling, redraw the screen, and place the + cursor where it belongs. */ + terminal_prep_terminal (); + *old_signal_handler = (signal_handler *) signal (sig, info_signal_handler); + redisplay_after_signal (); + fflush (stdout); } break; #if defined (SIGWINCH) && defined(SIGCONT) case SIGCONT: if(old_CONT) (void)(old_CONT)(sig); /* pretend a SIGWINCH in case the terminal window size has changed while we've been asleep */ /* FALLTROUGH */ #endif /* defined (SIGWINCH) && defined(SIGCONT) */ #if defined (SIGWINCH) case SIGWINCH: { - /* Turn off terminal IO, tell our parent that the window has changed, - then reinitialize the terminal and rebuild our windows. */ - old_signal_handler = &old_WINCH; - terminal_goto_xy (0, 0); - fflush (stdout); - terminal_unprep_terminal (); - signal (sig, *old_signal_handler); - UNBLOCK_SIGNAL (sig); - kill (getpid (), sig); - - /* After our old signal handler returns... */ - terminal_get_screen_size (); - terminal_prep_terminal (); - display_initialize_display (screenwidth, screenheight); - window_new_screen_size (screenwidth, screenheight, (VFunction *)NULL); - *old_signal_handler = (SigHandler *) signal (sig, info_signal_handler); - redisplay_after_signal (); + /* Turn off terminal IO, tell our parent that the window has changed, + then reinitialize the terminal and rebuild our windows. */ + old_signal_handler = &old_WINCH; + terminal_goto_xy (0, 0); + fflush (stdout); + terminal_unprep_terminal (); + signal (sig, *old_signal_handler); + UNBLOCK_SIGNAL (sig); + kill (getpid (), sig); + + /* After our old signal handler returns... */ + terminal_get_screen_size (); + terminal_prep_terminal (); + display_initialize_display (screenwidth, screenheight); + window_new_screen_size (screenwidth, screenheight, (VFunction *)NULL); + *old_signal_handler = (signal_handler *) signal (sig, info_signal_handler); + redisplay_after_signal (); } break; #endif /* SIGWINCH */ } } diff --git a/contrib/texinfo/info/terminal.c b/contrib/texinfo/info/terminal.c index fcf5137089d0..8eb47c2cb78e 100644 --- a/contrib/texinfo/info/terminal.c +++ b/contrib/texinfo/info/terminal.c @@ -1,793 +1,842 @@ -/* terminal.c -- How to handle the physical terminal for Info. */ +/* terminal.c -- How to handle the physical terminal for Info. + $Id: terminal.c,v 1.9 1998/02/22 00:05:15 karl Exp $ -/* This file is part of GNU Info, a program for reading online documentation - stored in Info format. - - This file has appeared in prior works by the Free Software Foundation; - thus it carries copyright dates from 1988 through 1993. - - Copyright (C) 1988, 89, 90, 91, 92, 93, 96 Free Software Foundation, Inc. + Copyright (C) 1988, 89, 90, 91, 92, 93, 96, 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 the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Written by Brian Fox (bfox@ai.mit.edu). */ -#include -#include -#include +#include "info.h" #include "terminal.h" #include "termdep.h" -extern void *xmalloc (), *xrealloc (); +#include +#include /* The Unix termcap interface code. */ +#ifdef HAVE_NCURSES_TERMCAP_H +#include +#else +#ifdef HAVE_TERMCAP_H +#include +#else +/* On Solaris2, sys/types.h #includes sys/reg.h, which #defines PC. + Unfortunately, PC is a global variable used by the termcap library. */ +#undef PC +/* Termcap requires these variables, whether we access them or not. */ +char *BC, *UP; +char PC; /* Pad character */ +short ospeed; /* Terminal output baud rate */ extern int tgetnum (), tgetflag (), tgetent (); extern char *tgetstr (), *tgoto (); -extern char *getenv (); extern void tputs (); +#endif /* not HAVE_TERMCAP_H */ +#endif /* not HAVE_NCURSES_TERMCAP_H */ /* Function "hooks". If you make one of these point to a function, that function is called when appropriate instead of its namesake. Your function is called with exactly the same arguments that were passed to the namesake function. */ VFunction *terminal_begin_inverse_hook = (VFunction *)NULL; VFunction *terminal_end_inverse_hook = (VFunction *)NULL; VFunction *terminal_prep_terminal_hook = (VFunction *)NULL; VFunction *terminal_unprep_terminal_hook = (VFunction *)NULL; VFunction *terminal_up_line_hook = (VFunction *)NULL; VFunction *terminal_down_line_hook = (VFunction *)NULL; VFunction *terminal_clear_screen_hook = (VFunction *)NULL; VFunction *terminal_clear_to_eol_hook = (VFunction *)NULL; VFunction *terminal_get_screen_size_hook = (VFunction *)NULL; VFunction *terminal_goto_xy_hook = (VFunction *)NULL; VFunction *terminal_initialize_terminal_hook = (VFunction *)NULL; VFunction *terminal_new_terminal_hook = (VFunction *)NULL; VFunction *terminal_put_text_hook = (VFunction *)NULL; VFunction *terminal_ring_bell_hook = (VFunction *)NULL; VFunction *terminal_write_chars_hook = (VFunction *)NULL; VFunction *terminal_scroll_terminal_hook = (VFunction *)NULL; /* **************************************************************** */ -/* */ -/* Terminal and Termcap */ -/* */ +/* */ +/* Terminal and Termcap */ +/* */ /* **************************************************************** */ -/* On Solaris2, sys/types.h #includes sys/reg.h, which #defines PC. - Unfortunately, PC is a global variable used by the termcap library. */ -#undef PC - -/* TERMCAP requires these variables, whether we access them or not. */ -char PC; -char *BC, *UP; -short ospeed; - /* 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; /* Some strings to control terminal actions. These are output by tputs (). */ static char *term_goto, *term_clreol, *term_cr, *term_clrpag; static char *term_begin_use, *term_end_use; static char *term_AL, *term_DL, *term_al, *term_dl; +static char *term_keypad_on, *term_keypad_off; + /* How to go up a line. */ static char *term_up; /* How to go down a line. */ static char *term_dn; /* An audible bell, if the terminal can be made to make noise. */ static char *audible_bell; /* A visible bell, if the terminal can be made to flash the screen. */ static char *visible_bell; /* The string to write to turn on the meta key, if this term has one. */ static char *term_mm; /* The string to write to turn off the meta key, if this term has one. */ static char *term_mo; /* The string to turn on inverse mode, if this term has one. */ static char *term_invbeg; /* The string to turn off inverse mode, if this term has one. */ static char *term_invend; /* The string to turn on keypad transmit mode, if this term has one. */ static char *term_ks; /* The string to turn off keypad transmit mode, if this term has one. */ static char *term_ke; -static void +/* Although I can't find any documentation that says this is supposed to + return its argument, all the code I've looked at (termutils, less) + does so, so fine. */ +static int output_character_function (c) int c; { putc (c, stdout); + return c; } /* Macro to send STRING to the terminal. */ #define send_to_terminal(string) \ do { \ if (string) \ tputs (string, 1, output_character_function); \ } while (0) -/* Tell the terminal that we will be doing cursor addressable motion. */ +/* Tell the terminal that we will be doing cursor addressable motion. */ static void terminal_begin_using_terminal () { + RETSIGTYPE (*sigsave) (); + + if (term_keypad_on) + send_to_terminal (term_keypad_on); + + if (!term_begin_use || !*term_begin_use) + return; + +#ifdef SIGWINCH + sigsave = signal (SIGWINCH, SIG_IGN); +#endif + send_to_terminal (term_begin_use); - if (term_ks) - send_to_terminal(term_ks); + /* 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); + +#ifdef SIGWINCH + signal (SIGWINCH, sigsave); +#endif } -/* Tell the terminal that we will not be doing any more cursor addressable - motion. */ +/* Tell the terminal that we will not be doing any more cursor + addressable motion. */ static void terminal_end_using_terminal () { - if (term_ke) - send_to_terminal(term_ke); + RETSIGTYPE (*sigsave) (); + + if (term_keypad_off) + send_to_terminal (term_keypad_off); + + if (!term_end_use || !*term_end_use) + return; + +#ifdef SIGWINCH + sigsave = signal (SIGWINCH, SIG_IGN); +#endif + send_to_terminal (term_end_use); + fflush (stdout); + sleep (1); + +#ifdef SIGWINCH + signal (SIGWINCH, sigsave); +#endif } /* **************************************************************** */ -/* */ -/* Necessary Terminal Functions */ -/* */ +/* */ +/* Necessary Terminal Functions */ +/* */ /* **************************************************************** */ /* The functions and variables on this page implement the user visible portion of the terminal interface. */ /* The width and height of the terminal. */ int screenwidth, screenheight; /* Non-zero means this terminal can't really do anything. */ int terminal_is_dumb_p = 0; /* Non-zero means that this terminal has a meta key. */ int terminal_has_meta_p = 0; /* Non-zero means that this terminal can produce a visible bell. */ int terminal_has_visible_bell_p = 0; /* Non-zero means to use that visible bell if at all possible. */ int terminal_use_visible_bell_p = 0; /* Non-zero means that the terminal can do scrolling. */ int terminal_can_scroll = 0; /* The key sequences output by the arrow keys, if this terminal has any. */ char *term_ku = (char *)NULL; char *term_kd = (char *)NULL; char *term_kr = (char *)NULL; char *term_kl = (char *)NULL; -char *term_kP = (char *)NULL; -char *term_kN = (char *)NULL; -char *term_kh = (char *)NULL; -char *term_kH = (char *)NULL; - +char *term_kP = (char *)NULL; /* page-up */ +char *term_kN = (char *)NULL; /* page-down */ /* Move the cursor to the terminal location of X and Y. */ void terminal_goto_xy (x, y) int x, y; { if (terminal_goto_xy_hook) (*terminal_goto_xy_hook) (x, y); else { if (term_goto) - tputs (tgoto (term_goto, x, y), 1, output_character_function); + tputs (tgoto (term_goto, x, y), 1, output_character_function); } } /* Print STRING to the terminal at the current position. */ void terminal_put_text (string) char *string; { if (terminal_put_text_hook) (*terminal_put_text_hook) (string); else { printf ("%s", string); } } /* Print NCHARS from STRING to the terminal at the current position. */ void terminal_write_chars (string, nchars) char *string; int nchars; { if (terminal_write_chars_hook) (*terminal_write_chars_hook) (string, nchars); else { if (nchars) - fwrite (string, 1, nchars, stdout); + fwrite (string, 1, nchars, stdout); } } /* Clear from the current position of the cursor to the end of the line. */ void terminal_clear_to_eol () { if (terminal_clear_to_eol_hook) (*terminal_clear_to_eol_hook) (); else { send_to_terminal (term_clreol); } } /* Clear the entire terminal screen. */ void terminal_clear_screen () { if (terminal_clear_screen_hook) (*terminal_clear_screen_hook) (); else { send_to_terminal (term_clrpag); } } /* Move the cursor up one line. */ void terminal_up_line () { if (terminal_up_line_hook) (*terminal_up_line_hook) (); else { send_to_terminal (term_up); } } /* Move the cursor down one line. */ void terminal_down_line () { if (terminal_down_line_hook) (*terminal_down_line_hook) (); else { send_to_terminal (term_dn); } } /* Turn on reverse video if possible. */ void terminal_begin_inverse () { if (terminal_begin_inverse_hook) (*terminal_begin_inverse_hook) (); else { send_to_terminal (term_invbeg); } } /* Turn off reverse video if possible. */ void terminal_end_inverse () { if (terminal_end_inverse_hook) (*terminal_end_inverse_hook) (); else { send_to_terminal (term_invend); } } /* Ring the terminal bell. The bell is run visibly if it both has one and terminal_use_visible_bell_p is non-zero. */ void terminal_ring_bell () { if (terminal_ring_bell_hook) (*terminal_ring_bell_hook) (); else { if (terminal_has_visible_bell_p && terminal_use_visible_bell_p) - send_to_terminal (visible_bell); + send_to_terminal (visible_bell); else - send_to_terminal (audible_bell); + send_to_terminal (audible_bell); } } /* At the line START, delete COUNT lines from the terminal display. */ static void terminal_delete_lines (start, count) int start, count; { int lines; /* Normalize arguments. */ if (start < 0) start = 0; lines = screenheight - start; terminal_goto_xy (0, start); if (term_DL) tputs (tgoto (term_DL, 0, count), lines, output_character_function); else { while (count--) - tputs (term_dl, lines, output_character_function); + tputs (term_dl, lines, output_character_function); } fflush (stdout); } /* At the line START, insert COUNT lines in the terminal display. */ static void terminal_insert_lines (start, count) int start, count; { int lines; /* Normalize arguments. */ if (start < 0) start = 0; lines = screenheight - start; terminal_goto_xy (0, start); if (term_AL) tputs (tgoto (term_AL, 0, count), lines, output_character_function); else { while (count--) - tputs (term_al, lines, output_character_function); + tputs (term_al, lines, output_character_function); } fflush (stdout); } /* Scroll an area of the terminal, starting with the region from START to END, AMOUNT lines. If AMOUNT is negative, the lines are scrolled towards the top of the screen, else they are scrolled towards the bottom of the screen. */ void terminal_scroll_terminal (start, end, amount) int start, end, amount; { if (!terminal_can_scroll) return; /* Any scrolling at all? */ if (amount == 0) return; if (terminal_scroll_terminal_hook) (*terminal_scroll_terminal_hook) (start, end, amount); else { /* If we are scrolling down, delete AMOUNT lines at END. Then insert - AMOUNT lines at START. */ + AMOUNT lines at START. */ if (amount > 0) - { - terminal_delete_lines (end, amount); - terminal_insert_lines (start, amount); - } + { + terminal_delete_lines (end, amount); + terminal_insert_lines (start, amount); + } /* If we are scrolling up, delete AMOUNT lines before START. This - actually does the upwards scroll. Then, insert AMOUNT lines - after the already scrolled region (i.e., END - AMOUNT). */ + actually does the upwards scroll. Then, insert AMOUNT lines + after the already scrolled region (i.e., END - AMOUNT). */ if (amount < 0) - { - int abs_amount = -amount; - terminal_delete_lines (start - abs_amount, abs_amount); - terminal_insert_lines (end - abs_amount, abs_amount); - } + { + int abs_amount = -amount; + terminal_delete_lines (start - abs_amount, abs_amount); + terminal_insert_lines (end - abs_amount, abs_amount); + } } } /* Re-initialize the terminal considering that the TERM/TERMCAP variable has changed. */ void terminal_new_terminal (terminal_name) char *terminal_name; { if (terminal_new_terminal_hook) (*terminal_new_terminal_hook) (terminal_name); else { terminal_initialize_terminal (terminal_name); } } /* Set the global variables SCREENWIDTH and SCREENHEIGHT. */ void terminal_get_screen_size () { if (terminal_get_screen_size_hook) (*terminal_get_screen_size_hook) (); else { screenwidth = screenheight = 0; #if defined (TIOCGWINSZ) { - struct winsize window_size; + struct winsize window_size; - if (ioctl (fileno (stdout), TIOCGWINSZ, &window_size) == 0) - { - screenwidth = (int) window_size.ws_col; - screenheight = (int) window_size.ws_row; - } + if (ioctl (fileno (stdout), TIOCGWINSZ, &window_size) == 0) + { + screenwidth = (int) window_size.ws_col; + screenheight = (int) window_size.ws_row; + } } -#endif /* TIOCGWINSZ */ +#endif /* TIOCGWINSZ */ /* Environment variable COLUMNS overrides setting of "co". */ if (screenwidth <= 0) - { - char *sw = getenv ("COLUMNS"); + { + char *sw = getenv ("COLUMNS"); - if (sw) - screenwidth = atoi (sw); + if (sw) + screenwidth = atoi (sw); - if (screenwidth <= 0) - screenwidth = tgetnum ("co"); - } + if (screenwidth <= 0) + screenwidth = tgetnum ("co"); + } /* Environment variable LINES overrides setting of "li". */ if (screenheight <= 0) - { - char *sh = getenv ("LINES"); + { + char *sh = getenv ("LINES"); - if (sh) - screenheight = atoi (sh); + if (sh) + screenheight = atoi (sh); - if (screenheight <= 0) - screenheight = tgetnum ("li"); - } + if (screenheight <= 0) + screenheight = tgetnum ("li"); + } /* If all else fails, default to 80x24 terminal. */ if (screenwidth <= 0) - screenwidth = 80; + screenwidth = 80; if (screenheight <= 0) - screenheight = 24; + screenheight = 24; } } -/* Initialize the terminal which is known as TERMINAL_NAME. If this terminal - doesn't have cursor addressability, TERMINAL_IS_DUMB_P becomes non-zero. - The variables SCREENHEIGHT and SCREENWIDTH are set to the dimensions that - this terminal actually has. The variable TERMINAL_HAS_META_P becomes non- - zero if this terminal supports a Meta key. Finally, the terminal screen is - cleared. */ +/* Initialize the terminal which is known as TERMINAL_NAME. If this + terminal doesn't have cursor addressability, `terminal_is_dumb_p' + becomes nonzero. The variables SCREENHEIGHT and SCREENWIDTH are set + to the dimensions that this terminal actually has. The variable + TERMINAL_HAS_META_P becomes nonzero if this terminal supports a Meta + key. Finally, the terminal screen is cleared. */ void terminal_initialize_terminal (terminal_name) char *terminal_name; { char *term, *buffer; terminal_is_dumb_p = 0; if (terminal_initialize_terminal_hook) { (*terminal_initialize_terminal_hook) (terminal_name); return; } term = terminal_name ? terminal_name : getenv ("TERM"); if (!term_string_buffer) term_string_buffer = (char *)xmalloc (2048); if (!term_buffer) term_buffer = (char *)xmalloc (2048); buffer = term_string_buffer; term_clrpag = term_cr = term_clreol = (char *)NULL; if (!term) term = "dumb"; if (tgetent (term_buffer, term) <= 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 = term_kh = term_kH = (char *)NULL; + term_kP = term_kN = (char *)NULL; return; } BC = tgetstr ("pc", &buffer); PC = BC ? *BC : 0; #if defined (TIOCGETP) { struct sgttyb sg; if (ioctl (fileno (stdout), TIOCGETP, &sg) != -1) ospeed = sg.sg_ospeed; else ospeed = B9600; } #else ospeed = B9600; -#endif /* !TIOCGETP */ +#endif /* !TIOCGETP */ 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. */ term_AL = tgetstr ("AL", &buffer); term_DL = tgetstr ("DL", &buffer); term_al = tgetstr ("al", &buffer); term_dl = tgetstr ("dl", &buffer); terminal_can_scroll = ((term_AL || term_al) && (term_DL || term_dl)); term_invbeg = tgetstr ("mr", &buffer); if (term_invbeg) term_invend = tgetstr ("me", &buffer); else term_invend = (char *)NULL; if (!term_cr) term_cr = "\r"; terminal_get_screen_size (); term_up = tgetstr ("up", &buffer); term_dn = tgetstr ("dn", &buffer); visible_bell = tgetstr ("vb", &buffer); terminal_has_visible_bell_p = (visible_bell != (char *)NULL); audible_bell = tgetstr ("bl", &buffer); if (!audible_bell) audible_bell = "\007"; term_begin_use = tgetstr ("ti", &buffer); term_end_use = tgetstr ("te", &buffer); + term_keypad_on = tgetstr ("ks", &buffer); + term_keypad_off = tgetstr ("ke", &buffer); + /* Check to see if this terminal has a meta key. */ terminal_has_meta_p = (tgetflag ("km") || tgetflag ("MT")); if (terminal_has_meta_p) { term_mm = tgetstr ("mm", &buffer); term_mo = tgetstr ("mo", &buffer); } else { term_mm = (char *)NULL; term_mo = (char *)NULL; } /* Attempt to find the arrow keys. */ term_ku = tgetstr ("ku", &buffer); term_kd = tgetstr ("kd", &buffer); term_kr = tgetstr ("kr", &buffer); term_kl = tgetstr ("kl", &buffer); + term_kP = tgetstr ("kP", &buffer); term_kN = tgetstr ("kN", &buffer); - term_kh = tgetstr ("kh", &buffer); - term_kH = tgetstr ("kH", &buffer); - - /* Enable keypad and cursor keys if ks defined */ - term_ks = tgetstr ("ks", &buffer); - term_ke = tgetstr ("ke", &buffer); /* If this terminal is not cursor addressable, then it is really dumb. */ if (!term_goto) terminal_is_dumb_p = 1; - - terminal_begin_using_terminal (); } /* **************************************************************** */ -/* */ -/* How to Read Characters From the Terminal */ -/* */ +/* */ +/* 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 #if defined (HAVE_TERMIOS_H) struct termios original_termios, ttybuff; #else # if defined (HAVE_TERMIO_H) /* A buffer containing the terminal mode flags upon entry to info. */ struct termio original_termio, ttybuff; # else /* !HAVE_TERMIO_H */ /* Buffers containing the terminal mode flags upon entry to info. */ int original_tty_flags = 0; int original_lmode; struct sgttyb ttybuff; # endif /* !HAVE_TERMIO_H */ #endif /* !HAVE_TERMIOS_H */ /* Prepare to start using the terminal to read characters singly. */ void terminal_prep_terminal () { int tty; if (terminal_prep_terminal_hook) { (*terminal_prep_terminal_hook) (); return; } + terminal_begin_using_terminal (); + tty = fileno (stdin); #if defined (HAVE_TERMIOS_H) tcgetattr (tty, &original_termios); tcgetattr (tty, &ttybuff); #else # if defined (HAVE_TERMIO_H) ioctl (tty, TCGETA, &original_termio); ioctl (tty, TCGETA, &ttybuff); # endif #endif #if defined (HAVE_TERMIOS_H) || defined (HAVE_TERMIO_H) ttybuff.c_iflag &= (~ISTRIP & ~INLCR & ~IGNCR & ~ICRNL & ~IXON); - ttybuff.c_oflag &= (~ONLCR); +/* These output flags are not part of POSIX, so only use them if they + are defined. */ +#ifdef ONLCR + ttybuff.c_oflag &= ~ONLCR ; +#endif +#ifdef OCRNL + ttybuff.c_oflag &= ~OCRNL; +#endif ttybuff.c_lflag &= (~ICANON & ~ECHO); ttybuff.c_cc[VMIN] = 1; ttybuff.c_cc[VTIME] = 0; if (ttybuff.c_cc[VINTR] == '\177') ttybuff.c_cc[VINTR] = -1; if (ttybuff.c_cc[VQUIT] == '\177') ttybuff.c_cc[VQUIT] = -1; -#endif + +#ifdef VLNEXT + if (ttybuff.c_cc[VLNEXT] == '\026') + ttybuff.c_cc[VLNEXT] = -1; +#endif /* VLNEXT */ +#endif /* TERMIOS or TERMIO */ #if defined (HAVE_TERMIOS_H) tcsetattr (tty, TCSANOW, &ttybuff); #else # if defined (HAVE_TERMIO_H) ioctl (tty, TCSETA, &ttybuff); # endif #endif #if !defined (HAVE_TERMIOS_H) && !defined (HAVE_TERMIO_H) ioctl (tty, TIOCGETP, &ttybuff); if (!original_tty_flags) original_tty_flags = ttybuff.sg_flags; /* Make this terminal pass 8 bits around while we are using it. */ # if defined (PASS8) ttybuff.sg_flags |= PASS8; # endif /* PASS8 */ # if defined (TIOCLGET) && defined (LPASS8) { int flags; ioctl (tty, TIOCLGET, &flags); original_lmode = flags; flags |= LPASS8; ioctl (tty, TIOCLSET, &flags); } # endif /* TIOCLGET && LPASS8 */ # if defined (TIOCGETC) { struct tchars temp; ioctl (tty, TIOCGETC, &original_tchars); temp = original_tchars; /* C-s and C-q. */ temp.t_startc = temp.t_stopc = -1; /* Often set to C-d. */ temp.t_eofc = -1; /* If the a quit or interrupt character conflicts with one of our commands, then make it go away. */ if (temp.t_intrc == '\177') temp.t_intrc = -1; if (temp.t_quitc == '\177') temp.t_quitc = -1; ioctl (tty, TIOCSETC, &temp); } # endif /* TIOCGETC */ # if defined (TIOCGLTC) { struct ltchars temp; ioctl (tty, TIOCGLTC, &original_ltchars); temp = original_ltchars; /* Make the interrupt keys go away. Just enough to make people happy. */ - temp.t_lnextc = -1; /* C-v. */ - temp.t_dsuspc = -1; /* C-y. */ - temp.t_flushc = -1; /* C-o. */ + temp.t_lnextc = -1; /* C-v. */ + temp.t_dsuspc = -1; /* C-y. */ + temp.t_flushc = -1; /* C-o. */ ioctl (tty, TIOCSLTC, &temp); } # endif /* TIOCGLTC */ ttybuff.sg_flags &= ~ECHO; ttybuff.sg_flags |= CBREAK; ioctl (tty, TIOCSETN, &ttybuff); #endif /* !HAVE_TERMIOS_H && !HAVE_TERMIO_H */ } /* Restore the tty settings back to what they were before we started using this terminal. */ void terminal_unprep_terminal () { int tty; if (terminal_unprep_terminal_hook) { (*terminal_unprep_terminal_hook) (); return; } tty = fileno (stdin); #if defined (HAVE_TERMIOS_H) tcsetattr (tty, TCSANOW, &original_termios); #else # if defined (HAVE_TERMIO_H) ioctl (tty, TCSETA, &original_termio); # else /* !HAVE_TERMIO_H */ ioctl (tty, TIOCGETP, &ttybuff); ttybuff.sg_flags = original_tty_flags; ioctl (tty, TIOCSETN, &ttybuff); # if defined (TIOCGETC) ioctl (tty, TIOCSETC, &original_tchars); # endif /* TIOCGETC */ # if defined (TIOCGLTC) ioctl (tty, TIOCSLTC, &original_ltchars); # endif /* TIOCGLTC */ # if defined (TIOCLGET) && defined (LPASS8) ioctl (tty, TIOCLSET, &original_lmode); # endif /* TIOCLGET && LPASS8 */ # endif /* !HAVE_TERMIO_H */ #endif /* !HAVE_TERMIOS_H */ terminal_end_using_terminal (); } diff --git a/contrib/texinfo/makeinfo/makeinfo.c b/contrib/texinfo/makeinfo/makeinfo.c index f30d366cbd48..ef892db00305 100644 --- a/contrib/texinfo/makeinfo/makeinfo.c +++ b/contrib/texinfo/makeinfo/makeinfo.c @@ -1,9343 +1,9629 @@ -/* Makeinfo -- convert texinfo format files into info files. - $Id: makeinfo.c,v 1.37 1996/10/04 18:20:52 karl Exp $ +/* Makeinfo -- convert Texinfo source files into Info files. + $Id: makeinfo.c,v 1.60 1998/02/25 20:36:22 karl Exp $ - Copyright (C) 1987, 92, 93, 94, 95, 96 Free Software Foundation, Inc. + Copyright (C) 1987, 92, 93, 94, 95, 96, 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 the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - Makeinfo is authored by Brian Fox (bfox@ai.mit.edu). */ + 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 = 67; +int minor_version = 68; + +#include "system.h" +#include "getopt.h" + +#ifdef TM_IN_SYS_TIME +#include +#else +#include +#endif /* !TM_IN_SYS_TIME */ -/* You can change some of the behaviour of Makeinfo by changing the +#ifdef __GNUC__ +# undef alloca +# define alloca __builtin_alloca +#else +# ifdef HAVE_ALLOCA_H +# include +# else +# ifndef _AIX +char *alloca (); +# endif +# endif +#endif + +/* We'd like to take advantage of _doprnt if it's around, a la error.c, + but then we'd have no VA_SPRINTF. */ +#if HAVE_VPRINTF +# if __STDC__ +# include +# define VA_START(args, lastarg) va_start(args, lastarg) +# else +# include +# define VA_START(args, lastarg) va_start(args) +# endif +# define VA_FPRINTF(file, fmt, ap) vfprintf (file, fmt, ap) +# define VA_SPRINTF(str, fmt, ap) vsprintf (str, fmt, ap) +#else /* not HAVE_VPRINTF */ +# define VA_START(args, lastarg) +# define va_alist a1, a2, a3, a4, a5, a6, a7, a8 +# define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8; +# define va_end(args) +#endif + +/* You can change some of the behavior of Makeinfo by changing the following defines: */ /* Define INDENT_PARAGRAPHS_IN_TABLE if you want the paragraphs which appear within an @table, @ftable, or @itemize environment to have standard paragraph indentation. Without this, such paragraphs have 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 line, with the --paragraph-indent option, or within the texinfo file, with the @paragraphindent command. */ #define PARAGRAPH_START_INDENT 3 /* Define DEFAULT_PARAGRAPH_SPACING as the number of blank lines that you wish to appear between paragraphs. A value of 1 creates a single blank 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. */ + facility, users can create their own command procedures with + arguments. Must always be defined. */ #define HAVE_MACROS -/* Indent #pragma so that older Cpp's don't try to parse it. */ -#if defined (_AIX) - # pragma alloca -#endif /* _AIX */ - -#include -#include -#include -#include -#include -#include - -#if defined (HAVE_VARARGS_H) -#include -#endif /* HAVE_VARARGS_H */ -#include "getopt.h" - -#if defined (HAVE_UNISTD_H) -#include -#endif /* HAVE_UNISTD_H */ - -#if defined (VMS) -#include -#endif - -#if defined (HAVE_STRING_H) -#include -#else -#include -#endif /* !HAVE_STRING_H */ - -#if defined (TM_IN_SYS_TIME) -#include -#else -#include -#endif /* !TM_IN_SYS_TIME */ - -#if defined (HAVE_SYS_FCNTL_H) -#include -#else -#include -#endif /* !HAVE_SYS_FCNTL_H */ - -#if defined (HAVE_SYS_FILE_H) -#include -#endif /* HAVE_SYS_FILE_H */ - -#if defined (__GNUC__) -#define alloca __builtin_alloca -#else -#if defined(HAVE_ALLOCA_H) -#include -#else /* !HAVE_ALLOCA_H */ -#if !defined (_AIX) -extern char *alloca (); -#endif /* !_AIX */ -#endif /* !HAVE_ALLOCA_H */ -#endif /* !__GNUC__ */ - -void *xmalloc (), *xrealloc (); -#if defined (__osf__) -extern void *malloc (), *realloc (); -#endif /* __osf__ */ - -char **get_brace_args (); -int array_len (); -void free_array (); -static void isolate_nodename (); - #define COMPILING_MAKEINFO #include "makeinfo.h" -/* Non-zero means that we are currently hacking the insides of an +/* 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; -/* Non-zero means that start_paragraph () MUST be called before we pay +/* Nonzero means that start_paragraph () MUST be called before we pay any attention to close_paragraph () calls. */ int must_start_paragraph = 0; -/* Non-zero means a string is in execution, as opposed to a file. */ +/* 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 resultant file is another texinfo file, but + 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. */ + 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; -/* Non-zero means to inhibit the writing of macro expansions to the output - stream. This is used in special cases where the output has already been - written. */ +/* 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 */ -/* Some systems don't declare this function in pwd.h. */ -struct passwd *getpwnam (); /* **************************************************************** */ -/* */ -/* Global Variables */ -/* */ +/* */ +/* Global Variables */ +/* */ /* **************************************************************** */ /* Global pointer to argv[0]. */ char *progname; -/* Return non-zero if STRING is the text at input_text + input_text_offset, +/* 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. */ /* The output file name. */ char *output_filename = (char *)NULL; char *pretty_output_filename; /* 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; /* 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; -/* Current output stream. */ -FILE *output_stream; - /* Position in the output file. */ int output_position; #define INITIAL_PARAGRAPH_SPACE 5000 int paragraph_buffer_len = INITIAL_PARAGRAPH_SPACE; -/* Filling.. */ -/* Non-zero indicates that filling will take place on long lines. */ +/* Nonzero indicates that filling will take place on long lines. */ int filling_enabled = 1; -/* Non-zero means that words are not to be split, even in long lines. This +/* Nonzero means that words are not to be split, even in long lines. This gets changed for cm_w (). */ int non_splitting_words = 0; -/* Non-zero indicates that filling a line also indents the new line. */ +/* 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. < 0 means indent to column zero by removing indentation if necessary. This is normally zero, but some people prefer paragraph starts to be somewhat more indented than paragraph bodies. A pretty value for this is 3. */ int paragraph_start_indent = PARAGRAPH_START_INDENT; -/* Non-zero means that the use of paragraph_start_indent is inhibited. +/* 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; -/* Non-zero indicates that indentation is temporarily turned off. */ +/* Nonzero indicates that indentation is temporarily turned off. */ int no_indent = 1; -/* Non-zero means forcing output text to be flushright. */ +/* Nonzero means forcing output text to be flushright. */ int force_flush_right = 0; -/* Non-zero means that the footnote style for this document was set on +/* 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; -/* Non-zero means that we automatically number footnotes that have no +/* 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' }; + { '\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' }; + { '\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' }; + '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; /* non-zero means this node has been referenced. */ - int flags; /* Room for growth. Right now, contains 1 bit. */ + 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 +#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 +#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; /* Non-zero means make find_macro () fail. */ - int flags; /* ME_RECURSE, ME_QUOTE_ARG, etc. */ + 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. */ + 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; -/* Non-zero means that we have seen "@top" once already. */ +/* Nonzero means that we have seen "@top" once already. */ int top_node_seen = 0; -/* Non-zero means that we have seen a non-"@top" node already. */ +/* 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. */ +int force = 0; + /* Default is to notify users of bad choices. */ int print_warnings = 1; /* Default is to check node references. */ int validating = 1; -/* Non-zero means do not output "Node: Foo" for node separations. */ +/* 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; -/* Non-zero means print out information about what is going on when it +/* Nonzero means print out information about what is going on when it is going on. */ int verbose_mode = 0; -/* Non-zero means to be relaxed about the input file. This is useful when +/* 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; /* 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 typedef struct brace_element { struct brace_element *next; COMMAND_FUNCTION *proc; int pos, line; int in_fixed_width_font; } BRACE_ELEMENT; BRACE_ELEMENT *brace_stack = (BRACE_ELEMENT *) NULL; -/* Forward declarations. */ -#if !defined (HAVE_STRDUP) -extern char *strdup (); -#endif /* HAVE_STRDUP */ - extern void do_multitable (); void print_version_info (); void usage (); void push_node_filename (), pop_node_filename (); -void remember_error (); +void remember_error (), flush_file_stack (); 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 handle_variable_internal (); -void execute_string (); void normalize_node_name (); void undefindex (), top_defindex (), gen_defindex (); void define_user_command (); void free_pending_notes (), output_pending_notes (); +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 remember_brace (), remember_brace_1 (); void pop_and_call_brace (), discard_braces (); -void add_word_args (), add_word (), add_char (), insert (), flush_output (); +void add_word (), add_char (), insert (), flush_output (); void insert_string (); void close_paragraph_with_lines (), close_paragraph (); void ignore_blank_line (); -void do_flush_right_indentation (); +void do_flush_right_indentation (), discard_insertions (); void start_paragraph (), indent (); +#if defined (VA_FPRINTF) && __STDC__ +/* Unfortunately we must use prototypes if we are to use . */ +void add_word_args (char *, ...); +void execute_string (char *, ...); +#else +void add_word_args (); +void execute_string (); +#endif /* will not use 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_angle_brackets (), cm_no_op (), cm_not_fixed_width (), - cm_strong (), cm_var (), cm_w (); + 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 @defxxx commands map to cm_defun, most accent commands map to +/* 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_quotation (), cm_display (), cm_itemize (), + 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_kindex (), cm_cindex (), + 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_sc (), cm_result (), cm_expansion (), + 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 CommandTable[] = { +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_angle_brackets, 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 }, - { "key", cm_angle_brackets, 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 }, -#if defined (HAVE_MACROS) { "macro", cm_macro, NO_BRACE_ARGS }, -#endif { "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_sc, 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, 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 supposed to. */ + /* Now @include does what this was used to. */ { "infoinclude", cm_obsolete, NO_BRACE_ARGS }, { "titlespec", cm_obsolete, NO_BRACE_ARGS }, - {(char *) NULL, (COMMAND_FUNCTION *) NULL}, 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 */ - { "no-headers", 0, &no_headers, 1 }, /* Do not output Node: foo */ + { "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 */ -#if defined (HAVE_MACROS) + { "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' }, -#endif /* HAVE_MACROS */ { "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 */ + { "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} }; -/* Values for calling handle_variable_internal (). */ -#define SET 1 -#define CLEAR 2 -#define IFSET 3 -#define IFCLEAR 4 +/* **************************************************************** */ +/* */ +/* Error Handling */ +/* */ +/* **************************************************************** */ + +/* Number of errors encountered. */ +int errors_printed = 0; + +/* Print the last error gotten from the file system. */ +int +fs_error (filename) + char *filename; +{ + remember_error (); + perror (filename); + return (0); +} + +/* Print an error message, and return false. */ +void +#if defined (VA_FPRINTF) && __STDC__ +error (char *format, ...) +#else +error (format, va_alist) + char *format; + va_dcl +#endif +{ +#ifdef VA_FPRINTF + va_list ap; +#endif + + remember_error (); + VA_START (ap, format); +#ifdef VA_FPRINTF + VA_FPRINTF (stderr, format, ap); +#else + fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8); +#endif /* not VA_FPRINTF */ + va_end (ap); + + putc ('\n', stderr); +} + +/* Just like error (), but print the line number as well. */ +void +#if defined (VA_FPRINTF) && __STDC__ +line_error (char *format, ...) +#else +line_error (format, va_alist) + char *format; + va_dcl +#endif +{ +#ifdef VA_FPRINTF + va_list ap; +#endif + + remember_error (); + fprintf (stderr, "%s:%d: ", input_filename, line_number); + + VA_START (ap, format); +#ifdef VA_FPRINTF + VA_FPRINTF (stderr, format, ap); +#else + fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8); +#endif /* not VA_FPRINTF */ + va_end (ap); + + fprintf (stderr, ".\n"); +} + +void +#if defined (VA_FPRINTF) && __STDC__ +warning (char *format, ...) +#else +warning (format, va_alist) + char *format; + va_dcl +#endif +{ +#ifdef VA_FPRINTF + va_list ap; +#endif + + if (print_warnings) + { + fprintf (stderr, _("%s:%d: warning: "), input_filename, line_number); + + VA_START (ap, format); +#ifdef VA_FPRINTF + VA_FPRINTF (stderr, format, ap); +#else + fprintf (stderr, format, a1, a2, a3, a4, a5, a6, a7, a8); +#endif /* not VA_FPRINTF */ + va_end (ap); + + fprintf (stderr, ".\n"); + } +} + + +/* Remember that an error has been printed. If more than + max_error_level have been printed, then exit the program. */ +void +remember_error () +{ + errors_printed++; + if (max_error_level && (errors_printed > max_error_level)) + { + fprintf (stderr, _("Too many errors! Gave up.\n")); + flush_file_stack (); + cm_bye (); + exit (FATAL); + } +} + /* **************************************************************** */ -/* */ -/* Main () Start of code */ -/* */ +/* */ +/* Main () Start of code */ +/* */ /* **************************************************************** */ /* For each file mentioned in the command line, process it, turning Texinfo commands into wonderfully formatted output text. */ int main (argc, argv) int argc; 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, ""); +#endif + + /* Set the text message domain. */ + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + /* Parse argument flags from the input line. */ - while ((c = getopt_long - (argc, argv, -#if defined (HAVE_MACROS) - "D:E:U:I:f:o:p:e:r:s:V", -#else - "D:U:I:f:o:p:e:r:s:V", -#endif /* !HAVE_MACROS */ - long_options, &ind)) - != EOF) + while ((c = getopt_long (argc, argv, "D:e:E:f:I:o:p:P:r:s:U:V", + long_options, &ind)) != EOF) { if (c == 0 && long_options[ind].flag == 0) - c = long_options[ind].val; + c = long_options[ind].val; switch (c) - { - /* User specified variable to set or clear? */ - case 'D': - case 'U': - handle_variable_internal ((c == 'D') ? SET : CLEAR, optarg); - break; - -#if defined (HAVE_MACROS) - /* Use specified a macro expansion output file? */ - case 'E': - if (!macro_expansion_output_stream) - { - macro_expansion_output_stream = fopen (optarg, "w"); - if (!macro_expansion_output_stream) - error ("Couldn't open macro expansion output \"%s\"", optarg); - } - else - error ("Cannot specify more than one macro expansion output"); - break; -#endif /* HAVE_MACROS */ - - /* User specified include file path? */ - case 'I': - if (!include_files_path) - include_files_path = strdup ("."); - - include_files_path = (char *) - xrealloc (include_files_path, - 2 + strlen (include_files_path) + strlen (optarg)); - strcat (include_files_path, ":"); - strcat (include_files_path, optarg); - break; - - /* User specified fill_column? */ - case 'f': - if (sscanf (optarg, "%d", &fill_column) != 1) - { - fprintf (stderr, - "%s: --fill-column arg must be numeric, not `%s'.\n", - progname, optarg); - usage (FATAL); - } - break; - - /* User specified output file? */ - case 'o': - command_output_filename = strdup (optarg); - break; - - /* User specified paragraph indent (paragraph_start_index)? */ - case 'p': - if (set_paragraph_indent (optarg) < 0) - { - fprintf (stderr, - "%s: --paragraph-indent arg must be numeric/none/asis, not `%s'.\n", - progname, optarg); - usage (FATAL); - } - break; - - /* User specified error level? */ - case 'e': - if (sscanf (optarg, "%d", &max_error_level) != 1) - { - fprintf (stderr, - "%s: --error-limit arg must be numeric, not `%s'.\n", - progname, optarg); - } - usage (stderr, FATAL); - break; - - /* User specified reference warning limit? */ - case 'r': - if (sscanf (optarg, "%d", &reference_warning_limit) != 1) - { - fprintf (stderr, - "%s: --reference-limit arg must be numeric, not `%s'.\n", + { + case 'D': + case 'U': + /* User specified variable to set or clear. */ + handle_variable_internal ((c == 'D') ? SET : CLEAR, optarg); + break; + + case 'e': + /* User specified error level. */ + 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); + } + break; + + case 'E': + /* User specified a macro expansion output file. */ + if (!macro_expansion_output_stream) + { + macro_expansion_filename = optarg; + macro_expansion_output_stream + = strcmp (optarg, "-") == 0 ? stdout : fopen (optarg, "w"); + if (!macro_expansion_output_stream) + error (_("Couldn't open macro expansion output `%s'"), optarg); + } + else + error (_("Cannot specify more than one macro expansion output")); + break; + + case 'f': + /* User specified fill_column. */ + if (sscanf (optarg, "%d", &fill_column) != 1) + { + fprintf (stderr, + _("%s: %s arg must be numeric, not `%s'.\n"), + "--fill-column", progname, optarg); + usage (FATAL); + } + break; + + case 'F': + force++; /* Do not remove erroneous output. */ + break; + + case 'h': + usage (NO_ERROR); + break; + + case 'I': + /* Append user-specified dir to include file path. */ + if (!include_files_path) + include_files_path = xstrdup ("."); + + include_files_path = (char *) + xrealloc (include_files_path, + 2 + strlen (include_files_path) + strlen (optarg)); + strcat (include_files_path, ":"); + strcat (include_files_path, optarg); + break; + + case 'o': + /* User specified output file. */ + command_output_filename = xstrdup (optarg); + break; + + case 'p': + /* User specified paragraph indent (paragraph_start_index). */ + if (set_paragraph_indent (optarg) < 0) + { + fprintf (stderr, + _("%s: --paragraph-indent arg must be numeric/`none'/`asis', not `%s'.\n"), progname, optarg); - usage (FATAL); - } - break; - - /* User specified footnote style? */ - case 's': - if (set_footnote_style (optarg) < 0) - { - fprintf (stderr, - "%s: --footnote-style arg must be `separate' or `end', not `%s'.\n", + usage (FATAL); + } + break; + + case 'P': + /* Prepend user-specified include dir to include path. */ + if (!include_files_path) + { + include_files_path = xstrdup (optarg); + include_files_path = (char *) xrealloc (include_files_path, + strlen (include_files_path) + 3); /* 3 for ":.\0" */ + strcat (include_files_path, ":."); + } + else + { + char *tmp = xstrdup (include_files_path); + include_files_path = (char *) xrealloc (include_files_path, + strlen (include_files_path) + strlen (optarg) + 2); /* 2 for ":\0" */ + strcpy (include_files_path, optarg); + strcat (include_files_path, ":"); + strcat (include_files_path, tmp); + free (tmp); + } + break; + + case 'r': + /* User specified reference warning limit. */ + if (sscanf (optarg, "%d", &reference_warning_limit) != 1) + { + fprintf (stderr, + _("%s: %s arg must be numeric, not `%s'.\n"), + "--reference-limit", progname, optarg); + usage (FATAL); + } + break; + + case 's': + /* User specified footnote style. */ + if (set_footnote_style (optarg) < 0) + { + fprintf (stderr, + _("%s: --footnote-style arg must be `separate' or `end', not `%s'.\n"), progname, optarg); - usage (FATAL); - } - footnote_style_preset = 1; - break; - - case 'h': - usage (NO_ERROR); - break; - - /* User requested version info? */ - case 'V': - print_version_info (); - puts ("Copyright (C) 1996 Free Software Foundation, Inc.\n\ + usage (FATAL); + } + footnote_style_preset = 1; + break; + + case 'V': + /* User requested version info. */ + print_version_info (); + 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."); - exit (NO_ERROR); - break; - - case '?': - usage (FATAL); - break; - } +For more information about these matters, see the files named COPYING.\n"), + "1998"); + exit (NO_ERROR); + break; + + case '?': + usage (FATAL); + break; + } } if (optind == argc) { /* Check to see if input is a file. If so, process that. */ if (!isatty (fileno (stdin))) - reading_from_stdin = 1; + reading_from_stdin = 1; else { - fprintf (stderr, "%s: missing file argument.\n", progname); - usage (FATAL); + fprintf (stderr, _("%s: missing file argument.\n"), progname); + usage (FATAL); } } /* 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) { splitting = 0; - /* If the user has not specified an output file, then use stdout by - default. */ + /* If the user has not specified an output file, use stdout. */ if (!command_output_filename) - command_output_filename = strdup ("-"); + command_output_filename = xstrdup ("-"); } if (verbose_mode) print_version_info (); /* Remaining arguments are file names of texinfo files. Convert them, one by one. */ if (!reading_from_stdin) { while (optind != argc) - convert_from_file (argv[optind++]); + convert_from_file (argv[optind++]); } else convert_from_stream (stdin, "stdin"); if (errors_printed) return (SYNTAX); else return (NO_ERROR); } /* Display the version info of this invocation of Makeinfo. */ void print_version_info () { - printf ("GNU Makeinfo (Texinfo 3.9) %d.%d\n", major_version, minor_version); + printf ("makeinfo (GNU %s %s) %d.%d\n", PACKAGE, VERSION, + major_version, minor_version); } /* **************************************************************** */ /* */ /* Generic Utilities */ /* */ /* **************************************************************** */ static void memory_error (callers_name, bytes_wanted) char *callers_name; int bytes_wanted; { char printable_string[80]; sprintf (printable_string, "Virtual memory exhausted in %s ()! Needed %d bytes.", callers_name, bytes_wanted); error (printable_string); abort (); } /* Just like malloc, but kills the program in case of fatal error. */ void * xmalloc (nbytes) unsigned int nbytes; { void *temp = (void *) malloc (nbytes); if (nbytes && temp == (void *)NULL) memory_error ("xmalloc", nbytes); return (temp); } /* Like realloc (), but barfs if there isn't enough memory. */ void * xrealloc (pointer, nbytes) void *pointer; unsigned int nbytes; { void *temp; if (!pointer) temp = (void *)xmalloc (nbytes); else temp = (void *)realloc (pointer, nbytes); - if (nbytes && !temp) - memory_error ("xrealloc", nbytes); - - return (temp); -} - /* 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); + fprintf (stderr, _("Try `%s --help' for more information.\n"), progname); else - printf ("Usage: %s [OPTION]... TEXINFO-FILE...\n\ + 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 add DIR to the directory search list for @include.\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 NUM indent paragraphs with NUM spaces (default %d).\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@prep.ai.mit.edu.\n\ -", - progname, paragraph_start_indent, - fill_column, max_error_level, reference_warning_limit); +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, n, i, count = 0; + 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. */ - result = (char *)xmalloc (1 + file_size); + /* 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) +#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 */ +#else /* !VMS && !WIN32 */ count = file_size; if (read (file, result, file_size) != file_size) -#endif /* !VMS */ +#endif /* !VMS && !WIN32 */ error_exit: { if (result) - free (result); + free (result); if (fullpath) - free (fullpath); + free (fullpath); if (file != -1) - close (file); + 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 = strdup (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). - The SIZE_OF_INPUT_TEXT is one past the actual end of the text. */ + 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? */ + 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) + 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 = strdup (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'; + *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'; + 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 (strdup (name + i + 1)); + return (xstrdup (name + i + 1)); - return (strdup (name)); + 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 = strdup ("noname.texi"); - } + { + free (filename); + filename = xstrdup ("noname.texi"); + } for (i = strlen (filename) - 1; i; i--) - if (filename[i] == '.') - break; + if (filename[i] == '.') + break; if (!i) - i = strlen (filename); + i = strlen (filename); if (i + 6 > (strlen (filename))) - filename = (char *)xrealloc (filename, i + 6); + 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--; + i--; if (result[i] == '/') - 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 (strdup ("")); + return (xstrdup ("")); /* Already absolute? */ if ((initial_character == '/') || ((strncmp (filename, "./", 2) == 0) || (strncmp (filename, "../", 3) == 0))) - return (strdup (filename)); + 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)) + if (!getwd (localdir)) #endif /* !HAVE_GETCWD */ - { - fprintf (stderr, "%s: getwd: %s, %s\n", - progname, filename, localdir); - exit (1); - } + { + fprintf (stderr, _("%s: getwd: %s, %s\n"), + progname, filename, localdir); + exit (1); + } strcat (localdir, "/"); strcat (localdir, filename); - result = strdup (localdir); + 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]); - } + { + /* 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 (strdup (filename)); - - result = (char *)xmalloc (1 + strlen (user_entry->pw_dir) - + strlen (&filename[i])); - strcpy (result, user_entry->pw_dir); - strcat (result, &filename[i]); - } + { + 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)); } /* **************************************************************** */ -/* */ -/* Error Handling */ -/* */ -/* **************************************************************** */ - -/* Number of errors encountered. */ -int errors_printed = 0; - -/* Print the last error gotten from the file system. */ -int -fs_error (filename) - char *filename; -{ - remember_error (); - perror (filename); - return (0); -} - -/* Print an error message, and return false. */ -#if defined (HAVE_VARARGS_H) && defined (HAVE_VFPRINTF) - -int -error (va_alist) - va_dcl -{ - char *format; - va_list args; - - remember_error (); - va_start (args); - format = va_arg (args, char *); - vfprintf (stderr, format, args); - va_end (args); - putc ('\n', stderr); -} - -/* Just like error (), but print the line number as well. */ -int -line_error (va_alist) - va_dcl -{ - char *format; - va_list args; - - remember_error (); - va_start (args); - format = va_arg (args, char *); - fprintf (stderr, "%s:%d: ", input_filename, line_number); - vfprintf (stderr, format, args); - fprintf (stderr, ".\n"); - va_end (args); - return ((int) 0); -} - -int -warning (va_alist) - va_dcl -{ - char *format; - va_list args; - - va_start (args); - format = va_arg (args, char *); - if (print_warnings) - { - fprintf (stderr, "%s:%d: Warning: ", input_filename, line_number); - vfprintf (stderr, format, args); - fprintf (stderr, ".\n"); - } - va_end (args); - return ((int) 0); -} - -#else /* !(HAVE_VARARGS_H && HAVE_VFPRINTF) */ - -int -error (format, arg1, arg2, arg3, arg4, arg5) - char *format; -{ - remember_error (); - fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5); - putc ('\n', stderr); - return ((int) 0); -} - -/* Just like error (), but print the line number as well. */ -int -line_error (format, arg1, arg2, arg3, arg4, arg5) - char *format; -{ - remember_error (); - fprintf (stderr, "%s:%d: ", input_filename, line_number); - fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5); - fprintf (stderr, ".\n"); - return ((int) 0); -} - -int -warning (format, arg1, arg2, arg3, arg4, arg5) - char *format; -{ - if (print_warnings) - { - fprintf (stderr, "%s:%d: Warning: ", input_filename, line_number); - fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5); - fprintf (stderr, ".\n"); - } - return ((int) 0); -} - -#endif /* !(HAVE_VARARGS_H && HAVE_VFPRINTF) */ - -/* Remember that an error has been printed. If this is the first - error printed, then tell them which program is printing them. - If more than max_error_level have been printed, then exit the - program. */ -void -remember_error () -{ - errors_printed++; - if (max_error_level && (errors_printed > max_error_level)) - { - fprintf (stderr, "Too many errors! Gave up.\n"); - flush_file_stack (); - cm_bye (); - exit (1); - } -} - -/* **************************************************************** */ -/* */ -/* Hacking Tokens and Strings */ -/* */ +/* */ +/* Hacking Tokens and Strings */ +/* */ /* **************************************************************** */ /* Return the next token as a string pointer. We cons the string. */ char * read_token () { int i, character; char *result; /* If the first character to be read is self-delimiting, then that is the command itself. */ character = curchar (); if (self_delimiting (character)) { input_text_offset++; if (character == '\n') - line_number++; + line_number++; - result = strdup (" "); + result = xstrdup (" "); *result = character; return (result); } for (i = 0; ((input_text_offset != size_of_input_text) - && (character = curchar ()) - && command_char (character)); + && (character = curchar ()) + && command_char (character)); i++, input_text_offset++); result = (char *)xmalloc (i + 1); memcpy (result, &input_text[input_text_offset - i], i); - result[i] = '\0'; + result[i] = 0; return (result); } -/* Return non-zero if CHARACTER is self-delimiting. */ +/* Return nonzero if CHARACTER is self-delimiting. */ int self_delimiting (character) int character; { /* @; and @\ are not Texinfo commands, but they are listed here anyway. I don't know why. --karl, 10aug96. */ return member (character, "~{|}`^\\@?=;:.-,*\'\" !\n\t"); } /* Clear whitespace from the front and end of string. */ void canon_white (string) char *string; { int len = strlen (string); int x; if (!len) return; for (x = 0; x < len; x++) { if (!cr_or_whitespace (string[x])) - { - strcpy (string, string + x); - break; - } + { + strcpy (string, string + x); + break; + } } len = strlen (string); if (len) len--; while (len > -1 && cr_or_whitespace (string[len])) len--; - string[len + 1] = '\0'; + string[len + 1] = 0; } /* Bash STRING, replacing all whitespace with just one space. */ void fix_whitespace (string) char *string; { char *temp = (char *)xmalloc (strlen (string) + 1); int string_index = 0; int temp_index = 0; int c; canon_white (string); while (string[string_index]) { c = temp[temp_index++] = string[string_index++]; if (c == ' ' || c == '\n' || c == '\t') - { - temp[temp_index - 1] = ' '; - while ((c = string[string_index]) && (c == ' ' || - c == '\t' || - c == '\n')) - string_index++; - } - } - temp[temp_index] = '\0'; + { + temp[temp_index - 1] = ' '; + while ((c = string[string_index]) && (c == ' ' || + c == '\t' || + c == '\n')) + string_index++; + } + } + temp[temp_index] = 0; strcpy (string, temp); free (temp); } /* Discard text until the desired string is found. The string is included in the discarded text. */ void discard_until (string) char *string; { int temp = search_forward (string, input_text_offset); int tt = (temp < 0) ? size_of_input_text : temp + strlen (string); int from = input_text_offset; /* Find out what line we are on. */ while (from != tt) if (input_text[from++] == '\n') line_number++; if (temp < 0) { input_text_offset = size_of_input_text - strlen (string); if (strcmp (string, "\n") != 0) - { - line_error ("Expected `%s'", string); - return; - } + { + line_error (_("Expected `%s'"), string); + return; + } } else input_text_offset = temp; input_text_offset += strlen (string); } /* Read characters from the file until we are at MATCH. Place the characters read into STRING. On exit input_text_offset is after the match string. Return the offset where the string starts. */ int get_until (match, string) char *match, **string; { int len, current_point, x, new_point, tem; current_point = x = input_text_offset; new_point = search_forward (match, input_text_offset); if (new_point < 0) new_point = size_of_input_text; len = new_point - current_point; /* Keep track of which line number we are at. */ tem = new_point + (strlen (match) - 1); while (x != tem) if (input_text[x++] == '\n') line_number++; *string = (char *)xmalloc (len + 1); memcpy (*string, &input_text[current_point], len); - (*string)[len] = '\0'; + (*string)[len] = 0; /* 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; return (new_point); } /* Read characters from the file until we are at MATCH or end of line. Place the characters read into STRING. */ void -get_until_in_line (match, string) +get_until_in_line (expand, match, string) + int expand; char *match, **string; { - int real_bottom, temp; - - real_bottom = size_of_input_text; - temp = search_forward ("\n", input_text_offset); - - if (temp < 0) - temp = size_of_input_text; + int real_bottom = size_of_input_text; + int limit = search_forward ("\n", input_text_offset); + if (limit < 0) + limit = size_of_input_text; + + /* 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: + @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) + { + 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; + } - size_of_input_text = temp; + size_of_input_text = limit; get_until (match, string); size_of_input_text = real_bottom; } void get_rest_of_line (string) char **string; { get_until ("\n", string); canon_white (*string); - if (curchar () == '\n') /* as opposed to the end of the file... */ + if (curchar () == '\n') /* as opposed to the end of the file... */ { line_number++; input_text_offset++; } } /* Backup the input pointer to the previous character, keeping track of the current line number. */ void backup_input_pointer () { if (input_text_offset) { input_text_offset--; if (curchar () == '\n') - line_number--; + line_number--; } } /* Read characters from the file until we are at MATCH or closing brace. Place the characters read into STRING. */ void get_until_in_braces (match, string) char *match, **string; { + char *temp; int i, brace = 0; int match_len = strlen (match); - char *temp; for (i = input_text_offset; i < size_of_input_text; i++) { if (input_text[i] == '{') - brace++; + brace++; else if (input_text[i] == '}') - brace--; + brace--; else if (input_text[i] == '\n') - line_number++; + line_number++; if (brace < 0 || - (brace == 0 && strncmp (input_text + i, match, match_len) == 0)) - break; + (brace == 0 && strncmp (input_text + i, match, match_len) == 0)) + break; } match_len = i - input_text_offset; temp = (char *)xmalloc (2 + match_len); strncpy (temp, input_text + input_text_offset, match_len); - temp[match_len] = '\0'; + temp[match_len] = 0; input_text_offset = i; *string = temp; } /* **************************************************************** */ -/* */ -/* Converting the File */ -/* */ +/* */ +/* Converting the 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[] = { ".texinfo", ".texi", ".txinfo", "", (char *)NULL }; void initialize_conversion () { init_tag_table (); init_indices (); init_internals (); init_paragraph (); /* This is used for splitting the output file and for doing section headings. It was previously initialized in `init_paragraph', but its use there loses with the `init_paragraph' calls done by the multitable code; the tag indices get reset to zero. */ output_position = 0; } /* We read in multiples of 4k, simply because it is a typical pipe size on unix systems. */ #define READ_BUFFER_GROWTH (4 * 4096) -/* Convert the texinfo file coming from the open stream STREAM. Assume the +/* Convert the Texinfo file coming from the open stream STREAM. Assume the source of the stream is named NAME. */ void convert_from_stream (stream, name) FILE *stream; char *name; { char *buffer = (char *)NULL; int buffer_offset = 0, buffer_size = 0; initialize_conversion (); /* Read until the end of the stream. This isn't strictly correct, since the texinfo input may end before the stream ends, but it is a quick working hueristic. */ while (!feof (stream)) { int count; if (buffer_offset + (READ_BUFFER_GROWTH + 1) >= buffer_size) - buffer = (char *) - xrealloc (buffer, (buffer_size += READ_BUFFER_GROWTH)); + buffer = (char *) + xrealloc (buffer, (buffer_size += READ_BUFFER_GROWTH)); count = fread (buffer + buffer_offset, 1, READ_BUFFER_GROWTH, stream); if (count < 0) - { - perror (name); - exit (FATAL); - } + { + perror (name); + exit (FATAL); + } buffer_offset += count; if (count == 0) - break; + break; } /* Set the globals to the new file. */ input_text = buffer; size_of_input_text = buffer_offset; - input_filename = strdup (name); - node_filename = strdup (name); + input_filename = xstrdup (name); + node_filename = xstrdup (name); 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). The SIZE_OF_INPUT_TEXT is one past the actual end of the text. */ input_text[size_of_input_text] = '\n'; convert_from_loaded_file (name); } void convert_from_file (name) char *name; { register int i; char *filename = (char *)xmalloc (strlen (name) + 50); initialize_conversion (); /* Try to load the file specified by NAME, concatenated with our various suffixes. Prefer files like `makeinfo.texi' to `makeinfo'. */ for (i = 0; suffixes[i]; i++) { strcpy (filename, name); strcat (filename, suffixes[i]); if (find_and_load (filename)) - break; + break; if (!suffixes[i][0] && strrchr (filename, '.')) - { - fs_error (filename); - free (filename); - return; - } + { + fs_error (filename); + free (filename); + return; + } } if (!suffixes[i]) { fs_error (name); free (filename); return; } input_filename = filename; convert_from_loaded_file (name); } void convert_from_loaded_file (name) char *name; { char *expand_filename (), *filename_part (); char *real_output_filename = (char *)NULL; #if defined (HAVE_MACROS) remember_itext (input_text, 0); #endif /* HAVE_MACROS */ /* 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); + search_forward (setfilename_search, input_text_offset); if ((input_text_offset == 0) || - ((input_text_offset > 0) && - (input_text[input_text_offset -1] == '\n'))) - break; + ((input_text_offset > 0) && + (input_text[input_text_offset -1] == '\n'))) + break; else if (input_text_offset > 0) - input_text_offset++; + input_text_offset++; } if (input_text_offset < 0) { if (!command_output_filename) - { + { #if defined (REQUIRE_SETFILENAME) - error ("No `%s' found in `%s'", setfilename_search, name); - goto finished; + 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); + 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 */ - } + } } else input_text_offset += strlen (setfilename_search); if (!command_output_filename) get_until ("\n", &output_filename); else { if (input_text_offset != -1) - discard_until ("\n"); + discard_until ("\n"); else - input_text_offset = 0; + input_text_offset = 0; real_output_filename = output_filename = command_output_filename; command_output_filename = (char *)NULL; } canon_white (output_filename); - if (real_output_filename && - strcmp (real_output_filename, "-") == 0) + if (real_output_filename && strcmp (real_output_filename, "-") == 0) { - real_output_filename = strdup (real_output_filename); + if (macro_expansion_filename + && strcmp (macro_expansion_filename, "-") == 0) + { + fprintf (stderr, _("%s: Skipping macro expansion to stdout as Info output is going there.\n"), + progname); + macro_expansion_output_stream = NULL; + } + real_output_filename = xstrdup (real_output_filename); output_stream = stdout; - splitting = 0; /* Cannot split when writing to stdout. */ + splitting = 0; /* Cannot split when writing to stdout. */ } else { if (!real_output_filename) - real_output_filename = expand_filename (output_filename, name); + real_output_filename = expand_filename (output_filename, name); else - real_output_filename = strdup (real_output_filename); + real_output_filename = xstrdup (real_output_filename); output_stream = fopen (real_output_filename, "w"); } - if (verbose_mode && output_stream != stdout) - printf ("Making %s file `%s' from `%s'.\n", + if (output_stream != stdout) + printf (_("Making %s file `%s' from `%s'.\n"), no_headers ? "text" : "info", output_filename, input_filename); if (output_stream == NULL) { fs_error (real_output_filename); goto finished; } /* Make the displayable filename from output_filename. Only the base portion of the filename need be displayed. */ if (output_stream != stdout) pretty_output_filename = filename_part (output_filename); else - pretty_output_filename = strdup ("stdout"); + pretty_output_filename = xstrdup ("stdout"); /* For this file only, count the number of newlines from the top of the file to here. This way, we keep track of line numbers for error reporting. Line_number starts at 1, since the user isn't zero-based. */ { int temp = 0; line_number = 1; while (temp != input_text_offset) if (input_text[temp++] == '\n') - line_number++; + 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); + 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); } close_paragraph (); reader_loop (); finished: + discard_insertions (0); close_paragraph (); flush_file_stack (); #if defined (HAVE_MACROS) if (macro_expansion_output_stream) - fclose (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) + { + fprintf (stderr, _("%s: Removing macro output file `%s' due to errors; use --force to preserve.\n"), + progname, macro_expansion_filename); + if (unlink (macro_expansion_filename) < 0) + perror (macro_expansion_filename); + } + } #endif /* HAVE_MACROS */ - if (output_stream != NULL) + if (output_stream) { output_pending_notes (); free_pending_notes (); if (tag_table != NULL) - { - tag_table = (TAG_ENTRY *) reverse_list (tag_table); - if (!no_headers) - write_tag_table (); - } + { + tag_table = (TAG_ENTRY *) reverse_list (tag_table); + if (!no_headers) + write_tag_table (); + } if (output_stream != stdout) - fclose (output_stream); + fclose (output_stream); /* If validating, then validate the entire file right now. */ if (validating) - validate_file (tag_table); - - /* This used to test && !errors_printed. - But some files might have legit warnings. So split anyway. */ - if (splitting) - split_file (real_output_filename, 0); + validate_file (tag_table); + + if (splitting && (!errors_printed || force)) + split_file (real_output_filename, 0); + else if (errors_printed && !force + && strcmp (real_output_filename, "-") != 0 + && strcmp (real_output_filename, "/dev/null") != 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); + if (unlink (real_output_filename) < 0) + perror (real_output_filename); + } } free (real_output_filename); } void free_and_clear (pointer) char **pointer; { - if ((*pointer) != (char *) NULL) + if (*pointer) { free (*pointer); *pointer = (char *) NULL; } } /* Initialize some state. */ void init_internals () { - free_and_clear (¤t_node); free_and_clear (&output_filename); free_and_clear (&command); free_and_clear (&input_filename); free_node_references (); init_insertion_stack (); init_brace_stack (); + current_node = NULL; /* sometimes already freed */ command_index = 0; in_menu = 0; in_detailmenu = 0; top_node_seen = 0; non_top_node_seen = 0; } void init_paragraph () { free_and_clear (&output_paragraph); output_paragraph = (unsigned char *)xmalloc (paragraph_buffer_len); - output_paragraph[0] = '\0'; + output_paragraph[0] = 0; output_paragraph_offset = 0; output_column = 0; paragraph_is_open = 0; current_indent = 0; } /* 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 () { int character; int done = 0; int dash_count = 0; while (!done) { if (input_text_offset >= size_of_input_text) - break; + break; character = curchar (); if (!in_fixed_width_font && - (character == '\'' || character == '`') && - input_text[input_text_offset + 1] == character) - { - input_text_offset++; - character = '"'; - } + (character == '\'' || character == '`') && + input_text[input_text_offset + 1] == character) + { + input_text_offset++; + character = '"'; + } if (character == '-') - { - dash_count++; - if (dash_count == 2 && !in_fixed_width_font) - { - input_text_offset++; - continue; - } - } + { + dash_count++; + if (dash_count == 2 && !in_fixed_width_font) + { + input_text_offset++; + continue; + } + } else - { - 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. */ + is blank. If so, advance to the carriage return. */ if (whitespace (character)) - { - register int i = input_text_offset + 1; + { + register int i = input_text_offset + 1; - while (i < size_of_input_text && whitespace (input_text[i])) - i++; + 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--; + if (i == size_of_input_text || input_text[i] == '\n') + { + if (i == size_of_input_text) + i--; - input_text_offset = i; - character = curchar (); - } - } + 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 non-zero argument. */ - if (!in_detailmenu) - tem = glean_node_from_menu (1); - } - } + { + 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 `{'"); - remember_brace (misplaced_brace); - - /* Don't advance input_text_offset since this happens in - remember_brace (). - input_text_offset++; + { + 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; + break; - case '}': - pop_and_call_brace (); - input_text_offset++; - break; + case '}': + pop_and_call_brace (); + input_text_offset++; + break; - default: - add_char (character); - input_text_offset++; - } + default: + add_char (character); + input_text_offset++; + } } #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 * get_command_entry (string) char *string; { register int i; - for (i = 0; CommandTable[i].name; i++) - if (strcmp (CommandTable[i].name, string) == 0) - return (&CommandTable[i]); + for (i = 0; command_table[i].name; i++) + if (strcmp (command_table[i].name, string) == 0) + 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)) + (strcmp (user_command_array[i]->name, string) == 0)) return (user_command_array[i]); - /* Nope, we never heard of this command. */ + /* We never heard of this command. */ return ((COMMAND *) -1); } /* input_text_offset is right at the command prefix character. Read the next token to determine what to do. */ void read_command () { COMMAND *entry; 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; def = find_macro (command); if (def) { - /* We disallow recursive use of a macro call. Inhibit the expansion - of this macro during the life of its execution. */ - if (!(def->flags & ME_RECURSE)) - def->inhibited = 1; + /* We disallow recursive use of a macro call. Inhibit the expansion + of this macro during the life of its execution. */ + if (!(def->flags & ME_RECURSE)) + def->inhibited = 1; - execute_macro (def); + execute_macro (def); - if (!(def->flags & ME_RECURSE)) - def->inhibited = 0; + if (!(def->flags & ME_RECURSE)) + def->inhibited = 0; - return; + return; } } #endif /* HAVE_MACROS */ entry = get_command_entry (command); - if (entry == (COMMAND *)-1) { - line_error ("Unknown command `%s'", command); + line_error (_("Unknown command `%s'"), command); return; } if (entry->argument_in_braces) remember_brace (entry->proc); (*(entry->proc)) (START, output_paragraph_offset, 0); } /* Return the string which invokes PROC; a pointer to a function. */ char * find_proc_name (proc) COMMAND_FUNCTION *proc; { register int i; - for (i = 0; CommandTable[i].name; i++) - if (proc == CommandTable[i].proc) - return (CommandTable[i].name); - return ("NO_NAME!"); + for (i = 0; command_table[i].name; i++) + if (proc == command_table[i].proc) + return command_table[i].name; + return _("NO_NAME!"); } void init_brace_stack () { brace_stack = (BRACE_ELEMENT *) NULL; } void remember_brace (proc) COMMAND_FUNCTION *proc; { if (curchar () != '{') - line_error ("%c%s expected `{..}'", COMMAND_PREFIX, command); + line_error (_("%c%s expected `{...}'"), COMMAND_PREFIX, command); else input_text_offset++; remember_brace_1 (proc, output_paragraph_offset); } /* Remember the current output position here. Save PROC along with it so you can call it later. */ void remember_brace_1 (proc, position) COMMAND_FUNCTION *proc; int position; { BRACE_ELEMENT *new = (BRACE_ELEMENT *) xmalloc (sizeof (BRACE_ELEMENT)); new->next = brace_stack; new->proc = proc; new->pos = position; new->line = line_number; new->in_fixed_width_font = in_fixed_width_font; brace_stack = new; } /* Pop the top of the brace stack, and call the associated function with the args END and POS. */ void pop_and_call_brace () { BRACE_ELEMENT *temp; COMMAND_FUNCTION *proc; int pos; if (brace_stack == (BRACE_ELEMENT *) NULL) { - line_error ("Unmatched }"); + 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; (*proc) (END, pos, output_paragraph_offset); } /* Shift all of the markers in `brace_stack' by AMOUNT. */ void adjust_braces_following (here, amount) int here, amount; { register BRACE_ELEMENT *stack = brace_stack; while (stack) { if (stack->pos >= here) - stack->pos += amount; + stack->pos += amount; stack = stack->next; } } /* 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 I only detect it at the beginning of nodes. */ void discard_braces () { if (!brace_stack) return; while (brace_stack) { if (brace_stack->proc != misplaced_brace) - { - char *proc_name; - int temp_line_number = line_number; - - line_number = brace_stack->line; - proc_name = find_proc_name (brace_stack->proc); - line_error ("%c%s missing close brace", COMMAND_PREFIX, proc_name); - line_number = temp_line_number; - pop_and_call_brace (); - } + { + char *proc_name; + int temp_line_number = line_number; + + line_number = brace_stack->line; + proc_name = find_proc_name (brace_stack->proc); + line_error (_("%c%s missing close brace"), COMMAND_PREFIX, proc_name); + line_number = temp_line_number; + pop_and_call_brace (); + } else - { - BRACE_ELEMENT *temp; - temp = brace_stack->next; - free (brace_stack); - brace_stack = temp; - } + { + BRACE_ELEMENT *temp; + temp = brace_stack->next; + free (brace_stack); + brace_stack = temp; + } } } int get_char_len (character) int character; { /* Return the printed length of the character. */ int len; switch (character) { case '\t': len = (output_column + 8) & 0xf7; if (len > fill_column) - len = fill_column - output_column; + len = fill_column - output_column; else - len = len - output_column; + len = len - output_column; break; case '\n': len = fill_column - output_column; break; default: - if (character < ' ') - len = 2; + /* ASCII control characters appear as two characters in the output + (e.g., ^A). But characters with the high bit set are just one + on suitable terminals, so don't count them as two for line + breaking purposes. */ + if (0 <= character && character < ' ') + len = 2; else - len = 1; + len = 1; } return (len); } -#if defined (HAVE_VARARGS_H) && defined (HAVE_VSPRINTF) - void -add_word_args (va_alist) - va_dcl +#if defined (VA_FPRINTF) && __STDC__ +add_word_args (char *format, ...) +#else +add_word_args (format, va_alist) + char *format; + va_dcl +#endif { char buffer[1000]; - char *format; - va_list args; - - va_start (args); - format = va_arg (args, char *); - vsprintf (buffer, format, args); - va_end (args); - add_word (buffer); -} - -#else /* !(HAVE_VARARGS_H && HAVE_VSPRINTF) */ +#ifdef VA_FPRINTF + va_list ap; +#endif -void -add_word_args (format, arg1, arg2, arg3, arg4, arg5) - char *format; -{ - char buffer[1000]; - sprintf (buffer, format, arg1, arg2, arg3, arg4, arg5); + VA_START (ap, format); +#ifdef VA_SPRINTF + VA_SPRINTF (buffer, format, ap); +#else + sprintf (temp_string, format, a1, a2, a3, a4, a5, a6, a7, a8); +#endif /* not VA_SPRINTF */ + va_end (ap); add_word (buffer); } -#endif /* !(HAVE_VARARGS_H && HAVE_VSPRINTF) */ - /* Add STRING to output_paragraph. */ void add_word (string) char *string; { while (*string) add_char (*string++); } -/* Non-zero if the last character inserted has the syntax class of NEWLINE. */ +/* 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; -/* Non-zero means that a newline character has already been +/* Nonzero means that a newline character has already been inserted, so close_paragraph () should insert one less. */ int line_already_broken = 0; -/* When non-zero we have finished an insertion (see end_insertion ()) and we +/* When nonzero we have finished an insertion (see `end_insertion') and we want to ignore false continued paragraph closings. */ int insertion_paragraph_closed = 0; -/* Non-zero means attempt to make all of the lines have fill_column width. */ +/* 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 - non-zero, then do filling as well. */ + nonzero, then do filling as well. */ void 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)) return; /* If we are adding a character now, then we don't have to ignore close_paragraph () calls any more. */ if (must_start_paragraph && character != '\n') { must_start_paragraph = 0; - line_already_broken = 0; /* The line is no longer broken. */ + line_already_broken = 0; /* The line is no longer broken. */ if (current_indent > output_column) - { - indent (current_indent - output_column); - output_column = current_indent; - } + { + indent (current_indent - output_column); + output_column = current_indent; + } } if (non_splitting_words && member (character, " \t\n")) character = ' ' | 0x80; insertion_paragraph_closed = 0; switch (character) { case '\n': if (!filling_enabled) - { - insert ('\n'); - - if (force_flush_right) - { - close_paragraph (); - /* Hack to force single blank lines out in this mode. */ - flush_output (); - } - - output_column = 0; - - if (!no_indent && paragraph_is_open) - indent (output_column = current_indent); - break; - } + { + insert ('\n'); + + if (force_flush_right) + { + close_paragraph (); + /* Hack to force single blank lines out in this mode. */ + flush_output (); + } + + output_column = 0; + + if (!no_indent && paragraph_is_open) + indent (output_column = current_indent); + break; + } else /* CHARACTER is newline, and filling is enabled. */ - { - if (sentence_ender (last_inserted_character)) - { - insert (' '); - output_column++; - last_inserted_character = character; - } - } + { + if (end_of_sentence_p ()) + { + insert (' '); + output_column++; + last_inserted_character = character; + } + } if (last_char_was_newline) - { - close_paragraph (); - pending_indent = 0; - } + { + close_paragraph (); + pending_indent = 0; + } else - { - last_char_was_newline = 1; - insert (' '); - output_column++; - } + { + last_char_was_newline = 1; + insert (' '); + output_column++; + } break; default: { - int len = get_char_len (character); - int suppress_insert = 0; - - if ((character == ' ') && (last_char_was_newline)) - { - if (!paragraph_is_open) - { - pending_indent++; - return; - } - } - - 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 (!paragraph_start_indent) - indent (pending_indent); - pending_indent = 0; - } - - if ((output_column += len) > fill_column) - { - if (filling_enabled) - { - int temp = output_paragraph_offset; - while (--temp > 0 && output_paragraph[temp] != '\n') - { - /* If we have found a space, we have the place to break - the line. */ - if (output_paragraph[temp] == ' ') - { - /* Remove trailing whitespace from output. */ - while (temp && whitespace (output_paragraph[temp - 1])) - temp--; - - output_paragraph[temp++] = '\n'; - - /* We have correctly broken the line where we want - to. What we don't want is spaces following where - we have decided to break the line. We get rid of - them. */ - { - int t1 = temp; - - for (;; t1++) - { - if (t1 == output_paragraph_offset) - { - if (whitespace (character)) - suppress_insert = 1; - break; - } - if (!whitespace (output_paragraph[t1])) - break; - } - - if (t1 != temp) - { - adjust_braces_following (temp, (- (t1 - temp))); - strncpy ((char *) &output_paragraph[temp], - (char *) &output_paragraph[t1], - (output_paragraph_offset - t1)); - output_paragraph_offset -= (t1 - temp); - } - } - - /* Filled, but now indent if that is right. */ - if (indented_fill && current_indent) - { - int buffer_len = ((output_paragraph_offset - temp) - + current_indent); - char *temp_buffer = (char *)xmalloc (buffer_len); - int indentation = 0; - - /* We have to shift any markers that are in - front of the wrap point. */ - adjust_braces_following (temp, current_indent); - - while (current_indent > 0 && - indentation != current_indent) - temp_buffer[indentation++] = ' '; - - strncpy ((char *) &temp_buffer[current_indent], - (char *) &output_paragraph[temp], - buffer_len - current_indent); - - if (output_paragraph_offset + buffer_len - >= paragraph_buffer_len) - { - unsigned char *tt = xrealloc - (output_paragraph, - (paragraph_buffer_len += buffer_len)); - output_paragraph = tt; - } - strncpy ((char *) &output_paragraph[temp], - temp_buffer, buffer_len); - output_paragraph_offset += current_indent; - free (temp_buffer); - } - output_column = 0; - while (temp < output_paragraph_offset) - output_column += - get_char_len (output_paragraph[temp++]); - output_column += len; - break; - } - } - } - } - - if (!suppress_insert) - { - insert (character); - last_inserted_character = character; - } - last_char_was_newline = 0; - line_already_broken = 0; + int len = get_char_len (character); + int suppress_insert = 0; + + if ((character == ' ') && (last_char_was_newline)) + { + if (!paragraph_is_open) + { + pending_indent++; + return; + } + } + + 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 (!paragraph_start_indent) + indent (pending_indent); + pending_indent = 0; + } + + if ((output_column += len) > fill_column) + { + if (filling_enabled) + { + int temp = output_paragraph_offset; + while (--temp > 0 && output_paragraph[temp] != '\n') + { + /* If we have found a space, we have the place to break + the line. */ + if (output_paragraph[temp] == ' ') + { + /* Remove trailing whitespace from output. */ + while (temp && whitespace (output_paragraph[temp - 1])) + temp--; + + output_paragraph[temp++] = '\n'; + + /* We have correctly broken the line where we want + to. What we don't want is spaces following where + we have decided to break the line. We get rid of + them. */ + { + int t1 = temp; + + for (;; t1++) + { + if (t1 == output_paragraph_offset) + { + if (whitespace (character)) + suppress_insert = 1; + break; + } + if (!whitespace (output_paragraph[t1])) + break; + } + + if (t1 != temp) + { + adjust_braces_following (temp, (- (t1 - temp))); + strncpy ((char *) &output_paragraph[temp], + (char *) &output_paragraph[t1], + (output_paragraph_offset - t1)); + output_paragraph_offset -= (t1 - temp); + } + } + + /* Filled, but now indent if that is right. */ + if (indented_fill && current_indent) + { + int buffer_len = ((output_paragraph_offset - temp) + + current_indent); + char *temp_buffer = (char *)xmalloc (buffer_len); + int indentation = 0; + + /* We have to shift any markers that are in + front of the wrap point. */ + adjust_braces_following (temp, current_indent); + + while (current_indent > 0 && + indentation != current_indent) + temp_buffer[indentation++] = ' '; + + strncpy ((char *) &temp_buffer[current_indent], + (char *) &output_paragraph[temp], + buffer_len - current_indent); + + if (output_paragraph_offset + buffer_len + >= paragraph_buffer_len) + { + unsigned char *tt = xrealloc + (output_paragraph, + (paragraph_buffer_len += buffer_len)); + output_paragraph = tt; + } + strncpy ((char *) &output_paragraph[temp], + temp_buffer, buffer_len); + output_paragraph_offset += current_indent; + free (temp_buffer); + } + output_column = 0; + while (temp < output_paragraph_offset) + output_column += + get_char_len (output_paragraph[temp++]); + output_column += len; + break; + } + } + } + } + + if (!suppress_insert) + { + insert (character); + last_inserted_character = character; + } + last_char_was_newline = 0; + line_already_broken = 0; } } } /* Insert CHARACTER into `output_paragraph'. */ void insert (character) int character; { output_paragraph[output_paragraph_offset++] = character; if (output_paragraph_offset == paragraph_buffer_len) { output_paragraph = - xrealloc (output_paragraph, (paragraph_buffer_len += 100)); + xrealloc (output_paragraph, (paragraph_buffer_len += 100)); } } /* Insert the null-terminated string STRING into `output_paragraph'. */ void insert_string (string) char *string; { while (*string) insert (*string++); } + +/* Sentences might have these characters after the period (or whatever). */ +#define post_sentence(c) ((c) == ')' || (c) == '\'' || (c) == '"' \ + || (c) == ']') + +/* Return true if at an end-of-sentence character, possibly followed by + post-sentence punctuation to ignore. */ +static int +end_of_sentence_p () +{ + int loc = output_paragraph_offset - 1; + while (loc > 0 && post_sentence (output_paragraph[loc])) + loc--; + return sentence_ender (output_paragraph[loc]); +} + + /* Remove upto COUNT characters of whitespace from the the current output line. If COUNT is less than zero, then remove until none left. */ void kill_self_indent (count) int count; { /* Handle infinite case first. */ if (count < 0) { output_column = 0; while (output_paragraph_offset) - { - if (whitespace (output_paragraph[output_paragraph_offset - 1])) - output_paragraph_offset--; - else - break; - } + { + if (whitespace (output_paragraph[output_paragraph_offset - 1])) + output_paragraph_offset--; + else + break; + } } else { while (output_paragraph_offset && count--) - if (whitespace (output_paragraph[output_paragraph_offset - 1])) - output_paragraph_offset--; - else - break; + if (whitespace (output_paragraph[output_paragraph_offset - 1])) + output_paragraph_offset--; + else + break; } } -/* Non-zero means do not honor calls to flush_output (). */ +/* Nonzero means do not honor calls to flush_output (). */ static int flushing_ignored = 0; /* Prevent calls to flush_output () from having any effect. */ void inhibit_output_flushing () { flushing_ignored++; } /* Allow calls to flush_output () to write the paragraph data. */ void uninhibit_output_flushing () { flushing_ignored--; } void flush_output () { register 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) { int temp = UNMETA (output_paragraph[i]); if (temp == ' ') - output_paragraph[i] &= 0x7f; + output_paragraph[i] &= 0x7f; } } fwrite (output_paragraph, 1, output_paragraph_offset, output_stream); output_position += output_paragraph_offset; output_paragraph_offset = 0; } /* How to close a paragraph controlling the number of lines between this one and the last one. */ /* Paragraph spacing is controlled by this variable. It is the number of blank lines that you wish to appear between paragraphs. A value of 1 creates a single blank line between paragraphs. */ int paragraph_spacing = DEFAULT_PARAGRAPH_SPACING; /* Close the current paragraph, leaving no blank lines between them. */ void close_single_paragraph () { close_paragraph_with_lines (0); } /* Close a paragraph after an insertion has ended. */ void close_insertion_paragraph () { if (!insertion_paragraph_closed) { /* Close the current paragraph, breaking the line. */ close_single_paragraph (); - /* Start a new paragraph here, inserting whatever indention is correct - for the now current insertion level (one above the one that we are - ending). */ + /* Start a new paragraph, with the correct indentation for the now + current insertion level (one above the one that we are ending). */ start_paragraph (); - /* Tell close_paragraph () that the previous line has already been - broken, so it should insert one less newline. */ + /* Tell `close_paragraph' that the previous line has already been + broken, so it should insert one less newline. */ line_already_broken = 1; - /* Let functions such as add_char () know that we have already found a - newline. */ + /* Tell functions such as `add_char' we've already found a newline. */ ignore_blank_line (); } else { /* If the insertion paragraph is closed already, then we are seeing - two `@end' commands in a row. Note that the first one we saw was - handled in the first part of this if-then-else clause, and at that - time start_paragraph () was called, partially to handle the proper - indentation of the current line. However, the indentation level - may have just changed again, so we may have to outdent the current - line to the new indentation level. */ + two `@end' commands in a row. Note that the first one we saw was + handled in the first part of this if-then-else clause, and at that + time `start_paragraph' was called, partially to handle the proper + indentation of the current line. However, the indentation level + may have just changed again, so we may have to outdent the current + line to the new indentation level. */ if (current_indent < output_column) - kill_self_indent (output_column - current_indent); + kill_self_indent (output_column - current_indent); } 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; /* The insertion paragraph is no longer closed. */ insertion_paragraph_closed = 0; if (paragraph_is_open && !must_start_paragraph) { register int tindex, c; tindex = output_paragraph_offset; /* Back up to last non-newline/space character, forcing all such - subsequent characters to be newlines. This isn't strictly - necessary, but a couple of functions use the presence of a newline - to make decisions. */ + subsequent characters to be newlines. This isn't strictly + necessary, but a couple of functions use the presence of a newline + to make decisions. */ for (tindex = output_paragraph_offset - 1; tindex >= 0; --tindex) - { - c = output_paragraph[tindex]; + { + c = output_paragraph[tindex]; - if (c == ' '|| c == '\n') - output_paragraph[tindex] = '\n'; - else - break; - } + if (c == ' '|| c == '\n') + output_paragraph[tindex] = '\n'; + else + break; + } /* All trailing whitespace is ignored. */ output_paragraph_offset = ++tindex; /* Break the line if that is appropriate. */ if (paragraph_spacing >= 0) - insert ('\n'); + insert ('\n'); - /* Add as many blank lines as is specified in PARAGRAPH_SPACING. */ + /* Add as many blank lines as is specified in `paragraph_spacing'. */ if (!force_flush_right) - { - for (i = 0; i < (paragraph_spacing - line_already_broken); i++) - insert ('\n'); - } + { + for (i = 0; i < (paragraph_spacing - line_already_broken); i++) + insert ('\n'); + } /* If we are doing flush right indentation, then do it now - on the paragraph (really a single line). */ + on the paragraph (really a single line). */ if (force_flush_right) - do_flush_right_indentation (); + do_flush_right_indentation (); flush_output (); paragraph_is_open = 0; no_indent = 0; output_column = 0; } ignore_blank_line (); } /* Make the last line just read look as if it were only a newline. */ void ignore_blank_line () { last_inserted_character = '\n'; last_char_was_newline = 1; } /* Align the end of the text in output_paragraph with fill_column. */ void do_flush_right_indentation () { char *temp; int temp_len; kill_self_indent (-1); if (output_paragraph[0] != '\n') { - output_paragraph[output_paragraph_offset] = '\0'; + output_paragraph[output_paragraph_offset] = 0; if (output_paragraph_offset < fill_column) - { - register int i; + { + register int i; - if (fill_column >= paragraph_buffer_len) - output_paragraph = - xrealloc (output_paragraph, - (paragraph_buffer_len += fill_column)); + if (fill_column >= paragraph_buffer_len) + output_paragraph = + xrealloc (output_paragraph, + (paragraph_buffer_len += fill_column)); - temp_len = strlen ((char *)output_paragraph); - temp = (char *)xmalloc (temp_len + 1); - memcpy (temp, (char *)output_paragraph, temp_len); + temp_len = strlen ((char *)output_paragraph); + temp = (char *)xmalloc (temp_len + 1); + memcpy (temp, (char *)output_paragraph, temp_len); - for (i = 0; i < fill_column - output_paragraph_offset; i++) - output_paragraph[i] = ' '; + for (i = 0; i < fill_column - output_paragraph_offset; i++) + output_paragraph[i] = ' '; - memcpy ((char *)output_paragraph + i, temp, temp_len); - free (temp); - output_paragraph_offset = fill_column; - } + memcpy ((char *)output_paragraph + i, temp, temp_len); + free (temp); + output_paragraph_offset = fill_column; + } } } /* Begin a new paragraph. */ void start_paragraph () { /* First close existing one. */ if (paragraph_is_open) close_paragraph (); /* In either case, the insertion paragraph is no longer closed. */ insertion_paragraph_closed = 0; /* However, the paragraph is open! */ paragraph_is_open = 1; /* If we MUST_START_PARAGRAPH, that simply means that start_paragraph () had to be called before we would allow any other paragraph operations to have an effect. */ if (!must_start_paragraph) { int amount_to_indent = 0; /* If doing indentation, then insert the appropriate amount. */ if (!no_indent) - { - if (inhibit_paragraph_indentation) - { - amount_to_indent = current_indent; - if (inhibit_paragraph_indentation < 0) - inhibit_paragraph_indentation++; - } - else if (paragraph_start_indent < 0) - amount_to_indent = current_indent; - else - amount_to_indent = current_indent + paragraph_start_indent; - - if (amount_to_indent >= output_column) - { - amount_to_indent -= output_column; - indent (amount_to_indent); - output_column += amount_to_indent; - } - } + { + if (inhibit_paragraph_indentation) + { + amount_to_indent = current_indent; + if (inhibit_paragraph_indentation < 0) + inhibit_paragraph_indentation++; + } + else if (paragraph_start_indent < 0) + amount_to_indent = current_indent; + else + amount_to_indent = current_indent + paragraph_start_indent; + + if (amount_to_indent >= output_column) + { + amount_to_indent -= output_column; + indent (amount_to_indent); + output_column += amount_to_indent; + } + } } else must_start_paragraph = 0; } /* Insert the indentation specified by AMOUNT. */ void indent (amount) int amount; { register BRACE_ELEMENT *elt = brace_stack; /* 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->pos += amount; elt = elt->next; } while (--amount >= 0) insert (' '); } /* Search forward for STRING in input_text. FROM says where where to start. */ int search_forward (string, from) char *string; int from; { int len = strlen (string); while (from < size_of_input_text) { 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); + return (0); ch1 = coerce_to_upper (ch1); ch2 = coerce_to_upper (ch2); if (ch1 != ch2) - return (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 ifset: - case ifclear: - case cartouche: - elt = elt->next; - level--; - break; - - default: - done = 1; - } + { + 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 (); - canon_white (item_function); 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 = strdup (input_filename); + 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 (_("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; + 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"); + 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); - } + { + 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, strdup ("")); + push_insertion (type, xstrdup ("")); no_discard++; } else push_insertion (type, get_item_function ()); switch (type) { case menu: if (!no_headers) - close_paragraph (); + close_paragraph (); filling_enabled = no_indent = 0; inhibit_paragraph_indentation = 1; if (!no_headers) - add_word ("* Menu:\n"); + add_word (_("* Menu:\n")); in_menu++; no_discard++; break; case detailmenu: - if (!in_menu) - { - if (!no_headers) - close_paragraph (); + { + if (!no_headers) + close_paragraph (); - filling_enabled = no_indent = 0; - inhibit_paragraph_indentation = 1; + filling_enabled = no_indent = 0; + inhibit_paragraph_indentation = 1; - no_discard++; - } + 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. */ + 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; - + 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 = strdup ("@bullet"); - insertion_stack->item_function[0] = COMMAND_PREFIX; - } - } + { + 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); - } + { + 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); + start_enumerating (atoi (enumeration_arg), ENUM_DIGITS); else - start_enumerating (*enumeration_arg, ENUM_ALPHA); + 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 (); + 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++; + 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. - TYPE, with a value of bad_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. */ +/* 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)); + (_("`%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. */ + in_detailmenu--; /* No longer hacking menus. */ if (!in_menu) - { - if (!no_headers) - close_insertion_paragraph (); - } + { + if (!no_headers) + close_insertion_paragraph (); + } break; case menu: - in_menu--; /* No longer hacking menus. */ + in_menu--; /* No longer hacking menus. */ if (!no_headers) - close_insertion_paragraph (); + 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. */ + in indentation. */ if (type != format) - current_indent -= default_indentation_increment; + current_indent -= default_indentation_increment; /* The ending of one of these insertions always marks the - start of a new paragraph. */ + 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 () + 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. */ + the insertion stack. With nonzero SPECIALS_OK, allows unmatched + ifinfo, ifset, ifclear, otherwise not. */ void -discard_insertions () +discard_insertions (specials_ok) + int specials_ok; { int real_line_number = line_number; while (insertion_stack) { - if (insertion_stack->insertion == ifinfo || - insertion_stack->insertion == ifset || - insertion_stack->insertion == ifclear) - break; + if (specials_ok && (insertion_stack->insertion == ifinfo + || insertion_stack->insertion == ifset + || insertion_stack->insertion == ifclear)) + break; else - { - char *offender; - char *current_filename; - - current_filename = input_filename; - offender = (char *)insertion_type_pname (insertion_stack->insertion); - input_filename = insertion_stack->filename; - line_number = insertion_stack->line_number; - line_error ("This `%s' doesn't have a matching `%cend %s'", offender, - COMMAND_PREFIX, offender); - input_filename = current_filename; - pop_insertion (); - } + { + 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; } /* 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 (); -#if !defined (ASTERISK_NEW_PARAGRAPH) cm_noindent (); -#endif /* ASTERISK_NEW_PARAGRAPH */ } /* 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); + 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'", + 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); + 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] = - { "January", "February", "March", "April", "May", "June", "July", - "August", "September", "October", "November", "December" }; + 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)); + 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 ('`'); + add_char ('`'); } else { if (!printing_index) - add_char ('\''); + 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_angle_brackets (arg) +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); + 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]; + for (i = start; i < end; i++) + { + character = output_paragraph[i]; - if (isletter (character)) - output_paragraph[i] = CTL (coerce_to_upper (character)); - } + 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 in makeinfo has to do just all caps. */ -void -cm_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++; - } - } -} - -/* @var in makeinfo just uppercases the text. */ +/* Small caps and @var in makeinfo just uppercase the text. */ void -cm_var (arg, start_pos, end_pos) +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++; - } + { + 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 NOP's. */ +/* 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 ("The command `%c%s' is obsolete", COMMAND_PREFIX, command); + 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) + 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) + if (macro_expansion_output_stream && !executing_string) { - char *temp1; - - temp1 = (char *)xmalloc (2 + strlen (temp)); + 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); + 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++) + for (i = 0; (t = section_alist[i].name); i++) { if (j == strlen (t) && strncmp (t, text, j) == 0) - { - int return_val; + { + int return_val; - return_val = (section_alist[i].level + section_alist_offset); + 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); - } + 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; + 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 ("There already is a node having %ctop as a section", - COMMAND_PREFIX); + 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; - } + { + 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; - } + { + 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; + int orig_offset = input_text_offset; - input_text_offset = search_forward (node_search_string, orig_offset); + input_text_offset = search_forward (node_search_string, orig_offset); - if (input_text_offset > 0) - { - int this_section; + 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; + /* 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++; + /* 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++; + if (input_text_offset < size_of_input_text) + input_text_offset++; - this_section = what_section (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; + /* 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 ("The command `%c%s' is obsolete; use `%c%s' instead", - COMMAND_PREFIX, command, COMMAND_PREFIX, command + 1); + 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 */ -/* */ +/* */ +/* 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. */ + 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 () +get_node_token (expand) + int expand; { char *string; - get_until_in_line (",", &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); + 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 (%d is first definition)", - node, tag->line_no); - return; - } + { + 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->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 (); + 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) + if (macro_expansion_output_stream && !executing_string) append_to_expansion_output (input_text_offset + 1); #endif /* HAVE_MACROS */ - node = get_node_token (); - next = get_node_token (); - prev = get_node_token (); - up = get_node_token (); + 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) + 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) - me_execute_string (node); + if (macro_expansion_output_stream && !executing_string) + me_execute_string (node); else #endif /* HAVE_MACROS */ - execute_string ("%s", node); + execute_string ("%s", node); filling_enabled = indented_fill = 0; } /* Check for defaulting of this node's next, prev, and up fields. */ - defaulting = ((strlen (next) == 0) && - (strlen (prev) == 0) && - (strlen (up) == 0)); + 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); - } + { + 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 = strdup ("(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; - } - } + { + 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. */ + 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; + { + /* Then this is the first non-@top node seen. */ + int level; - level = set_top_section_level (this_section - 1); - non_top_node_seen = 1; + 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; - } + while (ref) + { + if (ref->section == level) + ref->section = this_section - 1; + ref = ref->next; + } - ref = node_references; - } + 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 = strdup (containing_node); - - if (last_ref && - last_ref->type == menu_reference && - (strcmp (last_ref->containing_node, - containing_node) == 0)) - { - free (next); - next = strdup (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 = strdup (ref->next->node); - } - else if (!ref->next && - strcasecmp (ref->containing_node, "Top") == 0) - { - free (prev); - prev = strdup (ref->containing_node); - } - break; - } - last_ref = ref; - ref = ref->next; - } + { + 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 && !defaulting) + 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++; + me_inhibit_expansion++; #endif /* HAVE_MACROS */ if (*next) - { - execute_string (", Next: %s", next); - filling_enabled = indented_fill = 0; - } + { + execute_string (", Next: %s", next); + filling_enabled = indented_fill = 0; + } if (*prev) - { - execute_string (", Prev: %s", prev); - filling_enabled = indented_fill = 0; - } + { + execute_string (", Prev: %s", prev); + filling_enabled = indented_fill = 0; + } if (*up) - { - execute_string (", Up: %s", up); - filling_enabled = indented_fill = 0; - } + { + execute_string (", Up: %s", up); + filling_enabled = indented_fill = 0; + } #if defined (HAVE_MACROS) if (macro_expansion_output_stream) - me_inhibit_expansion--; + 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); + 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; - } + { + 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. */ - if (temp_tag = find_node (tags->next)) - { - 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 (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. */ + 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 = validate (tags->prev, tags->line_no, "Prev"); - - if (!valid) - 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') is the one with 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' is missing an \"Up\" field", tags->node); + { + 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 = 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 && *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; - } - } - } + { + 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; - } + { + 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); - } + { + 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); - } + { + 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 ( -"Validation error. `%s' field points to node `%s', which doesn't exist", - label, tag); + 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 = strdup (""); + 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; - } - } - } + 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); + 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; } } } -/* Some menu hacking. 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. */ +/* 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 ("Followed-Reference"); + return ("Cross"); default: return ("Internal-bad-reference-type"); } } -/* Remember this node name for later validation use. */ +/* 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 = strdup (node); + temp->node = xstrdup (node); temp->line_no = line; temp->section = current_section; temp->type = type; - temp->containing_node = strdup (current_node ? current_node : ""); + 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)); + 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; + 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) + menu_starter, + strlen (menu_starter)) != 0) return ((char *)NULL); else input_text_offset += strlen (menu_starter) + 1; - get_until_in_line (":", &nodename); + 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'; + 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'; + *nodename = 0; return; } if (*nodename == '(') { paren++; paren_seen++; i++; } - for (; c = nodename[i]; i++) + for (; (c = nodename[i]); i++) { if (paren) - { - if (c == '(') - paren++; - else if (c == ')') - paren--; + { + if (c == '(') + paren++; + else if (c == ')') + paren--; - continue; - } + continue; + } /* If the character following the close paren is a space, then this - node has no more characters associated with it. */ + 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; + 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'; + nodename[i] = 0; } void cm_menu () { if (current_node == (char *)NULL) { - warning ("%cmenu seen before a node has been defined", COMMAND_PREFIX); - warning ("Creating `TOP' node."); + 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 a node has been defined", COMMAND_PREFIX); - warning ("Creating `TOP' node."); + warning (_("%cmenu seen before first node"), COMMAND_PREFIX); + warning (_("creating `Top' node")); execute_string ("@node Top"); } begin_insertion (detailmenu); } /* **************************************************************** */ -/* */ -/* Cross Reference Hacking */ -/* */ +/* */ +/* Cross Reference Hacking */ +/* */ /* **************************************************************** */ +/* Return next comma-delimited argument, but do not cross a close-brace + boundary. Clean up whitespace, too. */ char * get_xref_token () { char *string; get_until_in_braces (",", &string); if (curchar () == ',') input_text_offset++; fix_whitespace (string); return (string); } -int px_ref_flag = 0; /* Controls initial output string. */ +int px_ref_flag = 0; /* Controls initial output string. */ /* Make a cross reference. */ void cm_xref (arg) { if (arg == START) { char *arg1, *arg2, *arg3, *arg4, *arg5; 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 (*arg5 || *arg4) - { - char *node_name; - - if (!*arg2) - { - if (*arg3) - node_name = arg3; - else - node_name = arg1; - } - else - node_name = arg2; - - execute_string ("%s: (%s)%s", node_name, arg4, arg1); - /* Free all of the arguments found. */ - if (arg1) free (arg1); - if (arg2) free (arg2); - if (arg3) free (arg3); - if (arg4) free (arg4); - if (arg5) free (arg5); - return; - } + { + char *node_name; + + if (!*arg2) + { + if (*arg3) + node_name = arg3; + else + node_name = arg1; + } + else + node_name = arg2; + + execute_string ("%s: (%s)%s", node_name, arg4, arg1); + /* Free all of the arguments found. */ + if (arg1) free (arg1); + if (arg2) free (arg2); + if (arg3) free (arg3); + if (arg4) free (arg4); + if (arg5) free (arg5); + return; + } else - remember_node_reference (arg1, line_number, followed_reference); + remember_node_reference (arg1, line_number, followed_reference); if (*arg3) - { - if (!*arg2) - execute_string ("%s: %s", arg3, arg1); - else - execute_string ("%s: %s", arg2, arg1); - } + { + if (!*arg2) + execute_string ("%s: %s", arg3, arg1); + else + execute_string ("%s: %s", arg2, arg1); + } else - { - if (*arg2) - execute_string ("%s: %s", arg2, arg1); - else - execute_string ("%s::", arg1); - } + { + if (*arg2) + execute_string ("%s: %s", arg2, arg1); + else + execute_string ("%s::", arg1); + } /* Free all of the arguments found. */ if (arg1) free (arg1); if (arg2) free (arg2); if (arg3) free (arg3); if (arg4) free (arg4); 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; if (output_paragraph[output_paragraph_offset - 2] == ':' && - output_paragraph[output_paragraph_offset - 1] == ':') - return; + output_paragraph[output_paragraph_offset - 1] == ':') + return; while (temp < size_of_input_text) - { - if (cr_or_whitespace (input_text[temp])) - temp++; - else - { - if (input_text[temp] == '.' || - input_text[temp] == ',' || - input_text[temp] == '\t') - return; - else - { - line_error ( - "Cross-reference must be terminated with a period or a comma"); - return; - } - } - } + { + 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]); + } + break; + } + } } } void cm_pxref (arg) int arg; { if (arg == START) { px_ref_flag++; cm_xref (arg); px_ref_flag--; } else add_char ('.'); } void cm_inforef (arg) int arg; { if (arg == START) { - char *node, *pname, *file; + char *node = get_xref_token (); + char *pname = get_xref_token (); + char *file = get_xref_token (); + + if (*pname) + execute_string ("*note %s: (%s)%s", pname, file, node); + else + execute_string ("*note (%s)%s::", file, node); + + free (node); + free (pname); + free (file); + } +} + +/* A URL reference. */ +void +cm_uref (arg, start_pos, end_pos) + int arg, start_pos, end_pos; +{ + if (arg == END) + { + char *comma; + char *arg = (char *) &output_paragraph[start_pos]; + + 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 (')'); + } + else + { + extern int printing_index; + + if (!printing_index) + add_char ('`'); + + add_word (arg); + + if (!printing_index) + add_char ('\''); + } + free (arg); + } +} + +/* An email reference. */ +void +cm_email (arg, start_pos, end_pos) + int arg, start_pos, end_pos; +{ + if (arg == END) + { + char *comma; + char *arg = (char *) &output_paragraph[start_pos]; + + output_paragraph[end_pos] = 0; + output_column -= end_pos - start_pos; + output_paragraph_offset = start_pos; + + arg = xstrdup (arg); + comma = strchr (arg, ','); + if (comma) + { + *comma = 0; + for (comma++; isspace (*comma); comma++) + ; + add_word (comma); + add_char (' '); + } + add_char ('<'); + add_word (arg); + add_char ('>'); + free (arg); + } +} - node = get_xref_token (); - pname = get_xref_token (); - file = get_xref_token (); +/* An external image is a reference, kind of. The parsing is (not + coincidentally) similar, anyway. */ +void +cm_image (arg) + int arg; +{ + if (arg == START) + { + 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. */ + FILE *image_file; + char *name = xmalloc (strlen (name_arg) + 4); + strcpy (name, name_arg); + strcat (name, ".txt"); + image_file = fopen (name, "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); + } + } + else + warning (_("@image file `%s' unreadable: %s"), name, + strerror (errno)); + } + else + line_error (_("@image missing filename argument")); - execute_string ("*note %s: (%s)%s", pname, file, node); + if (name_arg) free (name_arg); } } /* **************************************************************** */ -/* */ -/* Insertion Command Stubs */ -/* */ +/* */ +/* 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 NOP insertion. */ + 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); } 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 (".", &enumeration_arg); + get_until_in_line (0, ".", &enumeration_arg); canon_white (enumeration_arg); if (!*enumeration_arg) { free (enumeration_arg); - enumeration_arg = strdup (default_string); + enumeration_arg = xstrdup (default_string); } if (!isdigit (*enumeration_arg) && !isletter (*enumeration_arg)) { - warning ("%s requires a letter or a digit", insertion_type_pname (type)); + warning (_("%s requires letter or digit"), insertion_type_pname (type)); switch (type) - { - case enumerate: - default_string = "1"; - break; - } - enumeration_arg = strdup (default_string); + { + 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); /* @@ */ + 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); +} /* **************************************************************** */ -/* */ -/* Conditional Handling */ -/* */ +/* */ +/* Conditional Handling */ +/* */ /* **************************************************************** */ /* A structure which contains `defined' variables. */ typedef struct defines { struct defines *next; char *name; char *value; } DEFINE; /* The linked list of `set' defines. */ DEFINE *defines = (DEFINE *)NULL; /* Add NAME to the list of `set' defines. */ void set (name, value) char *name; char *value; { DEFINE *temp; for (temp = defines; temp; temp = temp->next) if (strcmp (name, temp->name) == 0) { - free (temp->value); - temp->value = strdup (value); - return; + free (temp->value); + temp->value = xstrdup (value); + return; } temp = (DEFINE *)xmalloc (sizeof (DEFINE)); temp->next = defines; - temp->name = strdup (name); - temp->value = strdup (value); + temp->name = xstrdup (name); + temp->value = xstrdup (value); defines = temp; } /* Remove NAME from the list of `set' defines. */ void clear (name) char *name; { register DEFINE *temp, *last; last = (DEFINE *)NULL; temp = defines; while (temp) { if (strcmp (temp->name, name) == 0) - { - if (last) - last->next = temp->next; - else - defines = temp->next; - - free (temp->name); - free (temp->value); - free (temp); - break; - } + { + if (last) + last->next = temp->next; + else + defines = temp->next; + + free (temp->name); + free (temp->value); + free (temp); + break; + } last = temp; temp = temp->next; } } /* Return the value of NAME. The return value is NULL if NAME is unset. */ char * set_p (name) char *name; { register DEFINE *temp; for (temp = defines; temp; temp = temp->next) if (strcmp (temp->name, name) == 0) 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); } /* Create a variable whose name appears as the first word on this line. */ void cm_set () { handle_variable (SET); } /* Remove a variable whose name appears as the first word on this line. */ void cm_clear () { handle_variable (CLEAR); } void cm_ifset () { handle_variable (IFSET); } void cm_ifclear () { handle_variable (IFCLEAR); } /* This command takes braces, but we parse the contents specially, so we don't use the standard brace popping code. The syntax @ifeq{arg1, arg2, texinfo-commands} performs texinfo-commands if ARG1 and ARG2 caselessly string compare to the same string, otherwise, it produces no output. */ void cm_ifeq () { - register int i; char **arglist; arglist = get_brace_args (0); if (arglist) { if (array_len (arglist) > 1) - { - if ((strcasecmp (arglist[0], arglist[1]) == 0) && - (arglist[2] != (char *)NULL)) - execute_string ("%s\n", arglist[2]); - } + { + if ((strcasecmp (arglist[0], arglist[1]) == 0) && + (arglist[2] != (char *)NULL)) + execute_string ("%s\n", arglist[2]); + } free_array (arglist); } } void cm_value (arg, start_pos, end_pos) int arg, start_pos, end_pos; { if (arg == END) { - char *name, *value; - name = (char *)&output_paragraph[start_pos]; - output_paragraph[end_pos] = '\0'; - name = strdup (name); + char *name = (char *) &output_paragraph[start_pos]; + char *value; + output_paragraph[end_pos] = 0; + name = xstrdup (name); value = set_p (name); output_column -= end_pos - start_pos; output_paragraph_offset = start_pos; 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); } } /* Set, clear, or conditionalize based on ACTION. */ void handle_variable (action) int action; { char *name; get_rest_of_line (&name); backup_input_pointer (); - canon_white (name); handle_variable_internal (action, name); free (name); } void handle_variable_internal (action, name) int action; char *name; { char *temp; int delimiter, additional_text_present = 0; /* Only the first word of NAME is a valid tag. */ temp = name; delimiter = 0; while (*temp && (delimiter || !whitespace (*temp))) { /* #if defined (SET_WITH_EQUAL) */ if (*temp == '"' || *temp == '\'') - { - if (*temp == delimiter) - delimiter = 0; - else - delimiter = *temp; - } + { + if (*temp == delimiter) + delimiter = 0; + else + delimiter = *temp; + } /* #endif SET_WITH_EQUAL */ temp++; } if (*temp) additional_text_present++; - *temp = '\0'; + *temp = 0; if (!*name) - line_error ("%c%s requires a name", COMMAND_PREFIX, command); + line_error (_("%c%s requires a name"), COMMAND_PREFIX, command); else { switch (action) - { - case SET: - { - char *value; + { + case SET: + { + char *value; #if defined (SET_WITH_EQUAL) - /* Allow a value to be saved along with a variable. The value is - the text following an `=' sign in NAME, if any is present. */ + /* Allow a value to be saved along with a variable. The value is + the text following an `=' sign in NAME, if any is present. */ - for (value = name; *value && *value != '='; value++); + for (value = name; *value && *value != '='; value++); - if (*value) - *value++ = '\0'; + if (*value) + *value++ = 0; - if (*value == '"' || *value == '\'') - { - value++; - value[strlen (value) - 1] = '\0'; - } + if (*value == '"' || *value == '\'') + { + value++; + value[strlen (value) - 1] = 0; + } #else /* !SET_WITH_EQUAL */ - /* The VALUE of NAME is the remainder of the line sans - whitespace. */ - if (additional_text_present) - { - value = temp + 1; - canon_white (value); - } - else - value = ""; + /* The VALUE of NAME is the remainder of the line sans + whitespace. */ + if (additional_text_present) + { + value = temp + 1; + canon_white (value); + } + else + value = ""; #endif /* !SET_WITH_VALUE */ - set (name, value); - } - break; - - case CLEAR: - clear (name); - break; - - case IFSET: - case IFCLEAR: - /* If IFSET and NAME is not set, or if IFCLEAR and NAME is set, - read lines from the the file until we reach a matching - "@end CONDITION". This means that we only take note of - "@ifset/clear" and "@end" commands. */ - { - char condition[8]; - int condition_len; - - if (action == IFSET) - strcpy (condition, "ifset"); - else - strcpy (condition, "ifclear"); - - condition_len = strlen (condition); - - if ((action == IFSET && !set_p (name)) || - (action == IFCLEAR && set_p (name))) - { - int level = 0, done = 0; - - while (!done) - { - char *freeable_line, *line; - - get_rest_of_line (&freeable_line); - - for (line = freeable_line; whitespace (*line); line++); - - if (*line == COMMAND_PREFIX && - (strncmp (line + 1, condition, condition_len) == 0)) - level++; - else if (strncmp (line, "@end", 4) == 0) - { - char *cname = line + 4; - char *temp; - - while (*cname && whitespace (*cname)) - cname++; - temp = cname; - - while (*temp && !whitespace (*temp)) - temp++; - *temp = '\0'; - - if (strcmp (cname, condition) == 0) - { - if (!level) - { - done = 1; - } - else - level--; - } - } - free (freeable_line); - } - /* 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. */ - break; - } - else - { - if (action == IFSET) - begin_insertion (ifset); - else - begin_insertion (ifclear); - } - } - break; - } + set (name, value); + } + break; + + case CLEAR: + clear (name); + break; + + case IFSET: + case IFCLEAR: + /* If IFSET and NAME is not set, or if IFCLEAR and NAME is set, + read lines from the the file until we reach a matching + "@end CONDITION". This means that we only take note of + "@ifset/clear" and "@end" commands. */ + { + char condition[8]; + int condition_len; + int orig_line_number = line_number; + + if (action == IFSET) + strcpy (condition, "ifset"); + else + strcpy (condition, "ifclear"); + + condition_len = strlen (condition); + + if ((action == IFSET && !set_p (name)) + || (action == IFCLEAR && set_p (name))) + { + int level = 0, done = 0; + + while (!done && input_text_offset < size_of_input_text) + { + char *freeable_line, *line; + + get_rest_of_line (&freeable_line); + + for (line = freeable_line; whitespace (*line); line++); + + if (*line == COMMAND_PREFIX && + (strncmp (line + 1, condition, condition_len) == 0)) + level++; + else if (strncmp (line, "@end", 4) == 0) + { + char *cname = line + 4; + char *temp; + + while (*cname && whitespace (*cname)) + cname++; + temp = cname; + + while (*temp && !whitespace (*temp)) + temp++; + *temp = 0; + + if (strcmp (cname, condition) == 0) + { + if (!level) + { + done = 1; + } + else + level--; + } + } + free (freeable_line); + } + + if (!done) + { + int save = line_number; + line_number = orig_line_number; + line_error (_("Reached eof before matching @end %s"), + 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. */ + break; + } + else + { + if (action == IFSET) + begin_insertion (ifset); + else + begin_insertion (ifclear); + } + } + break; + } } } /* Execution of random text not in file. */ typedef struct { - char *string; /* The string buffer. */ - int size; /* The size of the buffer. */ - int in_use; /* Non-zero means string currently in use. */ + char *string; /* The string buffer. */ + int size; /* The size of the buffer. */ + int in_use; /* Nonzero means string currently in use. */ } EXECUTION_STRING; static EXECUTION_STRING **execution_strings = (EXECUTION_STRING **)NULL; static int execution_strings_index = 0; static int execution_strings_slots = 0; EXECUTION_STRING * get_execution_string (initial_size) int initial_size; { register int i = 0; EXECUTION_STRING *es = (EXECUTION_STRING *)NULL; if (execution_strings) { for (i = 0; i < execution_strings_index; i++) - if (execution_strings[i] && (execution_strings[i]->in_use == 0)) - { - es = execution_strings[i]; - break; - } + if (execution_strings[i] && (execution_strings[i]->in_use == 0)) + { + es = execution_strings[i]; + break; + } } if (!es) { if (execution_strings_index + 1 >= execution_strings_slots) - { - execution_strings = (EXECUTION_STRING **)xrealloc - (execution_strings, - (execution_strings_slots += 3) * sizeof (EXECUTION_STRING *)); - for (; i < execution_strings_slots; i++) - execution_strings[i] = (EXECUTION_STRING *)NULL; - } + { + execution_strings = (EXECUTION_STRING **)xrealloc + (execution_strings, + (execution_strings_slots += 3) * sizeof (EXECUTION_STRING *)); + for (; i < execution_strings_slots; i++) + execution_strings[i] = (EXECUTION_STRING *)NULL; + } execution_strings[execution_strings_index] = - (EXECUTION_STRING *)xmalloc (sizeof (EXECUTION_STRING)); + (EXECUTION_STRING *)xmalloc (sizeof (EXECUTION_STRING)); es = execution_strings[execution_strings_index]; execution_strings_index++; es->size = 0; es->string = (char *)NULL; es->in_use = 0; } if (initial_size > es->size) { es->string = (char *) xrealloc (es->string, initial_size); es->size = initial_size; } return (es); } /* Execute the string produced by formatting the ARGs with FORMAT. This is like submitting a new file with @include. */ -#if defined (HAVE_VARARGS_H) && defined (HAVE_VSPRINTF) -void -execute_string (va_alist) - va_dcl -{ - EXECUTION_STRING *es; - char *temp_string; - char *format; - va_list args; - - es = get_execution_string (4000); - temp_string = es->string; - es->in_use = 1; - - va_start (args); - format = va_arg (args, char *); - vsprintf (temp_string, format, args); - va_end (args); - -#else /* !(HAVE_VARARGS_H && HAVE_VSPRINTF) */ - void -execute_string (format, arg1, arg2, arg3, arg4, arg5) - char *format; +#if defined (VA_FPRINTF) && __STDC__ +execute_string (char *format, ...) +#else +execute_string (format, va_alist) + char *format; + va_dcl +#endif { EXECUTION_STRING *es; char *temp_string; +#ifdef VA_FPRINTF + va_list ap; +#endif es = get_execution_string (4000); temp_string = es->string; es->in_use = 1; - sprintf (temp_string, format, arg1, arg2, arg3, arg4, arg5); - -#endif /* !(HAVE_VARARGS_H && HAVE_VSPRINTF) */ + VA_START (ap, format); +#ifdef VA_SPRINTF + VA_SPRINTF (temp_string, format, ap); +#else + sprintf (temp_string, format, a1, a2, a3, a4, a5, a6, a7, a8); +#endif /* not VA_SPRINTF */ + va_end (ap); pushfile (); input_text_offset = 0; input_text = temp_string; - input_filename = strdup (input_filename); + input_filename = xstrdup (input_filename); size_of_input_text = strlen (temp_string); executing_string++; reader_loop (); free (input_filename); popfile (); executing_string--; es->in_use = 0; } /* Return what would be output for STR, i.e., expand Texinfo commands. If IMPLICIT_CODE is set, expand @code{STR}. */ char * expansion (str, implicit_code) char *str; int implicit_code; { int length; char *result; /* Inhibit any real output. */ int start = output_paragraph_offset; int saved_paragraph_is_open = paragraph_is_open; inhibit_output_flushing (); + paragraph_is_open = 1; execute_string (implicit_code ? "@code{%s}" : "%s", str); uninhibit_output_flushing (); /* Copy the expansion from the buffer. */ length = output_paragraph_offset - start; result = xmalloc (1 + length); memcpy (result, (char *) (output_paragraph + start), length); result[length] = 0; /* Pretend it never happened. */ output_paragraph_offset = start; paragraph_is_open = saved_paragraph_is_open; 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); - canon_white (rest_of_line); item_func = current_item_function (); /* Okay, do the right thing depending on which insertion function - is active. */ + 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 ignore you until - the start_paragraph () function has been called. */ - must_start_paragraph = 1; - - /* 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 table: - case ftable: - case vtable: - { - /* Get rid of extra characters. */ - kill_self_indent (-1); - - /* 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 (); + { + 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 t; - - t = 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 = t; - } + /* 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'); + 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); - - 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; - } - } + 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); + line_error (_("%c%s found outside of an insertion block"), + COMMAND_PREFIX, command); } } /* **************************************************************** */ -/* */ -/* Defun and Friends */ -/* */ +/* */ +/* Defun and Friends */ +/* */ /* **************************************************************** */ -#define DEFUN_SELF_DELIMITING(c) \ - (((c) == '(') \ - || ((c) == ')') \ - || ((c) == '[') \ +#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->length * sizeof (char *))); } accumulator->tokens[accumulator->index] = token; accumulator->index += 1; } char * copy_substring (start, end) char *start; char *end; { 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'; + *scan_result = 0; return (result); } /* 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); - } + { + (*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 == 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; + level += 1; if (c == '}') - level -= 1; + 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') + 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), (strdup (" "))); - continue; - } + { + 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; - } + { + 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; - } + { + 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); + { + 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; - } + 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. */ + brackets, or braces. A token is also ended by a command. */ else - { - token_start = scan_string; + { + token_start = scan_string; - while (1) - { - register int c; + while (1) + { + register int c; - c = *scan_string++; + 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; - } + 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)); + (&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; + break; if (defun_arg[0] == ' ') - { - pending_space = 1; - continue; - } + { + pending_space = 1; + continue; + } if (pending_space) - { - add_char (' '); - pending_space = 0; - } + { + add_char (' '); + pending_space = 0; + } if (DEFUN_SELF_DELIMITING (defun_arg[0])) - add_char (defun_arg[0]); + add_char (defun_arg[0]); else if (defun_arg[0] == '&') - add_word (defun_arg); + add_word (defun_arg); else if (defun_arg[0] == COMMAND_PREFIX) - execute_string ("%s", defun_arg); + execute_string ("%s", defun_arg); else if (auto_var_p) - execute_string ("%cvar{%s}", COMMAND_PREFIX, defun_arg); + execute_string ("%cvar{%s}", COMMAND_PREFIX, defun_arg); else - add_word (defun_arg); + 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 says not to start a new insertion if non-zero. */ + 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"; + category = _("Function"); base_type = deffn; break; case defmac: - category = "Macro"; + category = _("Macro"); base_type = deffn; break; case defspec: - category = "Special Form"; + category = _("Special Form"); base_type = deffn; break; case defvar: - category = "Variable"; + category = _("Variable"); base_type = defvr; break; case defopt: - category = "User Option"; + category = _("User Option"); base_type = defvr; break; case deftypefun: - category = "Function"; + category = _("Function"); base_type = deftypefn; break; case deftypevar: - category = "Variable"; + category = _("Variable"); base_type = deftypevr; break; case defivar: - category = "Instance Variable"; + category = _("Instance Variable"); base_type = defcv; break; case defmethod: - category = "Method"; + category = _("Method"); base_type = defop; break; case deftypemethod: - category = "Method"; + 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); + 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; - case deftp: - case deftypefn: - case deftypemethod: + + /* 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 (); - if (!macro_expansion_output_stream) - /* Make an entry in the appropriate index unless we are just - expanding macros. */ - 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; - } + /* 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; + 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 = strdup (command); + char *temp = xstrdup (command); x_p = (command[strlen (command) - 1] == 'x'); if (x_p) - temp[strlen (temp) - 1] = '\0'; + 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); + 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); } -/* 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); - canon_white (temp); - - if (strlen (temp) == 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); -} - /* **************************************************************** */ -/* */ -/* Other Random Commands */ -/* */ +/* */ +/* 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); + line_error (_("%csp requires a positive numeric argument"), COMMAND_PREFIX); } else { if (lines < 0) - lines = 0; + lines = 0; while (lines--) - add_char ('\n'); + add_char ('\n'); } free (line); } /* @dircategory LINE outputs INFO-DIR-SECTION LINE, but not if --no-headers. */ void cm_dircategory () { - char *line, *p; + char *line; get_rest_of_line (&line);; - if (! no_headers) + 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--; + 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 (' '); + insert (' '); for (i = 0; i < length; i++) - insert (line[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 (); } -#if !defined (HAVE_STRERROR) -extern char *sys_errlist[]; -extern int sys_nerr; - -char * -strerror (num) - int num; -{ - if (num >= sys_nerr) - return ("Unknown file system error"); - else - return (sys_errlist[num]); -} -#endif /* !HAVE_STRERROR */ /* Remember this file, and move onto the next. */ void cm_include () { char *filename; #if defined (HAVE_MACROS) - if (macro_expansion_output_stream) + 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) + 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)); + 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) - remember_itext (input_text, input_text_offset); + 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 `}'"); + 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. + `none': Remove existing indentation. + NUM: Indent NUM spaces at the starts of paragraphs. + If NUM is zero, we assume `none'. + Returns 0 if successful, or nonzero if STRING isn't one of the above. */ +int +set_paragraph_indent (string) + char *string; +{ + if (strcmp (string, "asis") == 0 || strcmp (string, _("asis")) == 0) + paragraph_start_indent = 0; + else if (strcmp (string, "none") == 0 || strcmp (string, _("none")) == 0) + paragraph_start_indent = -1; + else + { + if (sscanf (string, "%d", ¶graph_start_indent) != 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 */ -/* */ +/* */ +/* 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; /* Non-zero means add `@code{...}' when - printing this element. */ - int defining_line; /* Line number where this entry was written. */ + 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, and the index to that index in our - index array, the_indices. 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. */ +/* 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 index; + 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; -/* We predefine these. */ -#define program_index 0 -#define function_index 1 -#define concept_index 2 -#define variable_index 3 -#define datatype_index 4 -#define key_index 5 - 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 *)); + sizeof (INDEX_ELT *)); the_indices[defined_indices] = (INDEX_ELT *) NULL; name_index_alist = (INDEX_ALIST **) xmalloc ((1 + defined_indices) * - sizeof (INDEX_ALIST *)); + 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); + { + 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 ("pg", 0); - top_defindex ("fn", 1); /* "fn" is a code index. */ - top_defindex ("cp", 0); - top_defindex ("vr", 0); - top_defindex ("tp", 0); - top_defindex ("ky", 0); - + 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 (name_index_alist[i]->index); + 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->index); + 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); - free (temp->node); + /* 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. */ +/* 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); - if (which < 0) + /* 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]->index; + 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 non-zero says to make this a code index. */ + 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; + 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 *)); + 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 *)); + 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 = strdup (name); - name_index_alist[slot]->index = slot; + 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->index : -1; + which = tem ? tem->write_index : -1; #if defined (HAVE_MACROS) - if (macro_expansion_output_stream) + 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) + 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 reference `%s'", name); + 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 = strdup (command); + char *name = xstrdup (command); if (strlen (name) >= strlen ("index")) - name[strlen (name) - strlen ("index")] = '\0'; + 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); + 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); } } -/* Append LIST2 to LIST1. Return the head of the list. */ -INDEX_ELT * -index_append (head, tail) - INDEX_ELT *head, *tail; -{ - register INDEX_ELT *t_head = head; - - if (!t_head) - return (tail); - - while (t_head->next) - t_head = t_head->next; - t_head->next = tail; - return (head); -} - /* 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 redirector, redirectee; - char *temp; + int source, target; + char *abbrev1, *abbrev2; skip_whitespace (); - get_until_in_line (" ", &temp); - redirectee = find_index_offset (temp); + get_until_in_line (0, " ", &abbrev1); + target = find_index_offset (abbrev1); skip_whitespace (); - free_and_clear (&temp); - get_until_in_line (" ", &temp); - redirector = find_index_offset (temp); - free (temp); - if (redirector < 0 || redirectee < 0) + get_until_in_line (0, " ", &abbrev2); + source = find_index_offset (abbrev2); + if (source < 0 || target < 0) { - line_error ("Unknown index reference"); + line_error (_("Unknown index `%s' and/or `%s' in @synindex"), + abbrev1, abbrev2); } else { - /* I think that we should let the user make indices synonymous to - each other without any lossage of info. This means that one can - say @synindex cp dt anywhere in the file, and things that used to - be in cp will go into dt. */ - INDEX_ELT *i1 = the_indices[redirectee], *i2 = the_indices[redirector]; - - if (i1 || i2) - { - if (i1) - the_indices[redirectee] = index_append (i1, i2); - else - the_indices[redirectee] = index_append (i2, i1); - } - - name_index_alist[redirectee]->index = - name_index_alist[redirector]->index; + name_index_alist[target]->write_index + = name_index_alist[source]->write_index; } + + free (abbrev1); + free (abbrev2); } void -cm_pindex () /* Pinhead index. */ +cm_pindex () /* Pinhead index. */ { index_add_arg ("pg"); } void -cm_vindex () /* Variable index. */ +cm_vindex () /* Variable index. */ { index_add_arg ("vr"); } void -cm_kindex () /* Key index. */ +cm_kindex () /* Key index. */ { index_add_arg ("ky"); } void -cm_cindex () /* Concept index. */ +cm_cindex () /* Concept index. */ { index_add_arg ("cp"); } void -cm_findex () /* Function index. */ +cm_findex () /* Function index. */ { index_add_arg ("fn"); } void -cm_tindex () /* Data Type index. */ +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]; + (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]); - } + { + 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. */ + 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++; - } + (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; + 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 *temp = 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; - /* Maybe should set line number to the defining_line? Any errors - have already been given, though, I think. */ + /* 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{...}". */ + 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. */ + 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); } -/* Non-zero means that we are in the middle of printing an index. */ +/* 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 name `%s'", index_name); + 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"); + 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) + strlen (index_node); + 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 + 6; /* * : .\0 */ + 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. */ + +/* 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 *)); + (1 + user_command_array_len) * + sizeof (COMMAND *)); user_command_array[slot] = (COMMAND *) xmalloc (sizeof (COMMAND)); - user_command_array[slot]->name = strdup (name); + user_command_array[slot]->name = xstrdup (name); user_command_array[slot]->proc = proc; user_command_array[slot]->argument_in_braces = needs_braces_p; } - -/* Set the paragraph indentation variable to the value specified in STRING. - Values can be: - `asis': Don't change existing indentation. - `none': Remove existing indentation. - NUM: Indent NUM spaces at the starts of paragraphs. - Note that if NUM is zero, we assume `none'. - - Returns 0 if successful, or non-zero if STRING isn't one of the above. */ -int -set_paragraph_indent (string) - char *string; -{ - if (strcmp (string, "asis") == 0) - paragraph_start_indent = 0; - else if (strcmp (string, "none") == 0) - paragraph_start_indent = -1; - else - { - if (sscanf (string, "%d", ¶graph_start_indent) != 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); -} - + /* 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, + 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. */ + 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)) + (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 = strdup (marker); - temp->note = strdup (note); + 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 marker character for this note. */ + 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' expected more than just `%s'. It needs something in `{...}'", - COMMAND_PREFIX, command, marker); + line_error (_("`%c%s' needs an argument `{...}', not just `%s'"), + COMMAND_PREFIX, command, marker); free (marker); return; } else { - int braces = 1; - int temp = ++input_text_offset; int len; + int braces = 1; + int loc = ++input_text_offset; while (braces) - { - if (temp == size_of_input_text) - { - line_error ("No closing brace for footnote `%s'", marker); - return; - } - - if (input_text[temp] == '{') - braces++; - else if (input_text[temp] == '}') - braces--; - else if (input_text[temp] == '\n') - line_number ++; - - temp++; - } - - len = (temp - input_text_offset) - 1; + { + 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 = temp; + 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"); + 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++; - } + { + marker = (char *)xmalloc (10); + sprintf (marker, "%d", current_footnote_number); + current_footnote_number++; + } else - marker = strdup ("*"); + 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; - } + { + 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); } -/* Non-zero means that we are currently in the process of outputting +/* 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 = strdup (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; + 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"); + 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; + array[footnote_count] = footnote; + footnote = footnote->next; } filling_enabled = 1; indented_fill = 1; - while (footnote = array[++footnote_count]) + while ((footnote = array[++footnote_count])) { - - switch (footnote_style) - { - case SeparateNode: - case EndNode: - execute_string ("(%s) %s", footnote->marker, footnote->note); - close_paragraph (); - break; - } + execute_string ("(%s) %s", footnote->marker, footnote->note); + close_paragraph (); } close_paragraph (); free (array); } } /* **************************************************************** */ /* */ -/* User definable Macros (text substitution) */ +/* 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. */ +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; + 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_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 ("The macro `%s' is previously defined", name); + 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); + warning (_("here is the previous definition of `%s'"), name); input_filename = temp_filename; line_number = temp_line; if (def->arglist) - { - register int i; + { + register int i; - for (i = 0; def->arglist[i]; i++) - free (def->arglist[i]); + for (i = 0; def->arglist[i]; i++) + free (def->arglist[i]); - free (def->arglist); - } + free (def->arglist); + } free (def->source_file); free (def->body); } - def->source_file = strdup (source_file); + 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 *)); - break; + 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; + 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); - } + { + 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--; - /* 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); - } - } + { + /* 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; + break; if (body[i] != '\\') - new_body[new_body_index++] = 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]) - i++; - - /* Now check against named parameters. */ - for (which = 0; named && named[which]; which++) - if (strcmp (named[which], param) == 0) - break; - - if (named[which]) - { - if (which < length_of_actuals) - text = actuals[which]; - else - text = (char *)NULL; - - if (!text) - text = ""; - - len = strlen (text); - } - else - { - len += 2; - text = (char *)xmalloc (1 + len); - sprintf (text, "\\%s\\", param); - } - - if ((2 + strlen (param)) < len) - new_body = (char *)xrealloc - (new_body, new_body_size += (1 + len)); - - free (param); - - strcpy (new_body + new_body_index, text); - new_body_index += len; - - if (!named[which]) - free (text); - } - } - new_body[new_body_index] = '\0'; + { + /* 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; { - register int i; char **arglist; int num_args; char *execution_string = (char *)NULL; - if (macro_expansion_output_stream && !me_inhibit_expansion) + 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); + 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 && !me_inhibit_expansion) - { - remember_itext (input_text, input_text_offset); - me_execute_string (execution_string); - } + 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); + 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) + 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++); + (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'; + 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. */ + 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; - } - } - } + { + 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; - } + { + 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; - } - } + (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); - } - } + (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++; + (strncmp (line + 1, "macro ", 6) == 0)) + depth++; if ((*line == COMMAND_PREFIX) && - (strncmp (line + 1, "end macro", 9) == 0)) - depth--; + (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'; - } + { + 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'; + body[strlen (body) - 1] = 0; add_macro (name, arglist, body, input_filename, defining_line, flags); - if (macro_expansion_output_stream) + 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) + if (macro_expansion_output_stream && !executing_string) me_append_before_this_command (); get_rest_of_line (&line); - canon_white (line); for (i = 0; line[i] && !whitespace (line[i]); i++); - name = (char *)xmalloc (i); + name = (char *)xmalloc (i + 1); strncpy (name, line, i); - name[i] = '\0'; + 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; + { + register int i; - for (i = 0; def->arglist[i]; i++) - free (def->arglist[i]); + for (i = 0; def->arglist[i]; i++) + free (def->arglist[i]); - free (def->arglist); - } + free (def->arglist); + } free (def); } free (line); free (name); - if (macro_expansion_output_stream) + 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; + 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_info[i]->pointer == pointer)) { - itext = itext_info[i]; - itext_info[i]->offset = offset; - break; + 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. */ + /* 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 (itext_info[i] == (ITEXT *)NULL) + break; /* If not found, then add some slots. */ if (i == itext_size) - { - register int j; + { + register int j; - itext_info = (ITEXT **)xrealloc - (itext_info, (itext_size += 10) * sizeof (ITEXT *)); + itext_info = (ITEXT **)xrealloc + (itext_info, (itext_size += 10) * sizeof (ITEXT *)); - for (j = i; j < itext_size; j++) - itext_info[j] = (ITEXT *)NULL; - } + 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; + 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 = strdup (input_filename); + input_filename = xstrdup (input_filename); size_of_input_text = strlen (execution_string); remember_itext (execution_string, 0); - executing_string++; + me_executing_string++; reader_loop (); popfile (); - executing_string--; + 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; + itext = itext_info[i]; + break; } if (!itext) - itext = remember_itext (input_text, 0); + return; if (offset > itext->offset) { write_region_to_macro_output - (input_text, itext->offset, offset); + (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; + 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[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 ()) + while ((character = curchar ())) { if (character == '\\') - { - input_text_offset += 2; - escape_seen = 1; - } + { + input_text_offset += 2; + escape_seen = 1; + } else if (character == '{') - { - depth++; - input_text_offset++; - } + { + 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; - } + ((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++; - } + { + depth--; + input_text_offset++; + } else - { - input_text_offset++; - if (character == '\n') line_number++; - } + { + 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)++; + (*index)++; /* Return "" in the case of a trailing `:'. */ - return (strdup ("")); + return (xstrdup ("")); } else { char *value; value = (char *)xmalloc (1 + (i - start)); strncpy (value, &string[start], (i - start)); - value [i - start] = '\0'; + 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] == '/')))) + (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 (strdup (filename)); + return (xstrdup (filename)); else - return ((char *)NULL); + return ((char *)NULL); } - while (dir = extract_colon_unit (path, &index)) + while ((dir = extract_colon_unit (path, &index))) { char *fullpath; if (!*dir) - { - free (dir); - dir = strdup ("."); - } + { + 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); + return (fullpath); else - free (fullpath); + free (fullpath); } - return ((char *)NULL); + return NULL; } diff --git a/contrib/texinfo/util/install-info.c b/contrib/texinfo/util/install-info.c index b6a7a919e6c7..afa9a68c34ee 100644 --- a/contrib/texinfo/util/install-info.c +++ b/contrib/texinfo/util/install-info.c @@ -1,1141 +1,1234 @@ /* install-info -- create Info directory entry(ies) for an Info file. - Copyright (C) 1996 Free Software Foundation, Inc. + $Id: install-info.c,v 1.21 1998/03/01 15:38:45 karl Exp $ -$Id: install-info.c,v 1.12 1996/10/03 23:13:36 karl Exp $ + Copyright (C) 1996, 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 -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/ -#define INSTALL_INFO_VERSION_STRING "GNU install-info (Texinfo 3.9) 1.2" - -#include -#include +#include "system.h" #include -#include - -/* Get O_RDONLY. */ -#ifdef HAVE_SYS_FCNTL_H -#include -#else -#include -#endif /* !HAVE_SYS_FCNTL_H */ -#ifdef HAVE_SYS_FILE_H -#include + +#ifdef HAVE_LIBZ +#include #endif /* Name this program was invoked with. */ char *progname; char *readfile (); struct line_data *findlines (); void fatal (); void insert_entry_here (); int compare_section_names (); struct spec_entry; /* Data structures. */ -/* Record info about a single line from a file - as read into core. */ +/* Record info about a single line from a file as read into core. */ struct line_data { /* The start of the line. */ char *start; /* The number of characters in the line, excluding the terminating newline. */ int size; /* Vector containing pointers to the entries to add before this line. The vector is null-terminated. */ struct spec_entry **add_entries_before; /* 1 means output any needed new sections before this line. */ int add_sections_before; /* 1 means don't output this line. */ int delete; }; + /* This is used for a list of the specified menu section names in which entries should be added. */ - struct spec_section { struct spec_section *next; char *name; /* 1 means we have not yet found an existing section with this name in the dir file--so we will need to add a new section. */ int missing; }; -/* This is used for a list of the entries specified to be added. */ +/* This is used for a list of the entries specified to be added. */ struct spec_entry { struct spec_entry *next; char *text; }; - -/* This is used for a list of nodes found by parsing the dir file. */ + +/* This is used for a list of nodes found by parsing the dir file. */ struct node { struct node *next; /* The node name. */ char *name; /* The line number of the line where the node starts. This is the line that contains control-underscore. */ int start_line; /* The line number of the line where the node ends, which is the end of the file or where the next line starts. */ int end_line; /* Start of first line in this node's menu (the line after the * Menu: line). */ char *menu_start; /* The start of the chain of sections in this node's menu. */ struct menu_section *sections; /* The last menu section in the chain. */ struct menu_section *last_section; }; + /* This is used for a list of sections found in a node's menu. Each struct node has such a list in the sections field. */ - struct menu_section { struct menu_section *next; char *name; /* Line number of start of section. */ int start_line; /* Line number of end of section. */ int end_line; }; /* Memory allocation and string operations. */ /* Like malloc but get fatal error if memory is exhausted. */ - void * xmalloc (size) unsigned int size; { extern void *malloc (); void *result = malloc (size); if (result == NULL) - fatal ("virtual memory exhausted", 0); + fatal (_("virtual memory exhausted"), 0); return result; } -/* Like malloc but get fatal error if memory is exhausted. */ - +/* Like realloc but get fatal error if memory is exhausted. */ void * xrealloc (obj, size) void *obj; unsigned int size; { extern void *realloc (); void *result = realloc (obj, size); if (result == NULL) - fatal ("virtual memory exhausted", 0); + fatal (_("virtual memory exhausted"), 0); return result; } -/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */ - +/* Return a newly-allocated string + whose contents concatenate those of S1, S2, S3. */ char * concat (s1, s2, s3) char *s1, *s2, *s3; { int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3); char *result = (char *) xmalloc (len1 + len2 + len3 + 1); strcpy (result, s1); strcpy (result + len1, s2); strcpy (result + len1 + len2, s3); *(result + len1 + len2 + len3) = 0; return result; } /* Return a string containing SIZE characters copied from starting at STRING. */ char * copy_string (string, size) char *string; int size; { int i; char *copy = (char *) xmalloc (size + 1); for (i = 0; i < size; i++) copy[i] = string[i]; copy[size] = 0; return copy; } /* Error message functions. */ -/* Print error message. `s1' is printf control string, `s2' is arg for it. */ +/* Print error message. S1 is printf control string, S2 and S3 args for it. */ /* VARARGS1 */ void error (s1, s2, s3) char *s1, *s2, *s3; { fprintf (stderr, "%s: ", progname); fprintf (stderr, s1, s2, s3); - fprintf (stderr, "\n"); + putc ('\n', stderr); } /* VARARGS1 */ void warning (s1, s2, s3) char *s1, *s2, *s3; { - fprintf (stderr, "%s: Warning: ", progname); + fprintf (stderr, _("%s: warning: "), progname); fprintf (stderr, s1, s2, s3); - fprintf (stderr, "\n"); + putc ('\n', stderr); } /* Print error message and exit. */ void fatal (s1, s2, s3) char *s1, *s2, *s3; { error (s1, s2, s3); exit (1); } /* Print fatal error message based on errno, with file name NAME. */ void pfatal_with_name (name) char *name; { - char *s = concat ("", strerror (errno), " for %s"); + char *s = concat ("", strerror (errno), _(" for %s")); fatal (s, name); } /* Given the full text of a menu entry, null terminated, return just the menu item name (copied). */ char * extract_menu_item_name (item_text) char *item_text; { char *p; if (*item_text == '*') item_text++; while (*item_text == ' ') item_text++; p = item_text; while (*p && *p != ':') p++; return copy_string (item_text, p - item_text); } /* Given the full text of a menu entry, terminated by null or newline, return just the menu item file (copied). */ char * extract_menu_file_name (item_text) char *item_text; { char *p = item_text; /* If we have text that looks like * ITEM: (FILE)NODE..., extract just FILE. Otherwise return "(none)". */ if (*p == '*') p++; while (*p == ' ') p++; /* Skip to and past the colon. */ while (*p && *p != '\n' && *p != ':') p++; if (*p == ':') p++; /* Skip past the open-paren. */ while (1) { if (*p == '(') break; else if (*p == ' ' || *p == '\t') p++; else return "(none)"; } p++; item_text = p; /* File name ends just before the close-paren. */ while (*p && *p != '\n' && *p != ')') p++; if (*p != ')') return "(none)"; return copy_string (item_text, p - item_text); } void suggest_asking_for_help () { - fprintf (stderr, "\tTry `%s --help' for a complete list of options.\n", + fprintf (stderr, _("\tTry `%s --help' for a complete list of options.\n"), progname); exit (1); } void print_help () { - printf ("%s [OPTION]... [INFO-FILE [DIR-FILE]]\n\ - Install INFO-FILE in the Info directory file DIR-FILE.\n\ + printf (_("Usage: %s [OPTION]... [INFO-FILE [DIR-FILE]]\n\ +\n\ +Install INFO-FILE in the Info directory file DIR-FILE.\n\ \n\ Options:\n\ --delete Delete existing entries in INFO-FILE;\n\ don't insert any new entries.\n\ --defentry=TEXT Like --entry, but only use TEXT if an entry\n\ is not present in INFO-FILE.\n\ --defsection=TEXT Like --section, but only use TEXT if a section\n\ is not present in INFO-FILE.\n\ --dir-file=NAME Specify file name of Info directory file.\n\ This is equivalent to using the DIR-FILE argument.\n\ --entry=TEXT Insert TEXT as an Info directory entry.\n\ TEXT should have the form of an Info menu item line\n\ plus zero or more extra lines starting with whitespace.\n\ If you specify more than one entry, they are all added.\n\ If you don't specify any entries, they are determined\n\ from information in the Info file itself.\n\ --forceentry=TEXT Like --entry, but ignore any entry in INFO-FILE.\n\ --help Display this help and exit.\n\ --info-file=FILE Specify Info file to install in the directory.\n\ This is equivalent to using the INFO-FILE argument.\n\ --info-dir=DIR Same as --dir-file=DIR/dir.\n\ --item=TEXT Same as --entry TEXT.\n\ An Info directory entry is actually a menu item.\n\ --quiet Suppress warnings.\n\ --remove Same as --delete.\n\ --section=SEC Put this file's entries in section SEC of the directory.\n\ If you specify more than one section, all the entries\n\ are added in each of the sections.\n\ If you don't specify any sections, they are determined\n\ from information in the Info file itself.\n\ --version Display version information and exit.\n\ \n\ -Email bug reports to bug-texinfo@prep.ai.mit.edu.\n\ -", progname); +Email bug reports to bug-texinfo@gnu.org.\n\ +"), progname); } + +/* If DIRFILE does not exist, create a minimal one (or abort). If it + already exists, do nothing. */ + +void +ensure_dirfile_exists (dirfile) + char *dirfile; +{ + int desc = open (dirfile, O_RDONLY); + if (desc < 0 && errno == ENOENT) + { + FILE *f; + char *readerr = strerror (errno); + close (desc); + f = fopen (dirfile, "w"); + if (f) + { + fputs (_("This is the file .../info/dir, which contains the\n\ +topmost node of the Info hierarchy, called (dir)Top.\n\ +The first time you invoke Info you start off looking at this node.\n\ +\n\ +File: dir,\tNode: Top,\tThis is the top of the INFO tree\n\ +\n\ + This (the Directory node) gives a menu of major topics.\n\ + Typing \"q\" exits, \"?\" lists all Info commands, \"d\" returns here,\n\ + \"h\" gives a primer for first-timers,\n\ + \"mEmacs\" visits the Emacs manual, etc.\n\ +\n\ + In Emacs, you can click mouse button 2 on a menu item or cross reference\n\ + to select it.\n\ +\n\ +* Menu:\n\ +"), f); + if (fclose (f) < 0) + pfatal_with_name (dirfile); + } + else + { + /* Didn't exist, but couldn't open for writing. */ + fprintf (stderr, + _("%s: could not read (%s) and could not create (%s)\n"), + dirfile, readerr, strerror (errno)); + exit (1); + } + } + else + close (desc); /* It already existed, so fine. */ +} /* This table defines all the long-named options, says whether they use an argument, and maps them into equivalent single-letter options. */ struct option longopts[] = { - { "delete", no_argument, NULL, 'r' }, - { "defentry", required_argument, NULL, 'E' }, - { "defsection", required_argument, NULL, 'S' }, - { "dir-file", required_argument, NULL, 'd' }, - { "entry", required_argument, NULL, 'e' }, - { "forceentry", required_argument, NULL, 'f' }, - { "help", no_argument, NULL, 'h' }, - { "info-dir", required_argument, NULL, 'D' }, - { "info-file", required_argument, NULL, 'i' }, - { "item", required_argument, NULL, 'e' }, - { "quiet", no_argument, NULL, 'q' }, - { "remove", no_argument, NULL, 'r' }, - { "section", required_argument, NULL, 's' }, - { "version", no_argument, NULL, 'V' }, + { "delete", no_argument, NULL, 'r' }, + { "dir-file", required_argument, NULL, 'd' }, + { "entry", required_argument, NULL, 'e' }, + { "help", no_argument, NULL, 'h' }, + { "info-dir", required_argument, NULL, 'D' }, + { "info-file", required_argument, NULL, 'i' }, + { "item", required_argument, NULL, 'e' }, + { "quiet", no_argument, NULL, 'q' }, + { "remove", no_argument, NULL, 'r' }, + { "section", required_argument, NULL, 's' }, + { "version", no_argument, NULL, 'V' }, { 0 } }; + +int main (argc, argv) int argc; char **argv; { char *infile = 0, *dirfile = 0; char *infile_sans_info; unsigned infilelen_sans_info; FILE *output; /* Record the text of the Info file, as a sequence of characters and as a sequence of lines. */ char *input_data; int input_size; struct line_data *input_lines; int input_nlines; /* Record here the specified section names and directory entries. */ struct spec_section *input_sections = NULL; struct spec_entry *entries_to_add = NULL; int n_entries_to_add = 0; /* Record the old text of the dir file, as plain characters, as lines, and as nodes. */ char *dir_data; int dir_size; int dir_nlines; struct line_data *dir_lines; struct node *dir_nodes; /* Nonzero means --delete was specified (just delete existing entries). */ int delete_flag = 0; int something_deleted = 0; /* Nonzero means -q was specified. */ int quiet_flag = 0; int node_header_flag; int prefix_length; int i; /* Nonzero means only use if not present in info file. */ int entry_default = 0; int entry_force = 0; int section_default = 0; progname = argv[0]; +#ifdef HAVE_SETLOCALE + /* Set locale via LC_ALL. */ + setlocale (LC_ALL, ""); +#endif + + /* Set the text message domain. */ + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + while (1) { int opt = getopt_long (argc, argv, "i:d:e:s:hHr", longopts, 0); if (opt == EOF) break; switch (opt) { case 0: /* If getopt returns 0, then it has already processed a long-named option. We should do nothing. */ break; case 1: abort (); case 'd': if (dirfile) { - fprintf (stderr, "%s: Specify the Info directory only once.\n", + fprintf (stderr, _("%s: Specify the Info directory only once.\n"), progname); suggest_asking_for_help (); } dirfile = optarg; break; case 'D': if (dirfile) { - fprintf (stderr, "%s: Specify the Info directory only once.\n", + fprintf (stderr, _("%s: Specify the Info directory only once.\n"), progname); suggest_asking_for_help (); } dirfile = concat (optarg, "", "/dir"); break; case 'f': entry_force = 1; if (!optarg[0]) { fprintf (stderr, "%s: Must provide entry name.\n", progname); suggest_asking_for_help (); } case 'E': entry_default = 1; if (!optarg[0]) break; case 'e': { struct spec_entry *next = (struct spec_entry *) xmalloc (sizeof (struct spec_entry)); if (! (*optarg != 0 && optarg[strlen (optarg) - 1] == '\n')) optarg = concat (optarg, "\n", ""); next->text = optarg; next->next = entries_to_add; entries_to_add = next; n_entries_to_add++; } break; case 'h': case 'H': print_help (); exit (0); case 'i': if (infile) { - fprintf (stderr, "%s: Specify the Info file only once.\n", + fprintf (stderr, _("%s: Specify the Info file only once.\n"), progname); suggest_asking_for_help (); } infile = optarg; break; case 'q': quiet_flag = 1; break; case 'r': delete_flag = 1; break; case 'S': section_default = 1; if (!optarg[0]) break; case 's': { struct spec_section *next = (struct spec_section *) xmalloc (sizeof (struct spec_section)); next->name = optarg; next->next = input_sections; next->missing = 1; input_sections = next; } break; case 'V': - puts (INSTALL_INFO_VERSION_STRING); -puts ("Copyright (C) 1996 Free Software Foundation, Inc.\n\ + printf ("install-info (GNU %s) %s\n", PACKAGE, VERSION); + 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."); - exit (0); +For more information about these matters, see the files named COPYING.\n"), + "1998"); + exit (0); default: suggest_asking_for_help (); } } if (entry_force) entry_default = 0; /* Interpret the non-option arguments as file names. */ for (; optind < argc; ++optind) { if (infile == 0) infile = argv[optind]; else if (dirfile == 0) dirfile = argv[optind]; else - error ("excess command line argument `%s'", argv[optind]); + error (_("excess command line argument `%s'"), argv[optind]); } if (!infile) - fatal ("No input file specified"); + fatal (_("No input file specified; try --help for more information.")); if (!dirfile) - fatal ("No dir file specified"); + fatal (_("No dir file specified; try --help for more information.")); /* Read the Info file and parse it into lines. */ input_data = readfile (infile, &input_size); input_lines = findlines (input_data, input_size, &input_nlines); /* Parse the input file to find the section names it specifies. */ if (input_sections == 0 || section_default) { prefix_length = strlen ("INFO-DIR-SECTION "); for (i = 0; i < input_nlines; i++) { if (!strncmp ("INFO-DIR-SECTION ", input_lines[i].start, prefix_length)) { struct spec_section *next = (struct spec_section *) xmalloc (sizeof (struct spec_section)); if (section_default) { input_sections = NULL; /* This leaks. */ section_default = 0; } next->name = copy_string (input_lines[i].start + prefix_length, input_lines[i].size - prefix_length); next->next = input_sections; next->missing = 1; input_sections = next; } } } /* Default to section "Miscellaneous" if no sections specified. */ if (input_sections == 0) { input_sections = (struct spec_section *) xmalloc (sizeof (struct spec_section)); input_sections->name = "Miscellaneous"; input_sections->next = 0; input_sections->missing = 1; } /* Now find the directory entries specified in the file and put them on entries_to_add. But not if entries were specified explicitly with command options. */ if ( !entry_force && (entries_to_add == 0 || entry_default) ) { char *start_of_this_entry = 0; for (i = 0; i < input_nlines; i++) { if (!strncmp ("START-INFO-DIR-ENTRY", input_lines[i].start, input_lines[i].size) && sizeof ("START-INFO-DIR-ENTRY") - 1 == input_lines[i].size) { if (start_of_this_entry != 0) - fatal ("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"); + fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY")); start_of_this_entry = input_lines[i + 1].start; } if (!strncmp ("END-INFO-DIR-ENTRY", input_lines[i].start, input_lines[i].size) && sizeof ("END-INFO-DIR-ENTRY") - 1 == input_lines[i].size) { if (start_of_this_entry != 0) { struct spec_entry *next = (struct spec_entry *) xmalloc (sizeof (struct spec_entry)); if (entry_default) { entries_to_add = NULL; entry_default = 0; } next->text = copy_string (start_of_this_entry, input_lines[i].start - start_of_this_entry); next->next = entries_to_add; entries_to_add = next; n_entries_to_add++; start_of_this_entry = 0; } else - fatal ("END-INFO-DIR-ENTRY without matching START-INFO-DIR-ENTRY"); + fatal (_("END-INFO-DIR-ENTRY without matching START-INFO-DIR-ENTRY")); } } if (start_of_this_entry != 0) - fatal ("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY"); + fatal (_("START-INFO-DIR-ENTRY without matching END-INFO-DIR-ENTRY")); } if (!delete_flag) if (entries_to_add == 0) - fatal ("no info dir entry in `%s'", infile); + { /* No need to abort here, the original info file may not have + the requisite Texinfo commands. This is not something an + installer should have to correct (it's a problem for the + maintainer), and there's no need to cause subsequent parts of + `make install' to fail. */ + warning (_("no info dir entry in `%s'"), infile); + exit (0); + } /* Now read in the Info dir file. */ + ensure_dirfile_exists (dirfile); dir_data = readfile (dirfile, &dir_size); dir_lines = findlines (dir_data, dir_size, &dir_nlines); /* We will be comparing the entries in the dir file against the current filename, so need to strip off any directory prefix and any .info suffix. */ { unsigned basename_len; - extern char *strrchr (); char *infile_basename = strrchr (infile, '/'); if (infile_basename) infile_basename++; else infile_basename = infile; basename_len = strlen (infile_basename); infile_sans_info = (strlen (infile_basename) > 5 && strcmp (infile_basename + basename_len - 5, ".info") == 0) ? copy_string (infile_basename, basename_len - 5) : infile_basename; infilelen_sans_info = strlen (infile_sans_info); } /* Parse the dir file. Find all the nodes, and their menus, and the sections of their menus. */ dir_nodes = 0; node_header_flag = 0; for (i = 0; i < dir_nlines; i++) { /* Parse node header lines. */ if (node_header_flag) { int j, end; for (j = 0; j < dir_lines[i].size; j++) /* Find the node name and store it in the `struct node'. */ if (!strncmp ("Node:", dir_lines[i].start + j, 5)) { char *line = dir_lines[i].start; /* Find the start of the node name. */ j += 5; while (line[j] == ' ' || line[j] == '\t') j++; /* Find the end of the node name. */ end = j; while (line[end] != 0 && line[end] != ',' && line[end] != '\n' && line[end] != '\t') end++; dir_nodes->name = copy_string (line + j, end - j); } node_header_flag = 0; } /* Notice the start of a node. */ if (*dir_lines[i].start == 037) { struct node *next = (struct node *) xmalloc (sizeof (struct node)); next->next = dir_nodes; next->name = NULL; next->start_line = i; next->end_line = 0; next->menu_start = NULL; next->sections = NULL; next->last_section = NULL; if (dir_nodes != 0) dir_nodes->end_line = i; /* Fill in the end of the last menu section of the previous node. */ if (dir_nodes != 0 && dir_nodes->last_section != 0) dir_nodes->last_section->end_line = i; dir_nodes = next; /* The following line is the header of this node; parse it. */ node_header_flag = 1; } /* Notice the lines that start menus. */ if (dir_nodes != 0 && !strncmp ("* Menu:", dir_lines[i].start, 7)) dir_nodes->menu_start = dir_lines[i + 1].start; /* Notice sections in menus. */ if (dir_nodes != 0 && dir_nodes->menu_start != 0 && *dir_lines[i].start != '\n' && *dir_lines[i].start != '*' && *dir_lines[i].start != ' ' && *dir_lines[i].start != '\t') { /* Add this menu section to the node's list. This list grows in forward order. */ struct menu_section *next = (struct menu_section *) xmalloc (sizeof (struct menu_section)); next->start_line = i + 1; next->next = 0; next->end_line = 0; next->name = copy_string (dir_lines[i].start, dir_lines[i].size); if (dir_nodes->sections) { dir_nodes->last_section->next = next; dir_nodes->last_section->end_line = i; } else dir_nodes->sections = next; dir_nodes->last_section = next; } /* Check for an existing entry that should be deleted. Delete all entries which specify this file name. */ if (*dir_lines[i].start == '*') { char *p = dir_lines[i].start; while (*p != 0 && *p != ':') p++; p++; while (*p == ' ') p++; if (*p == '(') { p++; if ((dir_lines[i].size > (p - dir_lines[i].start + infilelen_sans_info)) - && !strncmp(p, infile_sans_info, infilelen_sans_info) - && ( p[infilelen_sans_info] == ')' || - strncmp (p + infilelen_sans_info, ".info)", 6) == 0)) - dir_lines[i].delete = 1; + && !strncmp (p, infile_sans_info, infilelen_sans_info) + && (p[infilelen_sans_info] == ')' + || !strncmp (p + infilelen_sans_info, ".info)", 6))) + { + dir_lines[i].delete = 1; + something_deleted = 1; + } } } /* Treat lines that start with whitespace as continuations; if we are deleting an entry, delete all its continuations as well. */ else if (i > 0 && (*dir_lines[i].start == ' ' || *dir_lines[i].start == '\t')) { dir_lines[i].delete = dir_lines[i - 1].delete; something_deleted = 1; } } /* Finish the info about the end of the last node. */ if (dir_nodes != 0) { dir_nodes->end_line = dir_nlines; if (dir_nodes->last_section != 0) dir_nodes->last_section->end_line = dir_nlines; } /* Decide where to add the new entries (unless --delete was used). Find the menu sections to add them in. In each section, find the proper alphabetical place to add each of the entries. */ if (!delete_flag) { struct node *node; struct menu_section *section; struct spec_section *spec; for (node = dir_nodes; node; node = node->next) for (section = node->sections; section; section = section->next) { for (i = section->end_line; i > section->start_line; i--) if (dir_lines[i - 1].size != 0) break; section->end_line = i; for (spec = input_sections; spec; spec = spec->next) if (!strcmp (spec->name, section->name)) break; if (spec) { int add_at_line = section->end_line; struct spec_entry *entry; /* Say we have found at least one section with this name, so we need not add such a section. */ spec->missing = 0; /* For each entry, find the right place in this section to add it. */ for (entry = entries_to_add; entry; entry = entry->next) { int textlen = strlen (entry->text); /* Subtract one because dir_lines is zero-based, but the `end_line' and `start_line' members are one-based. */ for (i = section->end_line - 1; i >= section->start_line - 1; i--) { /* If an entry exists with the same name, and was not marked for deletion (which means it is for some other file), we are in trouble. */ if (dir_lines[i].start[0] == '*' && menu_line_equal (entry->text, textlen, dir_lines[i].start, dir_lines[i].size) && !dir_lines[i].delete) - fatal ("menu item `%s' already exists, for file `%s'", + fatal (_("menu item `%s' already exists, for file `%s'"), extract_menu_item_name (entry->text), extract_menu_file_name (dir_lines[i].start)); if (dir_lines[i].start[0] == '*' && menu_line_lessp (entry->text, textlen, dir_lines[i].start, dir_lines[i].size)) add_at_line = i; } insert_entry_here (entry, add_at_line, dir_lines, n_entries_to_add); } } } /* Mark the end of the Top node as the place to add any new sections that are needed. */ for (node = dir_nodes; node; node = node->next) if (node->name && strcmp (node->name, "Top") == 0) dir_lines[node->end_line].add_sections_before = 1; } if (delete_flag && !something_deleted && !quiet_flag) - warning ("no entries found for `%s'; nothing deleted", infile); + warning (_("no entries found for `%s'; nothing deleted"), infile); /* Output the old dir file, interpolating the new sections and/or new entries where appropriate. */ output = fopen (dirfile, "w"); if (!output) { perror (dirfile); exit (1); } for (i = 0; i <= dir_nlines; i++) { int j; /* If we decided to output some new entries before this line, output them now. */ if (dir_lines[i].add_entries_before) for (j = 0; j < n_entries_to_add; j++) { struct spec_entry *this = dir_lines[i].add_entries_before[j]; if (this == 0) break; fputs (this->text, output); } /* If we decided to add some sections here because there are no such sections in the file, output them now. */ if (dir_lines[i].add_sections_before) { struct spec_section *spec; struct spec_section **sections; int n_sections = 0; /* Count the sections and allocate a vector for all of them. */ for (spec = input_sections; spec; spec = spec->next) n_sections++; sections = ((struct spec_section **) xmalloc (n_sections * sizeof (struct spec_section *))); /* Fill the vector SECTIONS with pointers to all the sections, and sort them. */ j = 0; for (spec = input_sections; spec; spec = spec->next) sections[j++] = spec; qsort (sections, n_sections, sizeof (struct spec_section *), compare_section_names); /* Generate the new sections in alphabetical order. In each new section, output all of our entries. */ for (j = 0; j < n_sections; j++) { spec = sections[j]; if (spec->missing) { struct spec_entry *entry; putc ('\n', output); fputs (spec->name, output); putc ('\n', output); for (entry = entries_to_add; entry; entry = entry->next) fputs (entry->text, output); } } free (sections); } /* Output the original dir lines unless marked for deletion. */ if (i < dir_nlines && !dir_lines[i].delete) { fwrite (dir_lines[i].start, 1, dir_lines[i].size, output); putc ('\n', output); } } fclose (output); exit (0); } /* Read all of file FILNAME into memory and return the address of the data. Store the size into SIZEP. If there is trouble, do a fatal error. */ char * readfile (filename, sizep) char *filename; int *sizep; { + int desc; int data_size = 1024; char *data = (char *) xmalloc (data_size); int filled = 0; int nread = 0; +#ifdef HAVE_LIBZ + int isGZ = 0; + gzFile zdesc; +#endif - int desc = open (filename, O_RDONLY); - + desc = open (filename, O_RDONLY); if (desc < 0) pfatal_with_name (filename); +#ifdef HAVE_LIBZ + /* The file should always be two bytes long. */ + if (read (desc, data, 2) != 2) + pfatal_with_name (filename); + + /* Undo that read. */ + lseek (desc, 0, SEEK_SET); + + /* If we see gzip magic, use gzdopen. */ + if (data[0] == '\x1f' && data[1] == '\x8b') + { + isGZ = 1; + zdesc = gzdopen (desc, "r"); + if (zdesc == NULL) { + close (desc); + pfatal_with_name (filename); + } + } +#endif /* HAVE_LIBZ */ + while (1) { - nread = read (desc, data + filled, data_size - filled); +#ifdef HAVE_LIBZ + if (isGZ) + nread = gzread (zdesc, data + filled, data_size - filled); + else +#endif + nread = read (desc, data + filled, data_size - filled); + if (nread < 0) pfatal_with_name (filename); if (nread == 0) break; filled += nread; if (filled == data_size) { data_size *= 2; data = (char *) xrealloc (data, data_size); } } *sizep = filled; + +#ifdef HAVE_LIBZ + if (isGZ) + gzclose (zdesc); + else +#endif + close(desc); + return data; } /* Divide the text at DATA (of SIZE bytes) into lines. Return a vector of struct line_data describing the lines. Store the length of that vector into *NLINESP. */ struct line_data * findlines (data, size, nlinesp) char *data; int size; int *nlinesp; { struct line_data *lines; int lines_allocated = 512; int filled = 0; int i = 0; int lineflag; lines = (struct line_data *) xmalloc (lines_allocated * sizeof (struct line_data)); lineflag = 1; for (i = 0; i < size; i++) { if (lineflag) { if (filled == lines_allocated) { lines_allocated *= 2; lines = (struct line_data *) xrealloc (lines, lines_allocated * sizeof (struct line_data)); } lines[filled].start = &data[i]; lines[filled].add_entries_before = 0; lines[filled].add_sections_before = 0; lines[filled].delete = 0; if (filled > 0) lines[filled - 1].size = lines[filled].start - lines[filled - 1].start - 1; filled++; } lineflag = (data[i] == '\n'); } if (filled > 0) lines[filled - 1].size = &data[i] - lines[filled - 1].start - lineflag; /* Do not leave garbage in the last element. */ lines[filled].start = NULL; lines[filled].add_entries_before = NULL; lines[filled].add_sections_before = 0; lines[filled].delete = 0; lines[filled].size = 0; *nlinesp = filled; return lines; } /* Compare the menu item names in LINE1 (line length LEN1) and LINE2 (line length LEN2). Return 1 if the item name in LINE1 is less, 0 otherwise. */ int menu_line_lessp (line1, len1, line2, len2) char *line1; int len1; char *line2; int len2; { int minlen = (len1 < len2 ? len1 : len2); int i; for (i = 0; i < minlen; i++) { /* If one item name is a prefix of the other, the former one is less. */ if (line1[i] == ':' && line2[i] != ':') return 1; if (line2[i] == ':' && line1[i] != ':') return 0; /* If they both continue and differ, one is less. */ if (line1[i] < line2[i]) return 1; if (line1[i] > line2[i]) return 0; } /* With a properly formatted dir file, we can only get here if the item names are equal. */ return 0; } /* Compare the menu item names in LINE1 (line length LEN1) and LINE2 (line length LEN2). Return 1 if the item names are equal, 0 otherwise. */ int menu_line_equal (line1, len1, line2, len2) char *line1; int len1; char *line2; int len2; { int minlen = (len1 < len2 ? len1 : len2); int i; for (i = 0; i < minlen; i++) { /* If both item names end here, they are equal. */ if (line1[i] == ':' && line2[i] == ':') return 1; /* If they both continue and differ, one is less. */ if (line1[i] != line2[i]) return 0; } /* With a properly formatted dir file, we can only get here if the item names are equal. */ return 1; } /* This is the comparison function for qsort for a vector of pointers to struct spec_section. Compare the section names. */ int compare_section_names (sec1, sec2) struct spec_section **sec1, **sec2; { char *name1 = (*sec1)->name; char *name2 = (*sec2)->name; return strcmp (name1, name2); } /* Insert ENTRY into the add_entries_before vector for line number LINE_NUMBER of the dir file. DIR_LINES and N_ENTRIES carry information from like-named variables in main. */ void insert_entry_here (entry, line_number, dir_lines, n_entries) struct spec_entry *entry; int line_number; struct line_data *dir_lines; int n_entries; { int i; if (dir_lines[line_number].add_entries_before == 0) { dir_lines[line_number].add_entries_before = (struct spec_entry **) xmalloc (n_entries * sizeof (struct spec_entry *)); for (i = 0; i < n_entries; i++) dir_lines[line_number].add_entries_before[i] = 0; } for (i = 0; i < n_entries; i++) if (dir_lines[line_number].add_entries_before[i] == 0) break; if (i == n_entries) abort (); dir_lines[line_number].add_entries_before[i] = entry; }