diff -up -x 'Makefile*' -x '*.o' -x '*~' tag-node-5419/trunk/info/dir.c parse-node-5419/trunk/info/dir.c --- tag-node-5419/trunk/info/dir.c 2014-02-28 20:17:13.000000000 +0000 +++ parse-node-5419/trunk/info/dir.c 2014-02-28 20:46:52.000000000 +0000 @@ -32,7 +32,7 @@ static void add_menu_to_file_buffer (cha FILE_BUFFER *fb); static void insert_text_into_fb_at_binding (FILE_BUFFER *fb, SEARCH_BINDING *binding, char *text, int textlen); -void maybe_build_dir_node (char *dirname); +FILE_BUFFER *maybe_build_dir_node (char *dirname); static char *dirs_to_add[] = { "dir", "localdir", NULL @@ -74,27 +74,40 @@ new_dir_file_p (struct stat *test) return 1; } +static FILE_BUFFER *dir_buffer = 0; -void +static const char *dir_header = "\037\nFile:dir, Node: Top\n"; +static const char *dir_text = +"\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"; + +/* Return dir node, building it if necessary */ +FILE_BUFFER * maybe_build_dir_node (char *dirname) { int path_index, update_tags; char *this_dir; - FILE_BUFFER *dir_buffer = info_find_file (dirname); - /* If there is no "dir" in the current info path, we cannot build one - from nothing. */ - if (!dir_buffer) - return; - - /* If this directory has already been built, return now. */ - if (dir_buffer->flags & N_CannotGC) - return; - - /* Initialize the list we use to avoid reading the same dir file twice - with the dir file just found. */ - new_dir_file_p (&dir_buffer->finfo); - + /* Check if it's been built already */ + if (dir_buffer) return dir_buffer; + + dir_buffer = make_file_buffer(); + dir_buffer->filesize = strlen (dir_header) + strlen (dir_text); + dir_buffer->filename = "dir"; + /* FIXME: not the full path: does this matter? */ + /* This is used in get_nodes_of_info_file() */ + dir_buffer->fullpath = "dir"; + + dir_buffer->contents = calloc (dir_buffer->filesize + 1, 1); + strcpy (dir_buffer->contents, dir_header); + strcpy (dir_buffer->contents + strlen(dir_header), dir_text); + update_tags = 0; /* Using each element of the path, check for one of the files in @@ -157,11 +170,12 @@ maybe_build_dir_node (char *dirname) free (this_dir); } - if (update_tags) - build_tags_and_nodes (dir_buffer); + build_tags_and_nodes (dir_buffer); /* Flag that the dir buffer has been built. */ dir_buffer->flags |= N_CannotGC; + + return dir_buffer; } /* Given CONTENTS and FB (a file buffer), add the menu found in CONTENTS diff -up -x 'Makefile*' -x '*.o' -x '*~' tag-node-5419/trunk/info/echo-area.c parse-node-5419/trunk/info/echo-area.c --- tag-node-5419/trunk/info/echo-area.c 2014-02-28 20:17:13.000000000 +0000 +++ parse-node-5419/trunk/info/echo-area.c 2014-02-28 20:57:59.000000000 +0000 @@ -777,8 +777,12 @@ static size_t completions_found_slots = /* The lowest common denominator found while completing. */ static REFERENCE *LCD_completion; +/* Function to choose which references to offer as completion options. */ +static reference_bool_fn completion_exclude_func = 0; + /* Internal functions used by the user calls. */ -static void build_completions (void), completions_must_be_rebuilt (void); +static void build_completions (void); +static void completions_must_be_rebuilt (void); /* Variable which holds the output of completions. */ static NODE *possible_completions_output_node = NULL; @@ -799,10 +803,11 @@ completions_window_p (WINDOW *window) } /* Workhorse for completion readers. If FORCE is non-zero, the user cannot - exit unless the line read completes, or is empty. */ + exit unless the line read completes, or is empty. Use EXCLUDE_FUNC to + exclude items in COMPLETIONS. */ char * info_read_completing_internal (WINDOW *window, const char *prompt, - REFERENCE **completions, int force) + REFERENCE **completions, int force, reference_bool_fn exclude_func) { char *line; @@ -825,6 +830,7 @@ info_read_completing_internal (WINDOW *w /* Save away the list of items to complete over. */ echo_area_completion_items = completions; completions_must_be_rebuilt (); + completion_exclude_func = exclude_func; active_window = the_echo_area; echo_area_is_active++; @@ -899,7 +905,7 @@ char * info_read_completing_in_echo_area (WINDOW *window, const char *prompt, REFERENCE **completions) { - return info_read_completing_internal (window, prompt, completions, 1); + return info_read_completing_internal (window, prompt, completions, 1, 0); } /* Read a line in the echo area allowing completion over COMPLETIONS, but @@ -908,7 +914,17 @@ char * info_read_maybe_completing (WINDOW *window, const char *prompt, REFERENCE **completions) { - return info_read_completing_internal (window, prompt, completions, 0); + return info_read_completing_internal (window, prompt, completions, 0, 0); +} + +/* Read a line in the echo area with completion over COMPLETIONS, using + EXCLUDE to exclude items from the completion list. */ +char * +info_read_completing_in_echo_area_with_exclusions (WINDOW *window, + const char *prompt, REFERENCE **completions, reference_bool_fn exclude) +{ + return info_read_completing_internal + (window, prompt, completions, 1, exclude); } DECLARE_INFO_COMMAND (ea_possible_completions, _("List possible completions")) @@ -1217,6 +1233,11 @@ build_completions (void) for (i = 0; (entry = echo_area_completion_items[i]); i++) { + /* Skip certain items (for example, we might only want + a list of menu items). */ + if (completion_exclude_func && completion_exclude_func (entry)) + continue; + if (mbsncasecmp (request, entry->label, len) == 0) add_pointer_to_array (entry, completions_found_index, completions_found, completions_found_slots, diff -up -x 'Makefile*' -x '*.o' -x '*~' tag-node-5419/trunk/info/echo-area.h parse-node-5419/trunk/info/echo-area.h --- tag-node-5419/trunk/info/echo-area.h 2014-02-28 20:17:13.000000000 +0000 +++ parse-node-5419/trunk/info/echo-area.h 2014-02-28 20:22:55.000000000 +0000 @@ -34,8 +34,11 @@ extern void inform_in_echo_area (const c extern void echo_area_inform_of_deleted_window (WINDOW *window); extern void echo_area_prep_read (void); extern VFunction *ea_last_executed_command; + +typedef int (*reference_bool_fn) (REFERENCE *); + extern char * info_read_completing_internal (WINDOW *window, const char *prompt, - REFERENCE **completions, int force); + REFERENCE **completions, int force, reference_bool_fn exclude); /* Read a line of text in the echo area. Return a malloc ()'ed string, or NULL if the user aborted out of this read. WINDOW is the currently @@ -54,6 +57,12 @@ char *info_read_completing_in_echo_area extern char *info_read_maybe_completing (WINDOW *window, const char *prompt, REFERENCE **completions); +/* Read a line in the echo area with completion over COMPLETIONS, using + EXCLUDE to exclude items from the completion list. */ +char * +info_read_completing_in_echo_area_with_exclusions (WINDOW *window, + const char *prompt, REFERENCE **completions, reference_bool_fn exclude); + extern void ea_insert (WINDOW *window, int count, unsigned char key); extern void ea_quoted_insert (WINDOW *window, int count, unsigned char key); extern void ea_beg_of_line (WINDOW *window, int count, unsigned char key); diff -up -x 'Makefile*' -x '*.o' -x '*~' tag-node-5419/trunk/info/footnotes.c parse-node-5419/trunk/info/footnotes.c --- tag-node-5419/trunk/info/footnotes.c 2014-02-28 20:17:14.000000000 +0000 +++ parse-node-5419/trunk/info/footnotes.c 2014-02-28 20:22:55.000000000 +0000 @@ -67,7 +67,7 @@ make_footnotes_node (NODE *node) { REFERENCE **refs; - refs = info_xrefs_of_node (node); + refs = node->references; if (refs) { @@ -81,10 +81,11 @@ make_footnotes_node (NODE *node) strcat (refname, "-Footnotes"); for (i = 0; refs[i]; i++) - if ((refs[i]->nodename != NULL) && + if (refs[i]->type == REFERENCE_XREF + && (refs[i]->nodename != NULL) /* Support both the older "foo-Footnotes" and the new style "foo-Footnote-NN" references. */ - (strcmp (refs[i]->nodename, refname) == 0 || + && (strcmp (refs[i]->nodename, refname) == 0 || (strncmp (refs[i]->nodename, refname, reflen - 1) == 0 && refs[i]->nodename[reflen - 1] == '-' && isdigit (refs[i]->nodename[reflen])))) @@ -104,7 +105,6 @@ make_footnotes_node (NODE *node) } free (refname); - info_free_references (refs); } } Binary files tag-node-5419/trunk/info/ginfo and parse-node-5419/trunk/info/ginfo differ diff -up -x 'Makefile*' -x '*.o' -x '*~' tag-node-5419/trunk/info/indices.c parse-node-5419/trunk/info/indices.c --- tag-node-5419/trunk/info/indices.c 2014-02-28 20:18:25.000000000 +0000 +++ parse-node-5419/trunk/info/indices.c 2014-02-28 20:58:19.000000000 +0000 @@ -55,7 +55,8 @@ static size_t index_nodenames_index = 0; static size_t index_nodenames_slots = 0; /* Add the name of NODE, and the range of the associated index elements - (passed in ARRAY) to index_nodenames. */ + (passed in ARRAY) to index_nodenames. ARRAY must have at least one + element. */ static void add_index_to_index_nodenames (REFERENCE **array, NODE *node) { @@ -136,6 +137,10 @@ info_indices_of_file_buffer (FILE_BUFFER NODE *node; REFERENCE **menu; + /* If this is an anchor, don't bother. */ + if (tag->nodelen == 0) continue; + + /* FIXME: Just do node = tag; ??? */ /* Found one. Get its menu. */ node = info_get_node (tag->filename, tag->nodename, PARSE_NODE_VERBATIM); @@ -146,11 +151,11 @@ info_indices_of_file_buffer (FILE_BUFFER initial_index_filename = xstrdup (file_buffer->filename); initial_index_nodename = xstrdup (tag->nodename); - menu = info_menu_of_node (node); + menu = node->references; - /* If we have a menu, add this index's nodename and range - to our list of index_nodenames. */ - if (menu) + /* If we have a non-empty menu, add this index's nodename + and range to our list of index_nodenames. */ + if (menu && menu[0]) { add_index_to_index_nodenames (menu, node); @@ -541,8 +546,12 @@ apropos_in_all_indices (char *search_str NODE *dir_node; dir_node = info_get_node ("dir", "Top", PARSE_NODE_DFLT); + + /* It should be safe to assume that dir nodes do not contain any + cross-references, i.e., its references list only contains + menu items. */ if (dir_node) - dir_menu = info_menu_of_node (dir_node); + dir_menu = dir_node->references; if (!dir_menu) return NULL; @@ -584,9 +593,9 @@ apropos_in_all_indices (char *search_str continue; } + char *files_name; /* Get the file buffer associated with this node. */ { - char *files_name; files_name = this_node->parent; if (!files_name) @@ -627,10 +636,10 @@ apropos_in_all_indices (char *search_str /* Concatenate with the other indices. */ all_indices = info_concatenate_references (all_indices, this_index); } + /* Try to avoid running out of memory */ + forget_info_file (files_name); } - info_free_references (dir_menu); - /* Build a list of the references which contain SEARCH_STRING. */ if (all_indices) { diff -up -x 'Makefile*' -x '*.o' -x '*~' tag-node-5419/trunk/info/info-utils.c parse-node-5419/trunk/info/info-utils.c --- tag-node-5419/trunk/info/info-utils.c 2014-02-28 20:17:14.000000000 +0000 +++ parse-node-5419/trunk/info/info-utils.c 2014-02-28 20:22:55.000000000 +0000 @@ -54,24 +54,22 @@ static void saven_filename (char *filena static void save_nodename (char *nodename); static void saven_nodename (char *nodename, int len); -/* How to get a reference (either menu or cross). */ -static REFERENCE **info_references_internal (char *label, - SEARCH_BINDING *binding); - -/* Parse the filename and nodename out of STRING. If STRING doesn't - contain a filename (i.e., it is NOT (FILENAME)NODENAME) then set - INFO_PARSED_FILENAME to NULL. The second argument is one of - the PARSE_NODE_* constants. It specifies how to parse the node name: +/* Parse the filename and nodename out of STRING. Return length of node + specification. If STRING doesn't contain a filename (i.e., it is NOT + (FILENAME)NODENAME) then set INFO_PARSED_FILENAME to NULL. The + second argument is one of the PARSE_NODE_* constants. It specifies + how to parse the node name: PARSE_NODE_DFLT Node name stops at LF, `,', `.', or `TAB' PARSE_NODE_SKIP_NEWLINES Node name stops at `,', `.', or `TAB' PARSE_NODE_VERBATIM Don't parse nodename */ -void +int info_parse_node (char *string, int flag) { register int i = 0; + int length = 0; /* Return value */ /* Default the answer. */ save_filename (NULL); @@ -81,7 +79,8 @@ info_parse_node (char *string, int flag) if (!string || !*string) return; - string += skip_whitespace (string); + length = skip_whitespace (string); + string += length; /* Check for (FILENAME)NODENAME. */ if (*string == '(') @@ -92,6 +91,7 @@ info_parse_node (char *string, int flag) i = 0; /* Advance past the opening paren. */ string++; + length++; /* Find the closing paren. Handle nested parens correctly. */ for (bcnt = 0, bfirst = -1; string[i]; i++) @@ -119,13 +119,19 @@ info_parse_node (char *string, int flag) /* Point directly at the nodename. */ string += i; + length += i; if (*string) - string++; + { + string++; + length++; + } } /* Parse out nodename. */ i = skip_node_characters (string, flag); + length += i; + length++; /* skip_node_characters() stops on terminating character */ saven_nodename (string, i); canonicalize_whitespace (info_parsed_nodename); if (info_parsed_nodename && !*info_parsed_nodename) @@ -164,203 +170,20 @@ info_parse_node (char *string, int flag) else info_parsed_line_number = 0; } + return length; } -/* Return the node addressed by LABEL in NODE (usually one of "Prev:", - "Next:", "Up:", "File:", or "Node:". After a call to this function, - the global INFO_PARSED_NODENAME and INFO_PARSED_FILENAME contain - the information. */ -void -info_parse_label (char *label, NODE *node) -{ - register int i; - char *nodeline; - - /* Default answer to failure. */ - save_nodename (NULL); - save_filename (NULL); - - /* Find the label in the first line of this node. */ - nodeline = node->contents; - i = string_in_line (label, nodeline); - - if (i == -1) - return; - - nodeline += i; - nodeline += skip_whitespace (nodeline); - info_parse_node (nodeline, PARSE_NODE_DFLT); -} /* **************************************************************** */ /* */ -/* Finding and Building Menus */ +/* References */ /* */ /* **************************************************************** */ -/* Return a NULL terminated array of REFERENCE * which represents the menu - found in NODE. If there is no menu in NODE, just return a NULL pointer. */ -REFERENCE ** -info_menu_of_node (NODE *node) -{ - long position; - SEARCH_BINDING tmp_search; - REFERENCE **menu = NULL; - - tmp_search.buffer = node->contents; - tmp_search.start = 0; - tmp_search.end = node->nodelen; - tmp_search.flags = S_FoldCase; - - /* Find the start of the menu. */ - if (search_forward (INFO_MENU_LABEL, &tmp_search, &position) - != search_success) - return NULL; - - /* We have the start of the menu now. Glean menu items from the rest - of the node. */ - tmp_search.start = position + strlen (INFO_MENU_LABEL); - tmp_search.start += skip_line (tmp_search.buffer + tmp_search.start); - tmp_search.start--; - menu = info_menu_items (&tmp_search); - return menu; -} - -/* Return a NULL terminated array of REFERENCE * which represents the cross - refrences found in NODE. If there are no cross references in NODE, just - return a NULL pointer. */ -REFERENCE ** -info_xrefs_of_node (NODE *node) -{ - SEARCH_BINDING tmp_search; - -#if defined (HANDLE_MAN_PAGES) - if (node->flags & N_IsManPage) - return xrefs_of_manpage (node); -#endif - - tmp_search.buffer = node->contents; - tmp_search.start = 0; - tmp_search.end = node->nodelen; - tmp_search.flags = S_FoldCase; - - return info_xrefs (&tmp_search); -} - -/* Glean menu entries from BINDING->buffer + BINDING->start until we - have looked at the entire contents of BINDING. Return an array - of REFERENCE * that represents each menu item in this range. */ -REFERENCE ** -info_menu_items (SEARCH_BINDING *binding) -{ - return info_references_internal (INFO_MENU_ENTRY_LABEL, binding); -} - -/* Glean cross references from BINDING->buffer + BINDING->start until - BINDING->end. Return an array of REFERENCE * that represents each - cross reference in this range. */ -REFERENCE ** -info_xrefs (SEARCH_BINDING *binding) -{ - return info_references_internal (INFO_XREF_LABEL, binding); -} - -/* Glean cross references or menu items from BINDING. Return an array - of REFERENCE * that represents the items found. */ -static REFERENCE ** -info_references_internal (char *label, SEARCH_BINDING *binding) -{ - SEARCH_BINDING tmp_search; - REFERENCE **refs = NULL; - size_t refs_index = 0, refs_slots = 0; - int searching_for_menu_items = 0; - long position; - - tmp_search.buffer = binding->buffer; - tmp_search.start = binding->start; - tmp_search.end = binding->end; - tmp_search.flags = S_FoldCase | S_SkipDest; - - searching_for_menu_items = (mbscasecmp (label, INFO_MENU_ENTRY_LABEL) == 0); - - while (search_forward (label, &tmp_search, &position) == search_success) - { - int offset, start; - char *refdef; - REFERENCE *entry; - - tmp_search.start = position; - tmp_search.start += skip_whitespace (tmp_search.buffer + tmp_search.start); - start = tmp_search.start - binding->start; - refdef = tmp_search.buffer + tmp_search.start; - offset = string_in_line (":", refdef); - - /* When searching for menu items, if no colon, there is no - menu item on this line. */ - if (offset == -1) - { - if (searching_for_menu_items) - continue; - else - { - int temp; - - temp = skip_line (refdef); - offset = string_in_line (":", refdef + temp); - if (offset == -1) - continue; /* Give up? */ - else - offset += temp; - } - } - - entry = xmalloc (sizeof (REFERENCE)); - entry->filename = NULL; - entry->nodename = NULL; - entry->label = xmalloc (offset); - strncpy (entry->label, refdef, offset - 1); - entry->label[offset - 1] = '\0'; - canonicalize_whitespace (entry->label); - entry->line_number = 0; - - refdef += offset; - entry->start = start; - entry->end = refdef - binding->buffer; - - /* If this reference entry continues with another ':' then the - nodename is the same as the label. */ - if (*refdef == ':') - { - entry->nodename = xstrdup (entry->label); - } - else - { - /* This entry continues with a specific nodename. Parse the - nodename from the specification. */ - - refdef += skip_whitespace_and_newlines (refdef); - - if (searching_for_menu_items) - info_parse_node (refdef, PARSE_NODE_DFLT); - else - info_parse_node (refdef, PARSE_NODE_SKIP_NEWLINES); - - if (info_parsed_filename) - entry->filename = xstrdup (info_parsed_filename); - - if (info_parsed_nodename) - entry->nodename = xstrdup (info_parsed_nodename); - - entry->line_number = info_parsed_line_number; - } - - add_pointer_to_array (entry, refs_index, refs, refs_slots, 50); - } - return refs; -} - /* Get the entry associated with LABEL in REFERENCES. Return a pointer to the ENTRY if found, or NULL. */ +/* Only ever called for menus in rest of code - so we should rename it + info_get_reference_of_menu */ REFERENCE * info_get_labeled_reference (char *label, REFERENCE **references) { @@ -369,6 +192,7 @@ info_get_labeled_reference (char *label, for (i = 0; references && (entry = references[i]); i++) { + if (REFERENCE_MENU_ITEM != entry->type) continue; if (strcmp (label, entry->label) == 0) return entry; } @@ -377,7 +201,7 @@ info_get_labeled_reference (char *label, /* A utility function for concatenating REFERENCE **. Returns a new REFERENCE ** which is the concatenation of REF1 and REF2. The REF1 - and REF2 arrays are freed, but their contents are not. */ + array is freed, but its contents are not. REF2 is not freed. */ REFERENCE ** info_concatenate_references (REFERENCE **ref1, REFERENCE **ref2) { @@ -385,31 +209,46 @@ info_concatenate_references (REFERENCE * REFERENCE **result; int size; - /* With one argument passed as NULL, simply return the other arg. */ - if (!ref1) - return ref2; - else if (!ref2) + if (!ref2) return ref1; /* Get the total size of the slots that we will need. */ - for (i = 0; ref1[i]; i++); - size = i; + size = 0; + if (ref1) + { + for (i = 0; ref1[i]; i++); + size += i; + } for (i = 0; ref2[i]; i++); size += i; result = xmalloc ((1 + size) * sizeof (REFERENCE *)); - /* Copy the contents over. */ - for (i = 0; ref1[i]; i++) - result[i] = ref1[i]; + i = 0; + if (ref1) + /* Copy the contents over. */ + for (i = 0; ref1[i]; i++) + result[i] = ref1[i]; j = i; for (i = 0; ref2[i]; i++) - result[j++] = ref2[i]; + { + result[j] = xmalloc (sizeof (REFERENCE)); + + /* Copy REFERENCE structure */ + *(result[j]) = *(ref2[i]); + + if (result[j]->label) + result[j]->label = xstrdup (ref2[i]->label); + if (result[j]->filename) + result[j]->filename = xstrdup (ref2[i]->filename); + if (result[j]->nodename) + result[j]->nodename = xstrdup (ref2[i]->nodename); + j++; + } result[j] = NULL; free (ref1); - free (ref2); return result; } diff -up -x 'Makefile*' -x '*.o' -x '*~' tag-node-5419/trunk/info/info-utils.h parse-node-5419/trunk/info/info-utils.h --- tag-node-5419/trunk/info/info-utils.h 2014-02-28 20:17:14.000000000 +0000 +++ parse-node-5419/trunk/info/info-utils.h 2014-02-28 20:22:55.000000000 +0000 @@ -22,10 +22,6 @@ #ifndef INFO_UTILS_H #define INFO_UTILS_H -#include "nodes.h" -#include "window.h" -#include "search.h" - /* Structure which describes a node reference, such as a menu entry or cross reference. Arrays of such references can be built by calling info_menus_of_node () or info_xrefs_of_node (). */ @@ -35,8 +31,20 @@ typedef struct { char *nodename; /* Name of the node. */ int start, end; /* Offsets within the containing node of LABEL. */ int line_number; /* Specific line number a menu item points to. */ + int type; /* Whether reference is a xref or a menu item */ } REFERENCE; + +/* Possible values of REFERENCE.type */ +#define REFERENCE_XREF 0 +#define REFERENCE_MENU_ITEM 1 + +/* REFERENCE is used in nodes.h - it should probably be moved there */ + +#include "nodes.h" +#include "window.h" +#include "search.h" + /* When non-zero, various display and input functions handle ISO Latin character sets correctly. */ extern int ISO_Latin_p; @@ -65,32 +73,13 @@ extern char *info_parsed_nodename; PARSE_NODE_START The STRING argument is retrieved from a node start line, and therefore ends in `,' only. */ -void info_parse_node (char *string, int flag); - -/* Return a NULL terminated array of REFERENCE * which represents the menu - found in NODE. If there is no menu in NODE, just return a NULL pointer. */ -extern REFERENCE **info_menu_of_node (NODE *node); - -/* Return a NULL terminated array of REFERENCE * which represents the cross - refrences found in NODE. If there are no cross references in NODE, just - return a NULL pointer. */ -extern REFERENCE **info_xrefs_of_node (NODE *node); - -/* Glean cross references from BINDING->buffer + BINDING->start until - BINDING->end. Return an array of REFERENCE * that represents each - cross reference in this range. */ -extern REFERENCE **info_xrefs (SEARCH_BINDING *binding); +int info_parse_node (char *string, int flag); /* Get the entry associated with LABEL in REFERENCES. Return a pointer to the reference if found, or NULL. */ extern REFERENCE *info_get_labeled_reference (char *label, REFERENCE **references); -/* Glean menu entries from BINDING->buffer + BINDING->start until we - have looked at the entire contents of BINDING. Return an array - of REFERENCE * that represents each menu item in this range. */ -extern REFERENCE **info_menu_items (SEARCH_BINDING *binding); - /* A utility function for concatenating REFERENCE **. Returns a new REFERENCE ** which is the concatenation of REF1 and REF2. The REF1 and REF2 arrays are freed, but their contents are not. */ @@ -131,22 +120,6 @@ extern WINDOW *get_internal_info_window /* Return a window displaying the node NODE. */ extern WINDOW *get_window_of_node (NODE *node); -/* Return the node addressed by LABEL in NODE (usually one of "Prev:", - "Next:", "Up:", "File:", or "Node:". After a call to this function, - the globals `info_parsed_nodename' and `info_parsed_filename' contain - the information. */ -extern void info_parse_label (char *label, NODE *node); - -#define info_file_label_of_node(n) info_parse_label (INFO_FILE_LABEL, n) -#define info_next_label_of_node(n) info_parse_label (INFO_NEXT_LABEL, n) -#define info_up_label_of_node(n) info_parse_label (INFO_UP_LABEL, n) -#define info_prev_label_of_node(n) \ - do { \ - info_parse_label (INFO_PREV_LABEL, n); \ - if (!info_parsed_nodename && !info_parsed_filename) \ - info_parse_label (INFO_ALTPREV_LABEL, n); \ - } while (0) - struct text_buffer { char *base; diff -up -x 'Makefile*' -x '*.o' -x '*~' tag-node-5419/trunk/info/info.c parse-node-5419/trunk/info/info.c --- tag-node-5419/trunk/info/info.c 2014-02-28 20:17:13.000000000 +0000 +++ parse-node-5419/trunk/info/info.c 2014-02-28 20:22:55.000000000 +0000 @@ -721,6 +721,9 @@ There is NO WARRANTY, to the extent perm argc -= optind; argv += optind; + /* Load custom key mappings and variable settings */ + initialize_terminal_and_keymaps (); + if (all_matches_p) return all_files (user_filename, argc, argv); Binary files tag-node-5419/trunk/info/infokey and parse-node-5419/trunk/info/infokey differ Binary files tag-node-5419/trunk/info/makedoc and parse-node-5419/trunk/info/makedoc differ diff -up -x 'Makefile*' -x '*.o' -x '*~' tag-node-5419/trunk/info/man.c parse-node-5419/trunk/info/man.c --- tag-node-5419/trunk/info/man.c 2014-02-28 20:18:25.000000000 +0000 +++ parse-node-5419/trunk/info/man.c 2014-02-28 20:22:55.000000000 +0000 @@ -405,13 +405,31 @@ manpage_node_of_file_buffer (FILE_BUFFER node->filename = file_buffer->filename; node->nodename = xstrdup (tag->nodename); node->contents = file_buffer->contents + tag->nodestart; - node->nodelen = tag->nodelen; + node->nodelen = tag->nodelen; /* Set to -1 in build_tags_and_nodes */ + node->nodestart = tag->nodestart; node->flags = 0; node->display_pos = 0; node->parent = NULL; node->flags = (N_HasTagsTable | N_IsManPage); node->contents += skip_node_separator (node->contents); node->body_start = strcspn(node->contents, "\n"); + + node->up = "(dir)"; + node->prev = 0; + node->next = 0; + + /* Set nodelen, which is currently (size_t) -1. */ + + SEARCH_BINDING node_body; + + node_body.buffer = file_buffer->contents; + node_body.start = node->nodestart; + node_body.start += skip_node_separator (file_buffer->contents + node->nodestart); + node_body.end = file_buffer->filesize; + node_body.flags = S_FoldCase; + node->nodelen = get_node_length (&node_body); + + node->references = xrefs_of_manpage (node); } return node; @@ -545,10 +563,14 @@ xrefs_of_manpage (NODE *node) size_t refs_slots = 0; long position; + /* Initialize reference list to have a single null entry. */ + refs = calloc(1, sizeof (REFERENCE *)); + refs_slots = 1; + reference_section = find_reference_section (node); if (reference_section == NULL) - return NULL; + return refs; /* Grovel the reference section building a list of references found there. A reference is alphabetic characters followed by non-whitespace text @@ -593,6 +615,7 @@ xrefs_of_manpage (NODE *node) entry->nodename = xstrdup (entry->label); entry->start = start; entry->end = end; + entry->type = REFERENCE_XREF; add_pointer_to_array (entry, refs_index, refs, refs_slots, 10); } diff -up -x 'Makefile*' -x '*.o' -x '*~' tag-node-5419/trunk/info/nodemenu.c parse-node-5419/trunk/info/nodemenu.c --- tag-node-5419/trunk/info/nodemenu.c 2014-02-28 20:17:14.000000000 +0000 +++ parse-node-5419/trunk/info/nodemenu.c 2014-02-28 20:22:55.000000000 +0000 @@ -20,6 +20,7 @@ Originally written by Brian Fox. */ #include "info.h" +#include "variables.h" NODE *get_visited_nodes (Function *filter_func); @@ -195,6 +196,13 @@ get_visited_nodes (Function *filter_func initialize_message_buffer (); + if (preprocess_nodes_p) { + /* Lead with a blank line so that if parse_node strips out the + first line, thinking it is a node information line (containing + File:", "Node:", etc.), nothing important will disappear. */ + printf_to_message_buffer ("\n", replace_in_documentation); + } + printf_to_message_buffer ("%s", replace_in_documentation (_("Here is the menu of nodes you have recently visited.\n\ @@ -212,7 +220,11 @@ Select one from this menu, or use `\\[hi free (lines); node = message_buffer_to_node (); - add_gcable_pointer (node->contents); + + parse_node (node); + free (node->contents); + node->contents = node->content_cache; + add_gcable_pointer (node->content_cache); return node; } @@ -305,7 +317,7 @@ DECLARE_INFO_COMMAND (select_visited_nod node = get_visited_nodes (NULL); - menu = info_menu_of_node (node); + menu = node->references; free (node); line = diff -up -x 'Makefile*' -x '*.o' -x '*~' tag-node-5419/trunk/info/nodes.c parse-node-5419/trunk/info/nodes.c --- tag-node-5419/trunk/info/nodes.c 2014-02-28 20:18:25.000000000 +0000 +++ parse-node-5419/trunk/info/nodes.c 2014-02-28 21:30:55.000000000 +0000 @@ -33,10 +33,10 @@ #endif /* HANDLE_MAN_PAGES */ static void remember_info_file (FILE_BUFFER *file_buffer); -static void free_file_buffer_tags (FILE_BUFFER *file_buffer); static void free_info_tag (NODE *tag); static void get_nodes_of_tags_table (FILE_BUFFER *file_buffer, SEARCH_BINDING *buffer_binding); +void parse_node (NODE *node); static void get_nodes_of_info_file (FILE_BUFFER *file_buffer); static void get_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer, SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding); @@ -47,7 +47,7 @@ static FILE_BUFFER *info_find_file_inter static NODE *info_node_of_file_buffer_tags (FILE_BUFFER *file_buffer, char *nodename); -static long get_node_length (SEARCH_BINDING *binding); +long get_node_length (SEARCH_BINDING *binding); /* Magic number that RMS used to decide how much a tags table pointer could be off by. I feel that it should be much smaller, like 4. */ @@ -68,11 +68,14 @@ FILE_BUFFER **info_loaded_files = NULL; /* The number of slots currently allocated to LOADED_FILES. */ size_t info_loaded_files_slots = 0; + +/* Whether to strip syntax from the text of nodes. */ +int preprocess_nodes_p; /* Public functions for node manipulation. */ /* Used to build `dir' menu from `localdir' files found in INFOPATH. */ -extern void maybe_build_dir_node (char *dirname); +extern FILE_BUFFER *maybe_build_dir_node (char *dirname); /* Return a pointer to a NODE structure for the Info node (FILENAME)NODENAME. If FILENAME is NULL, `dir' is used. @@ -87,47 +90,59 @@ info_get_node (char *filename, char *nod NODE *node; FILE_BUFFER *file_buffer = NULL; + char *saved_filename = 0, *saved_nodename = 0; + info_recent_file_error = NULL; info_parse_node (nodename, flag); nodename = NULL; + /* We need to duplicate info_parsed_filename because it can be rewritten + when info_get_node_of_file_buffer is called below */ + /* strdup filename as well so we can free saved_filename in both cases. */ + /* FIXME: Change info_parse_node to make all this easier */ + if (info_parsed_filename) - filename = info_parsed_filename; + saved_filename = strdup(info_parsed_filename); + else if (filename) + saved_filename = strdup(filename); if (info_parsed_nodename) - nodename = info_parsed_nodename; + saved_nodename = strdup(info_parsed_nodename); + else if (nodename) + saved_nodename = strdup(nodename); + /* See comment above for use of strdup() */ /* If FILENAME is not specified, it defaults to "dir". */ - if (!filename) - filename = "dir"; + if (!saved_filename) + saved_filename = strdup("dir"); - /* If the file to be looked up is "dir", build the contents from all of - the "dir"s and "localdir"s found in INFOPATH. */ - if (is_dir_name (filename)) - maybe_build_dir_node (filename); + /* Both saved_filename and saved_nodename are set by now. */ + + file_buffer = info_find_file (saved_filename); /* Find the correct info file, or give up. */ - file_buffer = info_find_file (filename); if (!file_buffer) { - node = make_manpage_node (filename); + node = make_manpage_node (saved_filename); if (!node) { if (filesys_error_number) info_recent_file_error = - filesys_error_string (filename, filesys_error_number); + filesys_error_string (saved_filename, filesys_error_number); + free(saved_filename); free(saved_nodename); return NULL; } } else /* Look for the node. */ - node = info_get_node_of_file_buffer (nodename, file_buffer); + node = info_get_node_of_file_buffer (saved_nodename, file_buffer); /* If the node not found was "Top", try again with different case, unless this was a man page. */ if (!node - && mbscasecmp (filename, MANPAGE_FILE_BUFFER_NAME) != 0 - && (nodename == NULL || mbscasecmp (nodename, "Top") == 0)) + && mbscasecmp (saved_filename, MANPAGE_FILE_BUFFER_NAME) != 0 + && (saved_nodename == NULL + || mbscasecmp (saved_nodename, "Top") == 0)) { node = info_get_node_of_file_buffer ("Top", file_buffer); if (!node) @@ -136,6 +151,7 @@ info_get_node (char *filename, char *nod node = info_get_node_of_file_buffer ("TOP", file_buffer); } + free(saved_filename); free(saved_nodename); return node; } @@ -244,6 +260,13 @@ info_find_file_internal (char *filename, int i; FILE_BUFFER *file_buffer; + /* If the file to be looked up is "dir", build the contents from all of + the "dir"s and "localdir"s found in INFOPATH. */ + if (is_dir_name (filename)) + { + return maybe_build_dir_node (filename); + } + /* First try to find the file in our list of already loaded files. */ if (info_loaded_files) { @@ -466,18 +489,21 @@ build_tags_and_nodes (FILE_BUFFER *file_ file_buffer->flags |= N_HasTagsTable; tags_table_begin = position; - /* If this isn't an indirect tags table, just remember the nodes - described locally in this tags table. Note that binding.end - is pointing to just after the beginning label. */ + /* If this isn't an indirect tags table, build tag table + ourselves and ignore the tag table in the file. Note that + binding.end is pointing to just after the beginning label. */ binding.start = binding.end; binding.end = file_buffer->filesize; if (!looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, &binding)) { + /* Tags table in non-split file */ + binding.start = tags_table_begin; binding.end = tags_table_end; get_nodes_of_tags_table (file_buffer, &binding); - return; + + break; } else { @@ -505,11 +531,511 @@ build_tags_and_nodes (FILE_BUFFER *file_ } } - /* This file doesn't contain any kind of tags table. Grovel the - file and build node entries for it. */ + /* If no tags table, build node list ourselves. */ + /* For non-split files, build node list, but keep offset information + * tags tables. We need this for meaningful anchor offsets. */ get_nodes_of_info_file (file_buffer); } +static size_t output_allocated; +static size_t output_length; +static char **output_start; + +static void +init_output_stream (char **o_s) +{ + output_allocated = 0; + output_length = 0; + output_start = o_s; +} + +static void +write_and_advance (char **output, char *input, size_t n) +{ + if (preprocess_nodes_p) + { + output_length += n; + while (output_allocated < output_length) + { + size_t offset; + if (output_allocated == 0) + { + output_allocated = 8; /* Initial allocation */ + offset = 0; + } + else + { + offset = *output - *output_start; + } + + *output_start = x2realloc (*output_start, &output_allocated); + *output = *output_start + offset; + } + memmove (*output, input, n); + *output += n; + } +} + +/* Turn off underlining */ +static void +underlining_off (char **output) +{ + write_and_advance (output, "\033[24m", 5); +} + +static void +underlining_on (char **output) +{ + write_and_advance (output, "\033[4m", 4); +} + +/* Read first line of node and set next, prev and up. Advance INPTR past + the first line. */ +static void +parse_top_node_line (NODE *node, char **inptr) +{ + char *nodeptr = *inptr; + char **store_in; + int value_length; + + node->next = node->prev = node->up = 0; + + while (1) + { + store_in = 0; + + nodeptr += skip_whitespace (nodeptr); + + /* Check what field we are looking at */ + if (!strncmp (nodeptr, INFO_FILE_LABEL, strlen(INFO_FILE_LABEL))) + { + nodeptr += strlen(INFO_FILE_LABEL); + } + else if (!strncmp (nodeptr, INFO_NODE_LABEL, strlen(INFO_NODE_LABEL))) + { + nodeptr += strlen(INFO_NODE_LABEL); + } + else if (!strncmp (nodeptr, INFO_PREV_LABEL, strlen(INFO_PREV_LABEL))) + { + nodeptr += strlen(INFO_PREV_LABEL); + store_in = &(node->prev); + } + else if (!strncmp (nodeptr, INFO_NEXT_LABEL, strlen(INFO_NEXT_LABEL))) + { + nodeptr += strlen(INFO_NEXT_LABEL); + store_in = &(node->next); + } + else if (!strncmp (nodeptr, INFO_UP_LABEL, strlen(INFO_UP_LABEL))) + { + nodeptr += strlen(INFO_UP_LABEL); + store_in = &(node->up); + } + else + { + /* Not recognized - code below will skip to next comma */ + } + + nodeptr += skip_whitespace (nodeptr); + + /* PARSE_NODE_START separates at commas or newlines, so it + will work for filenames including full stops. */ + value_length = skip_node_characters (nodeptr, PARSE_NODE_START); + + if (store_in) + { + (*store_in) = xmalloc (value_length + 1); + memmove (*store_in, nodeptr, value_length); + (*store_in) [value_length] = '\0'; + } + + nodeptr += value_length; + if ((*nodeptr) == '\n') + { + nodeptr++; + break; + } + nodeptr++; /* Point after field terminator */ + } + *inptr = nodeptr; +} + +/* Call search_forward() as if it were regexp_search() */ +static enum search_result +wrap_search_forward (char *regexp, + SEARCH_BINDING *binding, + long *poff, + SEARCH_BINDING *pret) +{ + return search_forward (regexp, binding, poff); +} + +static int +colon_after_newline (char *nodeptr) +{ + int nl, colon_offset; + + /* Check if a newline intervenes */ + nl = skip_line (nodeptr); + colon_offset = string_in_line (":", nodeptr + nl); + if (colon_offset != -1) + return nl + colon_offset; + else + return -1; +} + +/* Remove syntax from node->contents and build list of references + in node. */ +void +parse_node (NODE *node) +{ + SEARCH_BINDING s; + char *search_string; + + char *nodeptr; + char *new_contents = 0, *outptr = 0; + + int found_menu_entry, in_index = 0; + + REFERENCE **refs = NULL; + size_t refs_index = 0, refs_slots = 0; + + /* Used to correct line offsets in index entries */ + int deleted_lines = 0; + + long position; + + init_output_stream (&new_contents); + + /* Initialize refs to point to array of one null pointer in case + * there are no results. This way we know if refs has been initialized + * even if it is empty. */ + refs = calloc (1, sizeof *refs); + + refs_slots = 1; + + nodeptr = node->contents; + + parse_top_node_line (node, &nodeptr); + deleted_lines++; + + /* Search for menu items or cross references in buffer. */ + /* INFO_MENU_LABEL "|" INFO_XREF_LABEL */ + search_string = "\\n\\* Menu:|\\*Note"; + + s.buffer = node->contents; + s.start = nodeptr - node->contents; + s.end = node->nodelen; +search_again: + + s.flags = S_FoldCase | S_SkipDest; + + while (regexp_search (search_string, + &s, &position, 0) == search_success) + { + /* Save offset of "*" starting link. When preprocess_nodes is Off, + we position the cursor on the * when moving to a link. */ + int start_of_reference; + + /* Cross-references can be generated by four different Texinfo + commands. @inforef and @xref output "*Note " in Info format, + and "See" in HTML and print. @ref and @pxref output "*note " + in Info format, and either nothing at all or "see" in HTML + and print. Unfortunately, there seems to be no way to distinguish + between these latter two cases (unless the terminating punctuation + (full stop or comma) means something). We must make do with + displayed manuals occasionally containing "See see" and the + like. */ + int capital_s; + + int colon_offset; + REFERENCE *entry; + char *copy_to; + char *labelptr; + int leading_whitespace; + int newline_offset; + + /* Pointer to search result (after match) */ + copy_to = s.buffer + position; + + /* Was "* Menu:" seen? If so, search for menu entries hereafter */ + if (*(copy_to - 1) == ':') + { + /* INFO_MENU_ENTRY_LABEL "|" INFO_XREF_LABEL */ + search_string = "\\n\\* |\\*Note"; + + /* Write out up to Menu label, and skip it */ + copy_to -= 8; + write_and_advance (&outptr, nodeptr, copy_to - nodeptr); + + nodeptr = copy_to + 8; + deleted_lines++; + s.start = nodeptr - s.buffer; + continue; + } + + /* Check what we found based on last character of match */ + if (*(copy_to - 1) == ' ') + { + found_menu_entry = 1; + start_of_reference = copy_to - node->contents - 2; + } + else + { + found_menu_entry = 0; + + capital_s = copy_to[-4] == 'N'; + start_of_reference = copy_to - node->contents - 5; + copy_to -= 5; /* Point to before link */ + } + + /* Write out up to current reference */ + write_and_advance (&outptr, nodeptr, copy_to - nodeptr); + + /* Skip notation */ + if (found_menu_entry) + nodeptr = copy_to; + else + nodeptr = copy_to + 5; + + /* Search forward to ":" to get label name */ + nodeptr += skip_whitespace (nodeptr); + colon_offset = string_in_line (":", nodeptr); + + /* Cross-references may have a newline in the middle. */ + if (colon_offset == -1 + && !found_menu_entry + && (colon_offset = colon_after_newline (nodeptr)) != -1) + ; + else if (colon_offset == -1) + { + /* This is not a menu entry or reference. */ + /* See mysql manual (mysql-nutshell) */ + /* FIXME: set nodeptr for both xrefs and mitems, making + sure we output everything we should */ + nodeptr++; + + s.start = nodeptr - s.buffer; + continue; + } + colon_offset--; /* Offset of colon, not character after it. */ + + /* We definitely have a reference by this point. Create + REFERENCE entity */ + entry = xmalloc (sizeof (REFERENCE)); + entry->filename = NULL; + entry->nodename = NULL; + entry->label = NULL; + entry->line_number = 0; + if (found_menu_entry) + entry->type = REFERENCE_MENU_ITEM; + else + entry->type = REFERENCE_XREF; + + /* Save label as it appears in input. */ + entry->label = xmalloc(colon_offset + 1); + strncpy (entry->label, nodeptr, colon_offset); + entry->label[colon_offset] = '\0'; + + /* Output reference text and set location of reference */ + + if (!found_menu_entry) + { + if (capital_s) + write_and_advance (&outptr, "See ", 4); + else + write_and_advance (&outptr, "see ", 4); + } + + /* Output any whitespace or newlines before reference label */ + leading_whitespace = skip_whitespace_and_newlines (entry->label); + write_and_advance (&outptr, entry->label, leading_whitespace); + + underlining_on (&outptr); + + /* Point reference to where we will put the displayed reference, + which could be after whitespace. */ + if (preprocess_nodes_p) + { + entry->start = outptr - new_contents; + } + else + { + /* FIXME: Check this is character-perfect */ + //entry->start = nodeptr - node->contents; + entry->start = start_of_reference; + } + + entry->end = entry->start + strlen (entry->label) + - leading_whitespace; + + /* Write text of label. If there is a newline in the middle of + a reference label, turn off underling until text starts again. */ + labelptr = entry->label + leading_whitespace; + while (*labelptr != '\n' && *labelptr) labelptr++; + newline_offset = labelptr - (entry->label + leading_whitespace); + + write_and_advance(&outptr, entry->label + leading_whitespace, + (*labelptr ? newline_offset : + strlen(entry->label) - leading_whitespace)); + + if (*labelptr) + { + int space_at_start_of_line; + + space_at_start_of_line = skip_whitespace_and_newlines (labelptr); + + /* Note we do this before the newline is output. This way if + the first half of the label is on the bottom line of the + screen, underlining will not be left on. */ + underlining_off (&outptr); + + /* Output newline and any whitespace at start of line */ + write_and_advance (&outptr, labelptr, space_at_start_of_line); + labelptr += space_at_start_of_line; + + underlining_on (&outptr); + + /* Output rest of label */ + write_and_advance (&outptr, labelptr, + entry->label + strlen(entry->label) - labelptr); + + /* Text of reference ends later in node because of terminal + control characters that were output. */ + if (preprocess_nodes_p) + { + entry->end += strlen ("\033[4m"); + entry->end += strlen ("\033[24m"); + } + } + + underlining_off (&outptr); + + /* We've output the label, so now we can canonicalize it. */ + canonicalize_whitespace (entry->label); + + /* Read from after ':' to get target of reference. */ + nodeptr += colon_offset; nodeptr++; + + /* If this reference entry continues with another ':' then the + * nodename is the same as the label. */ + if (*nodeptr == ':') + { + entry->nodename = xstrdup (entry->label); + + nodeptr++; + if (found_menu_entry) + { + /* Output two spaces to match the length of "::" */ + write_and_advance (&outptr, " ", 2); + } + } + else + { + /* This entry continues with a specific nodename. Parse the + nodename from the specification. */ + + /* Defined in info-utils.c */ + extern int info_parsed_line_number; + int length; /* Length of specification */ + int i; + + if (found_menu_entry) + /* Fixme: if newline in node specifier, don't erase it */ + length = info_parse_node (nodeptr, PARSE_NODE_DFLT); + else + length = info_parse_node (nodeptr, PARSE_NODE_SKIP_NEWLINES); + + if (info_parsed_filename) + entry->filename = xstrdup (info_parsed_filename); + + if (info_parsed_nodename) + entry->nodename = xstrdup (info_parsed_nodename); + + if (!preprocess_nodes_p) + entry->line_number = info_parsed_line_number; + else + /* Adjust line offset in file to one in displayed text */ + entry->line_number = info_parsed_line_number - deleted_lines; + + if (in_index) + { + /* For index nodes, output the destination as well, + which will be the name of the node the index entry + refers to. */ + write_and_advance (&outptr, nodeptr, length); + } + + nodeptr += length; + + if (found_menu_entry && !in_index) + /* Output spaces the length of the node specifier to avoid + messing up left edge of second column of menu. */ + for (i = 0; i < length; i++) + write_and_advance (&outptr, " ", 1); + } + add_pointer_to_array (entry, refs_index, refs, refs_slots, 50); + + s.start = nodeptr - s.buffer; + if (s.start >= s.end) break; + } + + /* Search may have stopped too early because of null byte + in index marker ("address@hidden@^H]") or in image marker + ("address@hidden address@hidden"). Skip past these and try again. */ + char *ptr_to_null_byte; + + ptr_to_null_byte = nodeptr + strlen (nodeptr); + + /* Three byte sequence "address@hidden" starts tag. Check there is enough + space for this in the rest of the node. */ + if (ptr_to_null_byte <= node->contents + node->nodelen - 3) + { + /* FIXME: Can we use anything in tag.c ? */ + /* FIXME: Check what kind of a tag it is */ + + ptr_to_null_byte += 3; + + /* Output is different for index nodes */ + if (!strcmp ("index", ptr_to_null_byte)) + in_index = 1; + + /* Go to second null byte */ + ptr_to_null_byte += strlen (ptr_to_null_byte); + + /* Three byte sequence "address@hidden" ends tag */ + if (ptr_to_null_byte <= node->contents + node->nodelen - 3) + { + /* Write out up to tag. */ + write_and_advance (&outptr, nodeptr, + ptr_to_null_byte + 3 - nodeptr); + + /* Point to first character after ']' */ + nodeptr = ptr_to_null_byte + 3; + + s.buffer = nodeptr; + s.start = 0; + s.end = node->nodelen - (nodeptr - node->contents); + goto search_again; + } + } + + /* Check this because nodeptr can go past end of node if file is + ill-formed. (mysql 5.5 - 5.5.30 manual, node + "performance-schema-setup-tables") */ + if (nodeptr < node->contents + node->nodelen) + /* Write out rest of node. */ + write_and_advance (&outptr, nodeptr, + (node->contents + node->nodelen) - nodeptr); + + node->references = refs; + + if (preprocess_nodes_p) + { + node->content_cache = new_contents; + node->nodelen = outptr - new_contents; + } +} + /* Search through FILE_BUFFER->contents building an array of NODE *, one entry per each node present in the file. Store the tags in FILE_BUFFER->tags, and the number of allocated slots in @@ -564,34 +1090,44 @@ get_nodes_of_info_file (FILE_BUFFER *fil /* Okay, we have isolated the node name, and we know where the node starts. Remember this information. */ entry = xmalloc (sizeof (NODE)); - entry->content_cache = NULL; entry->nodename = xmalloc (1 + (end - start)); strncpy (entry->nodename, nodeline + start, end - start); entry->nodename[end - start] = 0; - entry->nodestart = nodestart; + + /* Initialize sundry fields */ + entry->parent = 0; + entry->display_pos = 0; + entry->content_cache = NULL; + if (anchor) - entry->nodelen = 0; + { + entry->nodestart = nodeline - binding.buffer; + entry->nodelen = 0; + entry->references = 0; + entry->contents = 0; + } else { - SEARCH_BINDING node_body; + /* Record that the node hasn't been parsed yet. */ + entry->nodelen = -1; + + // entry->nodestart = 0; /* Ignored for non-anchors */ + entry->nodestart = nodeline - binding.buffer; + entry->contents = nodeline; - node_body.buffer = binding.buffer + binding.start; - node_body.start = 0; - node_body.end = binding.end - binding.start; - node_body.flags = S_FoldCase; - entry->nodelen = get_node_length (&node_body); + entry->references = 0; } entry->filename = file_buffer->fullpath; - /* Add this tag to the array of tag structures in this FILE_BUFFER. */ + /* Add entry to the array of node structures in this FILE_BUFFER. */ add_pointer_to_array (entry, tags_index, file_buffer->tags, file_buffer->tags_slots, 100); } } /* Return the length of the node which starts at BINDING. */ -static long +long get_node_length (SEARCH_BINDING *binding) { int i; @@ -608,6 +1144,8 @@ get_node_length (SEARCH_BINDING *binding /* Build and save the array of nodes in FILE_BUFFER by searching through the contents of BUFFER_BINDING for a tags table, and groveling the contents. */ +/* FIXME: Can we rename this "read_tag_table" to avoid confusion with the + other function? */ static void get_nodes_of_tags_table (FILE_BUFFER *file_buffer, SEARCH_BINDING *buffer_binding) @@ -671,6 +1209,10 @@ get_nodes_of_tags_table (FILE_BUFFER *fi entry = xmalloc (sizeof (NODE)); entry->content_cache = NULL; + /* Has to be initialized in case entry is freed. */ + entry->references = 0; + entry->contents = 0; + /* Find the beginning of the node definition. */ tmp_search->start += name_offset; nodedef = tmp_search->buffer + tmp_search->start; @@ -874,6 +1416,17 @@ get_tags_of_indirect_tags_table (FILE_BU free (subfiles[i]); } free (subfiles); + + /* Create reference tables for nodes in indirect tag table */ + /* Will have to wait until after subfiles are loaded */ + { + NODE **t = file_buffer->tags; + + for (; *t != 0; t++) + { + (*t)->references = 0; /* FIXME */ + } + } } @@ -945,6 +1498,82 @@ find_node_of_anchor (FILE_BUFFER *file_b return node; } +static void +overlay_subfile_tag_table (FILE_BUFFER *file_buffer, + FILE_BUFFER *subfile) +{ + NODE **subfile_tags, **topfile_tags; + NODE *file_node; + + int j, first_tag_in_subfile = -1, last_tag_in_subfile = -1; + int j2; + + get_nodes_of_info_file (subfile); + subfile_tags = subfile->tags; + topfile_tags = file_buffer->tags; + + for (j = 0; file_node = file_buffer->tags[j]; j++) + { + if (!strcmp (subfile->filename, file_node->filename)) + { + first_tag_in_subfile = j; + break; + } + } + + if (first_tag_in_subfile == -1) + { + printf ("Impossible!\n"); exit(23); + } + + for (; file_node = file_buffer->tags[j]; j++) + { + /* FIXME: Use subfile offsets instead for greater + * efficiency. */ + if (strcmp (subfile->filename, file_node->filename)) + { + break; + } + } + last_tag_in_subfile = j - 1; + + /* Copy data into master tag table */ + for (j = first_tag_in_subfile, j2 = 0; + j <= last_tag_in_subfile; + j++) + { + int topfile_nodestart; + + /* If it is an anchor, there will be no corresponding + * entry in the subfile node list, so skip. Set by + * get_nodes_of_tags_table(). FIXME: better way of + * knowing if it is an anchor? */ + if (file_buffer->tags[j]->nodelen == 0) + { + continue; + } + + if (strcmp (topfile_tags[j]->nodename, subfile_tags[j2]->nodename)) + { + /* FIXME: exit properly */ + printf("Subfile tags different from master tag table!\n"); + exit(23); + } + + topfile_nodestart = topfile_tags[j]->nodestart; + *(topfile_tags[j]) = *(subfile_tags[j2]); + topfile_tags[j]->nodestart = topfile_nodestart; + + j2++; + } + + /* Don't track node contents for subfile. This prevents double freeing + problems in info_gc_file_buffers. */ + subfile->tags = 0; + subfile->tags_slots = 0; + +} + /* Return the node from FILE_BUFFER which matches NODENAME by searching the tags table in FILE_BUFFER, or NULL. */ static NODE * @@ -961,6 +1590,7 @@ info_node_of_file_buffer_tags (FILE_BUFF if (strcmp (nodename, tag->nodename) == 0) { NODE *node; + /* If not a split file, subfile == file_buffer */ FILE_BUFFER *subfile = info_find_file_internal (tag->filename, INFO_NO_TAGS); if (!subfile) @@ -973,41 +1603,25 @@ info_node_of_file_buffer_tags (FILE_BUFF return NULL; } - /* If we were able to find this file and load it, then return - the node within it. */ - if (!(tag->nodestart >= 0 && tag->nodestart < subfile->filesize)) - return NULL; + /* For compound files, load subfile if not loaded already. + The tag->nodelen check checks that the tag isn't an anchor. + When subfile is loaded, tag->contents is set for all tags. */ + if (tag->nodelen != 0 + && tag->contents == 0 + && (file_buffer->flags & N_TagsIndirect)) /* FIXME: initialized? */ + overlay_subfile_tag_table (file_buffer, subfile); - /* FIXME: why not just copy the NODE structure instead of - assigning the fields individually? */ - node = xmalloc (sizeof (NODE)); - node->filename = subfile->fullpath; - node->parent = NULL; - node->nodename = tag->nodename; - - if (tag->content_cache) - node->contents = tag->content_cache; - else - node->contents = subfile->contents + tag->nodestart; + if (!tag->contents) + tag->contents = subfile->contents + tag->nodestart; + node = xmalloc (sizeof (NODE)); + /* FIXME: Why aren't these set already? */ + node->filename = subfile->fullpath; + node->parent = NULL; node->display_pos = 0; node->flags = 0; - node_set_body_start (node); - - if (file_buffer->flags & N_HasTagsTable) - { - node->flags |= N_HasTagsTable; - - if (file_buffer->flags & N_TagsIndirect) - { - node->flags |= N_TagsIndirect; - node->parent = file_buffer->fullpath; - } - } - - if (subfile->flags & N_IsCompressed) - node->flags |= N_IsCompressed; - + + /* If TAG->nodelen hasn't been calculated yet, then we aren't in a position to trust the entry pointer. Adjust things so that ENTRY->nodestart gets the exact address of the start of @@ -1040,10 +1654,9 @@ info_node_of_file_buffer_tags (FILE_BUFF this node, or NULL if the node wasn't found. NODE->contents is side-effected to point to right after the separator. */ - node_sep = adjust_nodestart (node, min, max); + node_sep = adjust_nodestart (tag, min, max); if (node_sep == NULL) { - free (node); return NULL; } /* Readjust tag->nodestart. */ @@ -1052,33 +1665,78 @@ info_node_of_file_buffer_tags (FILE_BUFF /* Calculate the length of the current node. */ buff_end = subfile->contents + subfile->filesize; - node_body.buffer = node->contents; + node_body.buffer = tag->contents; node_body.start = 0; node_body.end = buff_end - node_body.buffer; node_body.flags = 0; tag->nodelen = get_node_length (&node_body); - /* Expand eventual \b[...\b] constructs in the contents. - If found, update node->contents to point to the resulting - buffer. */ - if (tags_expand (node->contents, tag->nodelen, - &tag->content_cache, &tag->nodelen)) - node->contents = tag->content_cache; - node->nodelen = tag->nodelen; + + /* Initialize so we can know if parse_node or tags_expand + did anything. */ + tag->content_cache = 0; + + /* Read locations of references in node and similar. Rewrite + node from tag->contents to tag->content_cache if + preprocess_nodes=On. */ + /* TODO: Should we merge this with the previous step? */ + /* This has to be called before tags_expand because + tags_expand strips out the index markers, which we need + to alter the style of output indices. */ + + parse_node (tag); + if (tag->content_cache) + tag->contents = tag->content_cache; + + char *old_contents = tag->content_cache; + + /* Expand eventual \b[...\b] constructs in the contents. */ + + int new_nodelen; + if (tags_expand (tag->contents, tag->nodelen, + &tag->content_cache, &new_nodelen)) + { + tag->nodelen = new_nodelen; + + /* Free output of parse_node. */ + if (old_contents) + free (old_contents); + } + *node = *tag; } else if (tag->nodelen == 0) /* anchor, return containing node */ { - free (node); + free(node); node = find_node_of_anchor (file_buffer, tag); } else { + *node = *tag; + /* Since we know the length of this node, we have already adjusted tag->nodestart to point to the exact start of it. Simply skip the node separator. */ node->contents += skip_node_separator (node->contents); - node->nodelen = tag->nodelen; } + if (tag->content_cache) + node->contents = tag->content_cache; + + node_set_body_start (node); + + if (file_buffer->flags & N_HasTagsTable) + { + node->flags |= N_HasTagsTable; + + if (file_buffer->flags & N_TagsIndirect) + { + node->flags |= N_TagsIndirect; + node->parent = file_buffer->fullpath; + } + } + + if (subfile->flags & N_IsCompressed) + node->flags |= N_IsCompressed; + return node; } @@ -1155,7 +1813,7 @@ forget_info_file (char *filename) } /* Free the tags (if any) associated with FILE_BUFFER. */ -static void +void free_file_buffer_tags (FILE_BUFFER *file_buffer) { int i; @@ -1186,6 +1844,7 @@ free_file_buffer_tags (FILE_BUFFER *file static void free_info_tag (NODE *tag) { + if (tag->references) info_free_references (tag->references); free (tag->nodename); free (tag->content_cache); @@ -1196,11 +1855,7 @@ free_info_tag (NODE *tag) free (tag); } -/* Load the contents of FILE_BUFFER->contents. This function is called - when a file buffer was loaded, and then in order to conserve memory, the - file buffer's contents were freed and the pointer was zero'ed. Note that - the file was already loaded at least once successfully, so the tags and/or - nodes members are still correctly filled. */ +/* Load the contents of FILE_BUFFER->contents and rebuild tag table. */ static void info_reload_file_buffer_contents (FILE_BUFFER *fb) { @@ -1220,6 +1875,8 @@ info_reload_file_buffer_contents (FILE_B &is_compressed); if (is_compressed) fb->flags |= N_IsCompressed; + + build_tags_and_nodes (fb); } /* Return the actual starting memory location of NODE, side-effecting diff -up -x 'Makefile*' -x '*.o' -x '*~' tag-node-5419/trunk/info/nodes.h parse-node-5419/trunk/info/nodes.h --- tag-node-5419/trunk/info/nodes.h 2014-02-28 20:32:53.000000000 +0000 +++ parse-node-5419/trunk/info/nodes.h 2014-02-28 21:06:40.000000000 +0000 @@ -39,14 +39,21 @@ typedef struct { char *parent; /* Non-null is the logical file name. */ char *nodename; /* The name of this node. */ char *contents; /* Characters appearing in this node. */ - size_t nodelen; /* The length of the CONTENTS member. */ + size_t nodelen; /* The length of the CONTENTS member. + nodelen == -1 if length is unknown + because node hasn't been read yet. + nodelen == 0 if it is an anchor. */ unsigned long display_pos; /* Where to display at, if nonzero. */ long body_start; /* Offset of the actual node body */ int flags; /* See immediately below. */ + REFERENCE **references; /* Cross-references or menu items in node. + references == 0 implies uninitialized, + not empty */ long nodestart; /* The offset of the start of this node. */ char *content_cache; /* Cache of the node contents; used if the node contents must be preprocessed before displaying it. */ + char *up, *prev, *next; /* Names of nearby nodes. */ } NODE; /* Defines that can appear in NODE->flags. All informative. */ @@ -138,6 +145,10 @@ extern NODE *info_get_node (char *filena extern NODE *info_get_node_of_file_buffer (char *nodename, FILE_BUFFER *file_buffer); +/* If preprocess_nodes_p=On, remove syntax from NODE->contents and + place in NODE->content_cache. Set other fields of NODE. */ +void parse_node (NODE *node); + /* Grovel FILE_BUFFER->contents finding tags and nodes, and filling in the various slots. This can also be used to rebuild a tag or node table. */ extern void build_tags_and_nodes (FILE_BUFFER *file_buffer); @@ -150,4 +161,6 @@ extern FILE_BUFFER *make_file_buffer (vo void forget_info_file (char *filename); +void free_file_buffer_tags (FILE_BUFFER *file_buffer); + #endif /* not NODES_H */ diff -up -x 'Makefile*' -x '*.o' -x '*~' tag-node-5419/trunk/info/session.c parse-node-5419/trunk/info/session.c --- tag-node-5419/trunk/info/session.c 2014-02-28 20:18:25.000000000 +0000 +++ parse-node-5419/trunk/info/session.c 2014-03-01 19:24:28.000000000 +0000 @@ -21,6 +21,8 @@ #include "info.h" #include "search.h" +#include "variables.h" + #ifndef __MINGW32__ #include #endif @@ -40,10 +42,12 @@ static void info_clear_pending_input (void); static void info_set_pending_input (unsigned char key); -static void info_handle_pointer (char *label, WINDOW *window); +static void info_goto_description (char *node_description, WINDOW *window); static void display_info_keyseq (int expecting_future_input); char *node_printed_rep (NODE *node); +static REFERENCE *select_menu_digit (WINDOW *window, unsigned char key); + /* **************************************************************** */ /* */ /* Running an Info Session */ @@ -246,10 +250,8 @@ info_read_and_dispatch (void) /* Found in signals.c */ extern void initialize_info_signal_handler (void ); -/* Initialize the first info session by starting the terminal, window, - and display systems. If CLEAR_SCREEN is 0, don't clear the screen. */ void -initialize_info_session (NODE *node, int clear_screen) +initialize_terminal_and_keymaps (void) { char *term_name = getenv ("TERM"); terminal_initialize_terminal (term_name); @@ -263,13 +265,20 @@ initialize_info_session (NODE *node, int exit (EXIT_FAILURE); } + initialize_info_keymaps (); +} + +/* Initialize the first info session by starting the terminal, window, + and display systems. If CLEAR_SCREEN is 0, don't clear the screen. */ +void +initialize_info_session (NODE *node, int 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); @@ -1127,18 +1136,6 @@ int default_scroll_size = -1; /* meani (info_parsed_nodename || (info_parsed_filename \ && !is_dir_name (info_parsed_filename))) -static int -last_node_p (NODE *node) -{ - info_next_label_of_node (node); - if (!INFO_LABEL_FOUND ()) - { - info_up_label_of_node (node); - return !INFO_LABEL_FOUND () || strcmp (info_parsed_nodename, "Top") == 0; - } - return 0; -} - /* Move to 1st menu item, Next, Up/Next, or error in this window. */ static int forward_move_node_structure (WINDOW *window, int behaviour) @@ -1150,21 +1147,22 @@ forward_move_node_structure (WINDOW *win return 1; case IS_NextOnly: - info_next_label_of_node (window->node); - if (!info_parsed_nodename && !info_parsed_filename) + if (!window->node->next) { info_error (msg_no_pointer, _("Next")); return 1; } else { - info_handle_pointer ("Next", window); + info_goto_description (window->node->next, window); } break; case IS_Continuous: { - if (last_node_p (window->node)) + /* If last node in file */ + if (!window->node->next && + !(window->node->up && strcmp ("Top", window->node->up))) { switch (scroll_last_node) { @@ -1184,27 +1182,24 @@ forward_move_node_structure (WINDOW *win } } - /* First things first. If this node contains a menu, move down - into the menu. */ + /* If this node contains a menu, select its first entry. */ { - REFERENCE **menu; + REFERENCE *entry; - menu = info_menu_of_node (window->node); - - if (menu) - { - info_free_references (menu); - info_menu_digit (window, 1, '1'); - return 0; - } + if (entry = select_menu_digit (window, '1')) + { + info_select_reference (window, entry); + if (entry->line_number > 0) + internal_next_line (window, entry->line_number - 1, '1'); + return 0; + } } /* 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_FOUND ()) + if (window->node->next) { - info_handle_pointer ("Next", window); + info_goto_description (window->node->next, window); return 0; } @@ -1224,55 +1219,33 @@ forward_move_node_structure (WINDOW *win up_counter = 0; while (!info_error_was_printed) { - info_up_label_of_node (window->node); - if (INFO_LABEL_FOUND ()) + if (window->node->up) { - info_handle_pointer ("Up", window); + info_goto_description (window->node->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_FOUND ()) + if (!window->node->next) 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; + /* If this node's Next pointer is the same as the + first menu item, keep backing up. */ - /* 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); - } + REFERENCE *first_menu_item; + first_menu_item = select_menu_digit (window, '1'); + if (first_menu_item + && !strcmp(window->node->next, + first_menu_item->nodename)) + { + continue; } /* This node has a "Next" pointer, and it is not the same as the first menu item found in this node. */ - info_handle_pointer ("Next", window); + info_goto_description (window->node->next, window); return 0; } else @@ -1315,83 +1288,75 @@ backward_move_node_structure (WINDOW *wi return 1; case IS_NextOnly: - info_prev_label_of_node (window->node); - if (!info_parsed_nodename && !info_parsed_filename) + if (!window->node->prev) { - info_error ("%s", _("No `Prev' for this node.")); + info_error (msg_no_pointer, _("Prev")); return 1; } else { - info_handle_pointer ("Prev", window); + info_goto_description (window->node->next, window); } break; case IS_Continuous: - info_prev_label_of_node (window->node); - - if (!info_parsed_nodename && (!info_parsed_filename - || is_dir_name (info_parsed_filename))) + if (window->node->up) { - info_up_label_of_node (window->node); - if (!info_parsed_nodename && (!info_parsed_filename - || is_dir_name (info_parsed_filename))) + int traverse_menus = 0; + + /* If up is the dir node, we are at the top node. + Don't do anything. */ + if (!strcmp ("(dir)", window->node->up)) { - info_error ("%s", - _("No `Prev' or `Up' for this node within this document.")); - return 1; } - else + /* If 'Prev' and 'Up' are the same, we are at the first node + of the 'Up' node's menu. Go to up node. */ + else if (window->node->prev + && !strcmp(window->node->prev, window->node->up)) { - info_handle_pointer ("Up", window); + info_goto_description (window->node->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) + /* Otherwise, go to 'Prev' node and go down the last entry + in the menus as far as possible. */ + else if (window->node->prev) { - 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); - } + traverse_menus = 1; + info_goto_description (window->node->prev, window); + } + else /* 'Up' but no 'Prev' */ + { + info_goto_description (window->node->up, window); } - /* 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. */ - info_handle_pointer ("Prev", window); - - if (!inhibit_menu_traversing) + /* Repeatedly select last item of menus */ + if (traverse_menus) { - while (!info_error_was_printed && - (menu = info_menu_of_node (window->node))) + REFERENCE *entry; + while (!info_error_was_printed) { - info_free_references (menu); - info_menu_digit (window, 1, '0'); + if (entry = select_menu_digit (window, '0')) + { + info_select_reference (window, entry); + if (entry->line_number > 0) + internal_next_line (window, entry->line_number - 1, '0'); + } + else + break; } } } + else if (window->node->prev) /* 'Prev' but no 'Up' */ + { + info_goto_description (window->node->prev, window); + } + else + { + info_error ("%s", + _("No `Prev' or `Up' for this node within this document.")); + return 1; + } + + break; } return 0; @@ -2126,13 +2091,12 @@ info_win_find_node (INFO_WINDOW *win, NO return i; } -/* 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. */ +/* Try to get node decribed by NODE_DESCRIPTION into WINDOW. */ +/* FIXME: difference between this and info_parse_and_select ? */ static void -info_handle_pointer (char *label, WINDOW *window) +info_goto_description (char *node_description, WINDOW *window) { + info_parse_node (node_description, PARSE_NODE_DFLT); if (info_parsed_filename || info_parsed_nodename) { char *filename, *nodename; @@ -2164,12 +2128,15 @@ info_handle_pointer (char *label, WINDOW info_win = get_info_window_of_window (window); if (info_win) { +#if 0 +/* No idea what's going on here. */ if (strcmp (label, "Up") == 0) { int i = info_win_find_node (info_win, node); if (i >= 0) node->display_pos = info_win->points[i]; } +#endif info_win->pagetops[info_win->current] = window->pagetop; info_win->points[info_win->current] = window->point; } @@ -2188,7 +2155,7 @@ info_handle_pointer (char *label, WINDOW } else { - info_error (msg_no_pointer, label); + /* "Error - no next node" */ } } @@ -2196,24 +2163,27 @@ info_handle_pointer (char *label, WINDOW displayed. */ DECLARE_INFO_COMMAND (info_next_node, _("Select the Next node")) { - info_next_label_of_node (window->node); - info_handle_pointer ("Next", window); + if (!window->node->next) return; + + info_goto_description (window->node->next, window); } /* Make WINDOW display the "Prev:" node of the node currently being displayed. */ DECLARE_INFO_COMMAND (info_prev_node, _("Select the Prev node")) { - info_prev_label_of_node (window->node); - info_handle_pointer ("Prev", window); + if (!window->node->prev) return; + + info_goto_description (window->node->prev, window); } /* Make WINDOW display the "Up:" node of the node currently being displayed. */ DECLARE_INFO_COMMAND (info_up_node, _("Select the Up node")) { - info_up_label_of_node (window->node); - info_handle_pointer ("Up", window); + if (!window->node->up) return; + + info_goto_description (window->node->up, window); } /* Make WINDOW display the last node of this info file. */ @@ -2291,13 +2261,14 @@ DECLARE_INFO_COMMAND (info_last_menu_ite 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")) +/* Return menu entry */ +static REFERENCE * +select_menu_digit (WINDOW *window, unsigned char key) { register int i, item; register REFERENCE **menu; - menu = info_menu_of_node (window->node); + menu = window->node->references; if (!menu) { @@ -2310,27 +2281,52 @@ DECLARE_INFO_COMMAND (info_menu_digit, _ /* Special case. Item "0" is the last item in this menu. */ if (item == 0) - for (i = 0; menu[i + 1]; i++); + { + i = -1; /* Not found */ + int j; + for (j = 0; menu[j]; j++) + if (menu[j]->type == REFERENCE_MENU_ITEM) + i = j; + if (i == -1) return 0; + } else { + int k = 0; for (i = 0; menu[i]; i++) - if (i == item - 1) - break; + { + if (menu[i]->type == REFERENCE_MENU_ITEM) + k++; + if (k == item) + break; + } } - if (menu[i]) + return menu[i]; +} + +/* Use KEY (a digit) to select the Nth menu item in WINDOW->node. */ +DECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item")) +{ + int item = key - '0'; + REFERENCE *entry; + if (entry = select_menu_digit (window, key)) { - info_select_reference (window, menu[i]); - if (menu[i]->line_number > 0) - internal_next_line (window, menu[i]->line_number - 1, key); + info_select_reference (window, entry); + if (entry->line_number > 0) + internal_next_line (window, entry->line_number - 1, key); + } + else if (key == '0') + { + /* Don't print "There aren't 0 items in this menu." */ + info_error ("%s", msg_no_menu_node); } else - info_error (ngettext ("There isn't %d item in this menu.", - "There aren't %d items in this menu.", - item), - item); - - info_free_references (menu); + { + info_error (ngettext ("There isn't %d item in this menu.", + "There aren't %d items in this menu.", + item), + item); + } return; } @@ -2398,117 +2394,95 @@ nearest_xref (REFERENCE **xref_list, lon } } +static int exclude_cross_references (REFERENCE *r) +{ + return r->type == REFERENCE_XREF; +} + +static int exclude_menu_items (REFERENCE *r) +{ + return r->type == REFERENCE_MENU_ITEM; +} + +static int exclude_nothing (REFERENCE *r) +{ + return 1; +} /* 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. */ + reading is done with completion. ASK_P is non-zero if the user should + be prompted, or zero to select the item on the current line. MENU_ITEM + and XREF control whether menu items and cross-references are eligible + for selection. */ static void -info_menu_or_ref_item (WINDOW *window, int count, - unsigned char key, REFERENCE **(*builder) (NODE *node), int ask_p) +info_menu_or_ref_item (WINDOW *window, unsigned char key, + int menu_item, int xref, int ask_p) { - char *line; + REFERENCE *defentry = NULL; /* Default link */ + REFERENCE **refs = window->node->references; REFERENCE *entry; - REFERENCE *defentry = NULL; - REFERENCE **menu = (*builder) (window->node); - if (!menu) + /* Name of destination */ + char *line; + + reference_bool_fn exclude; + if (menu_item && !xref) { - if (builder == info_menu_of_node) - info_error ("%s", msg_no_menu_node); - else - info_error ("%s", msg_no_xref_node); - return; + exclude = &exclude_cross_references; } + else if (!menu_item && xref) + { + exclude = &exclude_menu_items; + } + else if (menu_item && xref) + { + exclude = &exclude_nothing; + } + else /* !menu_item && !xref */ + return; - /* Default the selected reference to the one which is on the line that - point is in. */ - { - REFERENCE **refs = NULL; - int point_line = window_line_of_point (window); - - if (point_line != -1) - { - SEARCH_BINDING binding; + int line_no; + int this_line, next_line; - 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; + int which, closest = -1; - 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 -#endif /* HANDLE_MAN_PAGES */ - refs = nearest_xref (menu, window->point); - } + /* Default the selected reference to the one which is on the line that + point is in. */ - if (refs && refs[0]) - { - if (strcmp (refs[0]->label, "Menu") != 0 - || builder == info_xrefs_of_node) - { - int which = 0; + line_no = window_line_of_point (window); + this_line = window->line_starts[line_no] - window->node->contents; + if (window->line_starts[line_no + 1]) + next_line = window->line_starts[line_no + 1] - window->node->contents; + else + next_line = window->node->nodelen; - /* For xrefs, find the closest reference to point, - unless we only have one reference (as we will if - we've called nearest_xref above). It would be better - to have only one piece of code, but the conditions - when we call this are tangled. */ - if (builder == info_xrefs_of_node && refs[1]) - { - int closest = -1; + for (which = 0; refs[which]; which++) + { + /* Check the type of reference is one we are looking for. */ + if (!( (menu_item && refs[which]->type == REFERENCE_MENU_ITEM) + || (xref && refs[which]->type == REFERENCE_XREF))) + continue; - 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 (which > 0) - { - if (closest == -1) - which--; - else - which = closest; - } - } + /* If reference starts in current line, it is eligible */ + if ( refs[which]->start >= this_line + && refs[which]->start < next_line) + closest = which; + + /* If point is inside a reference, choose that one. */ + if ( window->point >= refs[which]->start + && window->point <= refs[which]->end) + { + closest = which; + break; + } - defentry = xmalloc (sizeof (REFERENCE)); - defentry->label = xstrdup (refs[which]->label); - defentry->filename = refs[which]->filename; - defentry->nodename = refs[which]->nodename; - defentry->line_number = refs[which]->line_number; - - if (defentry->filename) - defentry->filename = xstrdup (defentry->filename); - if (defentry->nodename) - defentry->nodename = xstrdup (defentry->nodename); - } - info_free_references (refs); - } - } - } + /* If we got to the next line without finding an eligible reference. */ + if (refs[which]->start >= next_line) + break; + } + if (closest != -1) + defentry = refs[closest]; /* If we are going to ask the user a question, do it now. */ if (ask_p) @@ -2516,7 +2490,7 @@ info_menu_or_ref_item (WINDOW *window, i char *prompt; /* Build the prompt string. */ - if (builder == info_menu_of_node) + if (menu_item && !xref) { if (defentry) { @@ -2539,7 +2513,8 @@ info_menu_or_ref_item (WINDOW *window, i prompt = xstrdup (_("Follow xref: ")); } - line = info_read_completing_in_echo_area (window, prompt, menu); + line = info_read_completing_in_echo_area_with_exclusions + (window, prompt, refs, exclude); free (prompt); window = active_window; @@ -2547,8 +2522,6 @@ info_menu_or_ref_item (WINDOW *window, i /* User aborts, just quit. */ if (!line) { - free (defentry); - info_free_references (menu); info_abort_key (window, 0, 0); return; } @@ -2590,7 +2563,8 @@ info_menu_or_ref_item (WINDOW *window, i int best = -1, min_dist = window->node->nodelen; REFERENCE *ref; - for (i = 0; menu && (ref = menu[i]); i++) + /* FIXME : menu is too big */ + for (i = 0; refs && (ref = refs[i]); i++) { /* Need to use mbscasecmp because LINE is downcased inside info_read_completing_in_echo_area. */ @@ -2608,7 +2582,7 @@ info_menu_or_ref_item (WINDOW *window, i } } if (best != -1) - entry = menu[best]; + entry = refs[best]; else entry = NULL; } @@ -2620,46 +2594,26 @@ info_menu_or_ref_item (WINDOW *window, i NODE *orig = window->node; info_select_reference (window, entry); - if (builder == info_xrefs_of_node && window->node != orig - && !(window->node->flags & N_FromAnchor)) - { /* Search for this reference in the node. */ - long offset; - long start; - - if (window->line_count > 0) - start = window->line_starts[1] - window->node->contents; - else - start = 0; + if (entry->line_number > 0) + /* next_line starts at line 1? Anyway, the -1 makes it + move to the right line. */ + internal_next_line (window, entry->line_number - 1, key); - offset = info_target_search_node (window->node, entry->label, - start, 0); - - window->point = (offset == -1) ? - window->node->body_start : offset; - window_adjust_pagetop (window); - } - - if (entry->line_number > 0) - /* next_line starts at line 1? Anyway, the -1 makes it - move to the right line. */ - internal_next_line (window, entry->line_number - 1, key); } free (line); - info_reference_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")) { - info_menu_or_ref_item (window, count, key, info_menu_of_node, 1); + info_menu_or_ref_item (window, key, 1, 0, 1); } /* Read a line (with completion) which is the name of a reference to @@ -2667,7 +2621,7 @@ DECLARE_INFO_COMMAND (info_menu_item, _( DECLARE_INFO_COMMAND (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); + info_menu_or_ref_item (window, key, 0, 1, 1); } /* Position the cursor at the start of this node's menu. */ @@ -2698,7 +2652,7 @@ DECLARE_INFO_COMMAND (info_visit_menu, register int i; REFERENCE *entry, **menu; - menu = info_menu_of_node (window->node); + menu = window->node->references; if (!menu) info_error ("%s", msg_no_menu_node); @@ -2707,6 +2661,8 @@ DECLARE_INFO_COMMAND (info_visit_menu, { WINDOW *new; + if (entry->type != REFERENCE_MENU_ITEM) continue; + new = window_make_window (window->node); window_tile_windows (TILE_INTERNALS); @@ -2833,8 +2789,8 @@ info_follow_menus (NODE *initial_node, c if (!first_arg) first_arg = arg; - /* Build and return a list of the menu items in this node. */ - menu = info_menu_of_node (initial_node); + /* FIXME: This contains cross-references as well. */ + menu = initial_node->references; /* If no menu item in this node, stop here, but let the user continue to use Info. Perhaps they wanted this node and didn't @@ -2899,7 +2855,6 @@ info_follow_menus (NODE *initial_node, c goto maybe_got_node; } - info_free_references (menu); if (err_node) *err_node = format_message_node (_("No menu item `%s' in node `%s'."), arg, @@ -2940,14 +2895,11 @@ info_follow_menus (NODE *initial_node, c _("Unable to find node referenced by `%s' in `%s'."), entry->label, node_printed_rep (initial_node)); - info_free_references (menu); return strict ? NULL : initial_node; } debug (3, ("node: %s, %s", node->filename, node->nodename)); - info_free_references (menu); - /* Success. Go round the loop again. */ free (initial_node); initial_node = node; @@ -3060,6 +3012,8 @@ entry_in_menu (char *arg, REFERENCE **me for (i = 0; (entry = menu[i]); i++) { + if (REFERENCE_MENU_ITEM != entry->type) continue; + if (mbscasecmp (entry->label, arg) == 0) break; else @@ -3115,8 +3069,7 @@ info_intuit_options_node (WINDOW *window { REFERENCE *entry = NULL; - /* Build and return a list of the menu items in this node. */ - menu = info_menu_of_node (initial_node); + menu = initial_node->references; /* If no menu item in this node, stop here. Perhaps this node is the one they need. */ @@ -3148,7 +3101,6 @@ info_intuit_options_node (WINDOW *window /* Try to find this node. */ node = info_get_node (entry->filename, entry->nodename, PARSE_NODE_VERBATIM); - info_free_references (menu); if (!node) break; } @@ -3394,6 +3346,10 @@ kill_node (WINDOW *window, char *nodenam point = stealer->points[which]; pagetop = stealer->pagetops[which]; + /* FIXME: do *copy = *temp so if new fields are added, this + doesn't break. */ + *copy = *temp; + /* copy->filename = temp->filename; copy->parent = temp->parent; copy->nodename = temp->nodename; @@ -3402,6 +3358,12 @@ kill_node (WINDOW *window, char *nodenam copy->flags = temp->flags; copy->display_pos = temp->display_pos; + copy->references = temp->references; + copy->prev = temp->prev; + copy->next = temp->next; + copy->up = temp->up; + */ + temp = copy; } @@ -3600,12 +3562,14 @@ dump_node_to_stream (char *filename, cha /* 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 = node->references; if (menu) { for (i = 0; menu[i]; i++) { + if (REFERENCE_MENU_ITEM != menu[i]->type) continue; + /* We don't dump Info files which are different than the current one. */ if (!menu[i]->filename) @@ -3613,7 +3577,6 @@ dump_node_to_stream (char *filename, cha stream, dump_subnodes) == DUMP_SYS_ERROR) return DUMP_SYS_ERROR; } - info_free_references (menu); } } @@ -4762,6 +4725,11 @@ info_gc_file_buffers (void) /* If this file buffer wasn't referenced, free its contents. */ if (!fb_referenced_p) { + /* If preprocess-nodes=On, content_cache will be set for all + nodes in the file that have been loaded, so it is worth + freeing them. */ + if (preprocess_nodes_p) + free_file_buffer_tags (fb); free (fb->contents); fb->contents = NULL; } @@ -4783,85 +4751,37 @@ info_move_to_xref (WINDOW *window, int c long placement = -1; long start = 0; NODE *node = window->node; + REFERENCE **ref; + int nextref; - 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, - NULL, dir, 0, 0); - - /* 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, NULL, dir, 0, 0); - } - - firstxref = - info_search_in_node (INFO_XREF_LABEL, node, start, NULL, dir, 0, 0); - -#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) + /* Fail if there are no references in node */ + if (!node->references) { if (!cursor_movement_scrolls_p) - info_error ("%s", msg_no_xref_node); + info_error ("%s", msg_no_xref_node); return cursor_movement_scrolls_p; } - /* 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, NULL, dir, 0, 0); - - nextxref = info_search_in_node (INFO_XREF_LABEL, node, - window->point + dir, NULL, dir, 0, 0); - -#if defined (HANDLE_MAN_PAGES) +#if 0 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, NULL, dir, 0, 0); - } +#endif - /* 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; - else - placement = nextxref; - } - else if (nextmenu != -1) - placement = nextmenu + 1; - else if (nextxref != -1) - placement = nextxref; + if (dir == 1) /* Search forwards */ + for (ref = node->references; *ref != 0; ref++) + { + if ((*ref)->start > window->point) + { + placement = (*ref)->start; + break; + } + } + else /* Search backwards */ + for (ref = node->references; *ref != 0; ref++) + { + if ((*ref)->start >= window->point) break; + placement = (*ref)->start; + } /* 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. */ @@ -4870,21 +4790,9 @@ info_move_to_xref (WINDOW *window, int c if (cursor_movement_scrolls_p) return 1; else - { - if (firstmenu != -1 && firstxref != -1) - { - if (((dir == 1) && (firstmenu < firstxref)) || - ((dir == -1) && (firstmenu > firstxref))) - placement = firstmenu + 1; - else - placement = firstxref; - } - else if (firstmenu != -1) - placement = firstmenu + 1; - else - placement = firstxref; - } + placement = node->references[0]->start; } + window->point = placement; window_adjust_pagetop (window); window->flags |= W_UpdateWindow; @@ -4931,22 +4839,52 @@ DECLARE_INFO_COMMAND (info_move_to_next_ } /* Select the menu item or reference that appears on this line. */ +/* FIXME: This function does not do what it should - it actually picks + * the first link before the cursor */ DECLARE_INFO_COMMAND (info_select_reference_this_line, _("Select reference or menu item appearing on this line")) { - char *line; + REFERENCE **ref = window->node->references; - if (window->line_starts) - line = window->line_starts[window_line_of_point (window)]; - else - line = ""; + if (!ref || !*ref) return; /* No references in 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); + info_menu_or_ref_item (window, key, 1, 1, 0); + +#if 0 + /* Loop until we've checked all the references or the next reference + * is after the cursor. */ + for (; *ref != 0; ref++) + { + REFERENCE *entry = *ref; + if (*(ref+1) && (*(ref + 1))->start > window->point) + { + info_select_reference(window, entry); + + /* We originally called info_menu_or_ref_item which contains + the following, instead of info_select_reference. + FIXME: Why isn't this in info_select_reference instead? */ + if (entry->line_number > 0) + /* No idea what this comment means. */ + /* next_line starts at line 1? Anyway, the -1 makes it + move to the right line. */ + internal_next_line (window, entry->line_number - 1, key); + return; + } + + /* If no next reference but current reference is eligible */ + if (!*(ref+1) && (*ref)->start <= window->point) + { + info_select_reference(window, *ref); + if (entry->line_number > 0) + /* next_line starts at line 1? Anyway, the -1 makes it + move to the right line. */ + internal_next_line (window, entry->line_number - 1, key); + return; + } + } +#endif } + /* **************************************************************** */ /* */ diff -up -x 'Makefile*' -x '*.o' -x '*~' tag-node-5419/trunk/info/tag.c parse-node-5419/trunk/info/tag.c --- tag-node-5419/trunk/info/tag.c 2014-02-28 20:17:14.000000000 +0000 +++ parse-node-5419/trunk/info/tag.c 2014-02-28 20:22:55.000000000 +0000 @@ -260,6 +260,8 @@ tags_expand (char *input, size_t inputle if (input < endp) text_buffer_add_string (&outbuf, input, endp - input); *pbuflen = text_buffer_off (&outbuf); + + text_buffer_add_char (&outbuf, '\0'); *pbuf = text_buffer_base (&outbuf); return 1; } diff -up -x 'Makefile*' -x '*.o' -x '*~' tag-node-5419/trunk/info/variables.c parse-node-5419/trunk/info/variables.c --- tag-node-5419/trunk/info/variables.c 2014-02-28 20:17:13.000000000 +0000 +++ parse-node-5419/trunk/info/variables.c 2014-02-28 20:22:55.000000000 +0000 @@ -89,6 +89,11 @@ VARIABLE_ALIST info_variables[] = { { "search-skip-screen", N_("Skip current window when searching"), &search_skip_screen_p, (char **)on_off_choices }, + + { "preprocess-nodes", + N_("Remove Info file syntax from the text of nodes"), + &preprocess_nodes_p, (char **)on_off_choices }, + { NULL } }; diff -up -x 'Makefile*' -x '*.o' -x '*~' tag-node-5419/trunk/info/variables.h parse-node-5419/trunk/info/variables.h --- tag-node-5419/trunk/info/variables.h 2014-02-28 20:17:14.000000000 +0000 +++ parse-node-5419/trunk/info/variables.h 2014-02-28 20:22:55.000000000 +0000 @@ -63,5 +63,6 @@ extern int ISO_Latin_p; extern int scroll_last_node; extern int min_search_length; extern int search_skip_screen_p; +extern int preprocess_nodes_p; #endif /* not INFO_VARIABLES_H */ diff -up -x 'Makefile*' -x '*.o' -x '*~' tag-node-5419/trunk/info/window.c parse-node-5419/trunk/info/window.c --- tag-node-5419/trunk/info/window.c 2014-02-28 20:17:13.000000000 +0000 +++ parse-node-5419/trunk/info/window.c 2014-02-28 20:22:55.000000000 +0000 @@ -26,6 +26,7 @@ #include "info-utils.h" #include "infomap.h" #include "tag.h" +#include "variables.h" /* The window which describes the screen. */ WINDOW *the_screen = NULL; @@ -1067,7 +1068,6 @@ window_make_modeline (WINDOW *window) if (node->parent) { parent = filename_non_directory (node->parent); - modeline_len += strlen ("Subfile: ") + strlen (node->filename); } if (node->filename) @@ -1077,36 +1077,63 @@ window_make_modeline (WINDOW *window) update_message = _("--*** Tags out of Date ***"); } - if (update_message) - modeline_len += strlen (update_message); - modeline_len += strlen (filename); - modeline_len += strlen (nodename); - modeline_len += 4; /* strlen (location_indicator). */ - - /* 10 for the decimal representation of the number of lines in this - node, and the remainder of the text that can appear in the line. */ - modeline_len += 10 + strlen (_("-----Info: (), lines ----, ")); - modeline_len += window->width; - - modeline = xmalloc (1 + modeline_len); - - /* Special internal windows have no filename. */ - if (!parent && !*filename) - sprintf (modeline, _("-%s---Info: %s, %d lines --%s--"), - (window->flags & W_NoWrap) ? "$" : "-", - nodename, window->line_count, location_indicator); + if (preprocess_nodes_p) + { + modeline_len += strlen ("--() --"); + if (parent) modeline_len += strlen (parent); + if (filename) modeline_len += strlen (filename); + if (nodename) modeline_len += strlen (nodename); + modeline_len += 3; /* strlen (location_indicator) */ + if (modeline_len < window->width) + modeline_len = window->width; + + modeline = xmalloc (1 + modeline_len); + + sprintf (modeline, _("%s--(%s) %s--"), + location_indicator, + parent ? parent : filename ? filename : "", + nodename); + } else - sprintf (modeline, _("-%s%s-Info: (%s)%s, %d lines --%s--"), - (window->flags & W_NoWrap) ? "$" : "-", - (node && (node->flags & N_IsCompressed)) ? "zz" : "--", - parent ? parent : filename, - nodename, window->line_count, location_indicator); + { + if (node && node->parent) + modeline_len += strlen ("Subfile: ") + strlen (node->filename); + + if (update_message) + modeline_len += strlen (update_message); + modeline_len += strlen (filename); + modeline_len += strlen (nodename); + modeline_len += 4; /* strlen (location_indicator). */ + + /* 10 for the decimal representation of the number of lines in this + node, and the remainder of the text that can appear in the line. */ + modeline_len += 10 + strlen (_("-----Info: (), lines ----, ")); + modeline_len += window->width; + + modeline = xmalloc (1 + modeline_len); + + /* Special internal windows have no filename. */ + if (!parent && !*filename) + sprintf (modeline, _("-%s---Info: %s, %d lines --%s--"), + (window->flags & W_NoWrap) ? "$" : "-", + nodename, window->line_count, location_indicator); + else + sprintf (modeline, _("-%s%s-Info: (%s)%s, %d lines --%s--"), + (window->flags & W_NoWrap) ? "$" : "-", + (node && (node->flags & N_IsCompressed)) ? "zz" : "--", + parent ? parent : filename, + nodename, window->line_count, location_indicator); + + if (window->node->next) + sprintf (modeline + strlen (modeline), + "Next: %s--", window->node->next); - if (parent) - sprintf (modeline + strlen (modeline), _(" Subfile: %s"), filename); + if (parent) + sprintf (modeline + strlen (modeline), _(" Subfile: %s"), filename); - if (update_message) - sprintf (modeline + strlen (modeline), "%s", update_message); + if (update_message) + sprintf (modeline + strlen (modeline), "%s", update_message); + } i = strlen (modeline);