156160Sru/* indices.c -- deal with an Info file index. 2146515Sru $Id: indices.c,v 1.5 2004/04/11 17:56:45 karl Exp $ 321495Sjmacd 4146515Sru Copyright (C) 1993, 1997, 1998, 1999, 2002, 2003, 2004 Free Software 5116525Sru Foundation, Inc. 621495Sjmacd 721495Sjmacd This program is free software; you can redistribute it and/or modify 821495Sjmacd it under the terms of the GNU General Public License as published by 921495Sjmacd the Free Software Foundation; either version 2, or (at your option) 1021495Sjmacd any later version. 1121495Sjmacd 1221495Sjmacd This program is distributed in the hope that it will be useful, 1321495Sjmacd but WITHOUT ANY WARRANTY; without even the implied warranty of 1421495Sjmacd MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1521495Sjmacd GNU General Public License for more details. 1621495Sjmacd 1721495Sjmacd You should have received a copy of the GNU General Public License 1821495Sjmacd along with this program; if not, write to the Free Software 1921495Sjmacd Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 2021495Sjmacd 21146515Sru Originally written by Brian Fox (bfox@ai.mit.edu). */ 2221495Sjmacd 2321495Sjmacd#include "info.h" 2421495Sjmacd#include "indices.h" 2521495Sjmacd 2621495Sjmacd/* User-visible variable controls the output of info-index-next. */ 2721495Sjmacdint show_index_match = 1; 2821495Sjmacd 2921495Sjmacd/* In the Info sense, an index is a menu. This variable holds the last 3021495Sjmacd parsed index. */ 3121495Sjmacdstatic REFERENCE **index_index = (REFERENCE **)NULL; 3221495Sjmacd 3321495Sjmacd/* The offset of the most recently selected index element. */ 3421495Sjmacdstatic int index_offset = 0; 3521495Sjmacd 3621495Sjmacd/* Variable which holds the last string searched for. */ 3721495Sjmacdstatic char *index_search = (char *)NULL; 3821495Sjmacd 3921495Sjmacd/* A couple of "globals" describing where the initial index was found. */ 4021495Sjmacdstatic char *initial_index_filename = (char *)NULL; 4121495Sjmacdstatic char *initial_index_nodename = (char *)NULL; 4221495Sjmacd 4321495Sjmacd/* A structure associating index names with index offset ranges. */ 4421495Sjmacdtypedef struct { 4542660Smarkm char *name; /* The nodename of this index. */ 4642660Smarkm int first; /* The index in our list of the first entry. */ 4742660Smarkm int last; /* The index in our list of the last entry. */ 4821495Sjmacd} INDEX_NAME_ASSOC; 4921495Sjmacd 5021495Sjmacd/* An array associating index nodenames with index offset ranges. */ 5121495Sjmacdstatic INDEX_NAME_ASSOC **index_nodenames = (INDEX_NAME_ASSOC **)NULL; 5221495Sjmacdstatic int index_nodenames_index = 0; 5321495Sjmacdstatic int index_nodenames_slots = 0; 5421495Sjmacd 5521495Sjmacd/* Add the name of NODE, and the range of the associated index elements 5621495Sjmacd (passed in ARRAY) to index_nodenames. */ 5721495Sjmacdstatic void 58146515Sruadd_index_to_index_nodenames (REFERENCE **array, NODE *node) 5921495Sjmacd{ 6021495Sjmacd register int i, last; 6121495Sjmacd INDEX_NAME_ASSOC *assoc; 6221495Sjmacd 63100513Sru for (last = 0; array[last + 1]; last++); 6421495Sjmacd assoc = (INDEX_NAME_ASSOC *)xmalloc (sizeof (INDEX_NAME_ASSOC)); 6542660Smarkm assoc->name = xstrdup (node->nodename); 6621495Sjmacd 6721495Sjmacd if (!index_nodenames_index) 6821495Sjmacd { 6921495Sjmacd assoc->first = 0; 7021495Sjmacd assoc->last = last; 7121495Sjmacd } 7221495Sjmacd else 7321495Sjmacd { 7421495Sjmacd for (i = 0; index_nodenames[i + 1]; i++); 7521495Sjmacd assoc->first = 1 + index_nodenames[i]->last; 7621495Sjmacd assoc->last = assoc->first + last; 7721495Sjmacd } 7821495Sjmacd add_pointer_to_array 7921495Sjmacd (assoc, index_nodenames_index, index_nodenames, index_nodenames_slots, 8021495Sjmacd 10, INDEX_NAME_ASSOC *); 8121495Sjmacd} 8221495Sjmacd 8321495Sjmacd/* Find and return the indices of WINDOW's file. The indices are defined 8421495Sjmacd as the first node in the file containing the word "Index" and any 8521495Sjmacd immediately following nodes whose names also contain "Index". All such 8621495Sjmacd indices are concatenated and the result returned. If WINDOW's info file 8721495Sjmacd doesn't have any indices, a NULL pointer is returned. */ 8821495SjmacdREFERENCE ** 89146515Sruinfo_indices_of_window (WINDOW *window) 9021495Sjmacd{ 9121495Sjmacd FILE_BUFFER *fb; 9221495Sjmacd 9321495Sjmacd fb = file_buffer_of_window (window); 9421495Sjmacd 9521495Sjmacd return (info_indices_of_file_buffer (fb)); 9621495Sjmacd} 9721495Sjmacd 9821495SjmacdREFERENCE ** 99146515Sruinfo_indices_of_file_buffer (FILE_BUFFER *file_buffer) 10021495Sjmacd{ 10121495Sjmacd register int i; 10221495Sjmacd REFERENCE **result = (REFERENCE **)NULL; 10321495Sjmacd 10421495Sjmacd /* No file buffer, no indices. */ 10521495Sjmacd if (!file_buffer) 10621495Sjmacd return ((REFERENCE **)NULL); 10721495Sjmacd 10821495Sjmacd /* Reset globals describing where the index was found. */ 10921495Sjmacd maybe_free (initial_index_filename); 11021495Sjmacd maybe_free (initial_index_nodename); 11121495Sjmacd initial_index_filename = (char *)NULL; 11221495Sjmacd initial_index_nodename = (char *)NULL; 11321495Sjmacd 11421495Sjmacd if (index_nodenames) 11521495Sjmacd { 11621495Sjmacd for (i = 0; index_nodenames[i]; i++) 11742660Smarkm { 11842660Smarkm free (index_nodenames[i]->name); 11942660Smarkm free (index_nodenames[i]); 12042660Smarkm } 12121495Sjmacd 12221495Sjmacd index_nodenames_index = 0; 12321495Sjmacd index_nodenames[0] = (INDEX_NAME_ASSOC *)NULL; 12421495Sjmacd } 12521495Sjmacd 12621495Sjmacd /* Grovel the names of the nodes found in this file. */ 12721495Sjmacd if (file_buffer->tags) 12821495Sjmacd { 12921495Sjmacd TAG *tag; 13021495Sjmacd 13142660Smarkm for (i = 0; (tag = file_buffer->tags[i]); i++) 13242660Smarkm { 13342660Smarkm if (string_in_line ("Index", tag->nodename) != -1) 13442660Smarkm { 13542660Smarkm NODE *node; 13642660Smarkm REFERENCE **menu; 13721495Sjmacd 13842660Smarkm /* Found one. Get its menu. */ 13942660Smarkm node = info_get_node (tag->filename, tag->nodename); 14042660Smarkm if (!node) 14142660Smarkm continue; 14221495Sjmacd 14342660Smarkm /* Remember the filename and nodename of this index. */ 14442660Smarkm initial_index_filename = xstrdup (file_buffer->filename); 14542660Smarkm initial_index_nodename = xstrdup (tag->nodename); 14621495Sjmacd 14742660Smarkm menu = info_menu_of_node (node); 14821495Sjmacd 14942660Smarkm /* If we have a menu, add this index's nodename and range 15042660Smarkm to our list of index_nodenames. */ 15142660Smarkm if (menu) 15242660Smarkm { 15342660Smarkm add_index_to_index_nodenames (menu, node); 15421495Sjmacd 15542660Smarkm /* Concatenate the references found so far. */ 15642660Smarkm result = info_concatenate_references (result, menu); 15742660Smarkm } 15842660Smarkm free (node); 15942660Smarkm } 16042660Smarkm } 16121495Sjmacd } 16221495Sjmacd 16321495Sjmacd /* If there is a result, clean it up so that every entry has a filename. */ 16421495Sjmacd for (i = 0; result && result[i]; i++) 16521495Sjmacd if (!result[i]->filename) 16642660Smarkm result[i]->filename = xstrdup (file_buffer->filename); 16721495Sjmacd 16821495Sjmacd return (result); 16921495Sjmacd} 17021495Sjmacd 17121495SjmacdDECLARE_INFO_COMMAND (info_index_search, 17242660Smarkm _("Look up a string in the index for this file")) 17321495Sjmacd{ 17442660Smarkm do_info_index_search (window, count, 0); 17542660Smarkm} 17642660Smarkm 17742660Smarkm/* Look up SEARCH_STRING in the index for this file. If SEARCH_STRING 17842660Smarkm is NULL, prompt user for input. */ 17942660Smarkmvoid 180146515Srudo_info_index_search (WINDOW *window, int count, char *search_string) 18142660Smarkm{ 18221495Sjmacd FILE_BUFFER *fb; 18321495Sjmacd char *line; 18421495Sjmacd 18521495Sjmacd /* Reset the index offset, since this is not the info-index-next command. */ 18621495Sjmacd index_offset = 0; 18721495Sjmacd 18821495Sjmacd /* The user is selecting a new search string, so flush the old one. */ 18921495Sjmacd maybe_free (index_search); 19021495Sjmacd index_search = (char *)NULL; 19121495Sjmacd 19221495Sjmacd /* If this window's file is not the same as the one that we last built an 19321495Sjmacd index for, build and remember an index now. */ 19421495Sjmacd fb = file_buffer_of_window (window); 19521495Sjmacd if (!initial_index_filename || 19656160Sru (FILENAME_CMP (initial_index_filename, fb->filename) != 0)) 19721495Sjmacd { 19821495Sjmacd info_free_references (index_index); 199146515Sru window_message_in_echo_area ((char *) _("Finding index entries..."), 200146515Sru NULL, NULL); 20121495Sjmacd index_index = info_indices_of_file_buffer (fb); 20221495Sjmacd } 20321495Sjmacd 20421495Sjmacd /* If there is no index, quit now. */ 20521495Sjmacd if (!index_index) 20621495Sjmacd { 207146515Sru info_error ((char *) _("No indices found."), NULL, NULL); 20821495Sjmacd return; 20921495Sjmacd } 21021495Sjmacd 21142660Smarkm /* Okay, there is an index. Look for SEARCH_STRING, or, if it is 21242660Smarkm empty, prompt for one. */ 21342660Smarkm if (search_string && *search_string) 21442660Smarkm line = xstrdup (search_string); 21542660Smarkm else 21642660Smarkm { 217146515Sru line = info_read_maybe_completing (window, (char *) _("Index entry: "), 21842660Smarkm index_index); 21942660Smarkm window = active_window; 22021495Sjmacd 22142660Smarkm /* User aborted? */ 22242660Smarkm if (!line) 22342660Smarkm { 22442660Smarkm info_abort_key (active_window, 1, 0); 22542660Smarkm return; 22642660Smarkm } 22721495Sjmacd 22842660Smarkm /* Empty line means move to the Index node. */ 22942660Smarkm if (!*line) 23042660Smarkm { 23142660Smarkm free (line); 23221495Sjmacd 23342660Smarkm if (initial_index_filename && initial_index_nodename) 23442660Smarkm { 23542660Smarkm NODE *node; 23621495Sjmacd 23742660Smarkm node = info_get_node (initial_index_filename, 23842660Smarkm initial_index_nodename); 23942660Smarkm set_remembered_pagetop_and_point (window); 24042660Smarkm window_set_node_of_window (window, node); 24142660Smarkm remember_window_and_node (window, node); 24242660Smarkm window_clear_echo_area (); 24342660Smarkm return; 24442660Smarkm } 24542660Smarkm } 24621495Sjmacd } 24721495Sjmacd 24821495Sjmacd /* The user typed either a completed index label, or a partial string. 24921495Sjmacd Find an exact match, or, failing that, the first index entry containing 25021495Sjmacd the partial string. So, we just call info_next_index_match () with minor 25121495Sjmacd manipulation of INDEX_OFFSET. */ 25221495Sjmacd { 25321495Sjmacd int old_offset; 25421495Sjmacd 25521495Sjmacd /* Start the search right after/before this index. */ 25621495Sjmacd if (count < 0) 25721495Sjmacd { 25842660Smarkm register int i; 25942660Smarkm for (i = 0; index_index[i]; i++); 26042660Smarkm index_offset = i; 26121495Sjmacd } 26221495Sjmacd else 26321495Sjmacd index_offset = -1; 26421495Sjmacd 26521495Sjmacd old_offset = index_offset; 26621495Sjmacd 26721495Sjmacd /* The "last" string searched for is this one. */ 26821495Sjmacd index_search = line; 26921495Sjmacd 27021495Sjmacd /* Find it, or error. */ 27121495Sjmacd info_next_index_match (window, count, 0); 27221495Sjmacd 27321495Sjmacd /* If the search failed, return the index offset to where it belongs. */ 27421495Sjmacd if (index_offset == old_offset) 27521495Sjmacd index_offset = 0; 27621495Sjmacd } 27721495Sjmacd} 27821495Sjmacd 27942660Smarkmint 280146515Sruindex_entry_exists (WINDOW *window, char *string) 28142660Smarkm{ 28242660Smarkm register int i; 28342660Smarkm FILE_BUFFER *fb; 28442660Smarkm 28542660Smarkm /* If there is no previous search string, the user hasn't built an index 28642660Smarkm yet. */ 28742660Smarkm if (!string) 28842660Smarkm return 0; 28942660Smarkm 29042660Smarkm fb = file_buffer_of_window (window); 29142660Smarkm if (!initial_index_filename 29256160Sru || (FILENAME_CMP (initial_index_filename, fb->filename) != 0)) 29342660Smarkm { 29442660Smarkm info_free_references (index_index); 29542660Smarkm index_index = info_indices_of_file_buffer (fb); 29642660Smarkm } 29742660Smarkm 29842660Smarkm /* If there is no index, that is an error. */ 29942660Smarkm if (!index_index) 30042660Smarkm return 0; 30142660Smarkm 30242660Smarkm for (i = 0; (i > -1) && (index_index[i]); i++) 30342660Smarkm if (strcmp (string, index_index[i]->label) == 0) 30442660Smarkm break; 30542660Smarkm 30642660Smarkm /* If that failed, look for the next substring match. */ 30742660Smarkm if ((i < 0) || (!index_index[i])) 30842660Smarkm { 30942660Smarkm for (i = 0; (i > -1) && (index_index[i]); i++) 31042660Smarkm if (string_in_line (string, index_index[i]->label) != -1) 31142660Smarkm break; 31242660Smarkm 31342660Smarkm if ((i > -1) && (index_index[i])) 31442660Smarkm string_in_line (string, index_index[i]->label); 31542660Smarkm } 31642660Smarkm 31742660Smarkm /* If that failed, return 0. */ 31842660Smarkm if ((i < 0) || (!index_index[i])) 31942660Smarkm return 0; 32042660Smarkm 32142660Smarkm return 1; 32242660Smarkm} 32342660Smarkm 32421495SjmacdDECLARE_INFO_COMMAND (info_next_index_match, 32542660Smarkm _("Go to the next matching index item from the last `\\[index-search]' command")) 32621495Sjmacd{ 32721495Sjmacd register int i; 32821495Sjmacd int partial, dir; 32921495Sjmacd NODE *node; 33021495Sjmacd 33121495Sjmacd /* If there is no previous search string, the user hasn't built an index 33221495Sjmacd yet. */ 33321495Sjmacd if (!index_search) 33421495Sjmacd { 335146515Sru info_error ((char *) _("No previous index search string."), NULL, NULL); 33621495Sjmacd return; 33721495Sjmacd } 33821495Sjmacd 33921495Sjmacd /* If there is no index, that is an error. */ 34021495Sjmacd if (!index_index) 34121495Sjmacd { 342146515Sru info_error ((char *) _("No index entries."), NULL, NULL); 34321495Sjmacd return; 34421495Sjmacd } 34521495Sjmacd 34621495Sjmacd /* The direction of this search is controlled by the value of the 34721495Sjmacd numeric argument. */ 34821495Sjmacd if (count < 0) 34921495Sjmacd dir = -1; 35021495Sjmacd else 35121495Sjmacd dir = 1; 35221495Sjmacd 35321495Sjmacd /* Search for the next occurence of index_search. First try to find 35421495Sjmacd an exact match. */ 35521495Sjmacd partial = 0; 35621495Sjmacd 35721495Sjmacd for (i = index_offset + dir; (i > -1) && (index_index[i]); i += dir) 35821495Sjmacd if (strcmp (index_search, index_index[i]->label) == 0) 35921495Sjmacd break; 36021495Sjmacd 36121495Sjmacd /* If that failed, look for the next substring match. */ 36221495Sjmacd if ((i < 0) || (!index_index[i])) 36321495Sjmacd { 36421495Sjmacd for (i = index_offset + dir; (i > -1) && (index_index[i]); i += dir) 36542660Smarkm if (string_in_line (index_search, index_index[i]->label) != -1) 36642660Smarkm break; 36721495Sjmacd 36821495Sjmacd if ((i > -1) && (index_index[i])) 36942660Smarkm partial = string_in_line (index_search, index_index[i]->label); 37021495Sjmacd } 37121495Sjmacd 37221495Sjmacd /* If that failed, print an error. */ 37321495Sjmacd if ((i < 0) || (!index_index[i])) 37421495Sjmacd { 375146515Sru info_error ((char *) _("No %sindex entries containing `%s'."), 376146515Sru index_offset > 0 ? (char *) _("more ") : "", index_search); 37721495Sjmacd return; 37821495Sjmacd } 37921495Sjmacd 38021495Sjmacd /* Okay, we found the next one. Move the offset to the current entry. */ 38121495Sjmacd index_offset = i; 38221495Sjmacd 38321495Sjmacd /* Report to the user on what we have found. */ 38421495Sjmacd { 38521495Sjmacd register int j; 386116525Sru const char *name = _("CAN'T SEE THIS"); 38721495Sjmacd char *match; 38821495Sjmacd 38921495Sjmacd for (j = 0; index_nodenames[j]; j++) 39021495Sjmacd { 39142660Smarkm if ((i >= index_nodenames[j]->first) && 39242660Smarkm (i <= index_nodenames[j]->last)) 39342660Smarkm { 39442660Smarkm name = index_nodenames[j]->name; 39542660Smarkm break; 39642660Smarkm } 39721495Sjmacd } 39821495Sjmacd 39921495Sjmacd /* If we had a partial match, indicate to the user which part of the 40021495Sjmacd string matched. */ 40142660Smarkm match = xstrdup (index_index[i]->label); 40221495Sjmacd 40321495Sjmacd if (partial && show_index_match) 40421495Sjmacd { 405146515Sru int k, ls, start, upper; 40621495Sjmacd 40742660Smarkm ls = strlen (index_search); 40842660Smarkm start = partial - ls; 40942660Smarkm upper = isupper (match[start]) ? 1 : 0; 41021495Sjmacd 411146515Sru for (k = 0; k < ls; k++) 41242660Smarkm if (upper) 413146515Sru match[k + start] = info_tolower (match[k + start]); 41442660Smarkm else 415146515Sru match[k + start] = info_toupper (match[k + start]); 41621495Sjmacd } 41721495Sjmacd 41821495Sjmacd { 41921495Sjmacd char *format; 42021495Sjmacd 42121495Sjmacd format = replace_in_documentation 422146515Sru ((char *) _("Found `%s' in %s. (`\\[next-index-match]' tries to find next.)"), 423146515Sru 0); 42421495Sjmacd 425146515Sru window_message_in_echo_area (format, match, (char *) name); 42621495Sjmacd } 42721495Sjmacd 42821495Sjmacd free (match); 42921495Sjmacd } 43021495Sjmacd 43121495Sjmacd /* Select the node corresponding to this index entry. */ 43221495Sjmacd node = info_get_node (index_index[i]->filename, index_index[i]->nodename); 43321495Sjmacd 43421495Sjmacd if (!node) 43521495Sjmacd { 436146515Sru info_error ((char *) msg_cant_file_node, 43742660Smarkm index_index[i]->filename, index_index[i]->nodename); 43821495Sjmacd return; 43921495Sjmacd } 44021495Sjmacd 44156160Sru info_set_node_of_window (1, window, node); 44221495Sjmacd 44321495Sjmacd /* Try to find an occurence of LABEL in this node. */ 44421495Sjmacd { 44521495Sjmacd long start, loc; 44621495Sjmacd 44721495Sjmacd start = window->line_starts[1] - window->node->contents; 44821495Sjmacd loc = info_target_search_node (node, index_index[i]->label, start); 44921495Sjmacd 45021495Sjmacd if (loc != -1) 45121495Sjmacd { 45242660Smarkm window->point = loc; 45342660Smarkm window_adjust_pagetop (window); 45421495Sjmacd } 45521495Sjmacd } 45621495Sjmacd} 45721495Sjmacd 45821495Sjmacd/* **************************************************************** */ 45942660Smarkm/* */ 46042660Smarkm/* Info APROPOS: Search every known index. */ 46142660Smarkm/* */ 46221495Sjmacd/* **************************************************************** */ 46321495Sjmacd 46421495Sjmacd/* For every menu item in DIR, search the indices of that file for 46521495Sjmacd SEARCH_STRING. */ 46621495SjmacdREFERENCE ** 467146515Sruapropos_in_all_indices (char *search_string, int inform) 46821495Sjmacd{ 46921495Sjmacd register int i, dir_index; 47021495Sjmacd REFERENCE **all_indices = (REFERENCE **)NULL; 47121495Sjmacd REFERENCE **dir_menu = (REFERENCE **)NULL; 47221495Sjmacd NODE *dir_node; 47321495Sjmacd 47421495Sjmacd dir_node = info_get_node ("dir", "Top"); 47521495Sjmacd if (dir_node) 47621495Sjmacd dir_menu = info_menu_of_node (dir_node); 47721495Sjmacd 47821495Sjmacd if (!dir_menu) 47942660Smarkm return NULL; 48021495Sjmacd 48121495Sjmacd /* For every menu item in DIR, get the associated node's file buffer and 48221495Sjmacd read the indices of that file buffer. Gather all of the indices into 48321495Sjmacd one large one. */ 48421495Sjmacd for (dir_index = 0; dir_menu[dir_index]; dir_index++) 48521495Sjmacd { 48621495Sjmacd REFERENCE **this_index, *this_item; 48721495Sjmacd NODE *this_node; 48821495Sjmacd FILE_BUFFER *this_fb; 48956160Sru int dir_node_duplicated = 0; 49021495Sjmacd 49121495Sjmacd this_item = dir_menu[dir_index]; 49221495Sjmacd 49321495Sjmacd if (!this_item->filename) 49442660Smarkm { 49556160Sru dir_node_duplicated = 1; 49642660Smarkm if (dir_node->parent) 49742660Smarkm this_item->filename = xstrdup (dir_node->parent); 49842660Smarkm else 49942660Smarkm this_item->filename = xstrdup (dir_node->filename); 50042660Smarkm } 50121495Sjmacd 50221495Sjmacd /* Find this node. If we cannot find it, try using the label of the 50342660Smarkm entry as a file (i.e., "(LABEL)Top"). */ 50421495Sjmacd this_node = info_get_node (this_item->filename, this_item->nodename); 50521495Sjmacd 50621495Sjmacd if (!this_node && this_item->nodename && 50742660Smarkm (strcmp (this_item->label, this_item->nodename) == 0)) 50842660Smarkm this_node = info_get_node (this_item->label, "Top"); 50921495Sjmacd 51021495Sjmacd if (!this_node) 51156160Sru { 51256160Sru if (dir_node_duplicated) 51356160Sru free (this_item->filename); 51456160Sru continue; 51556160Sru } 51621495Sjmacd 51721495Sjmacd /* Get the file buffer associated with this node. */ 51821495Sjmacd { 51942660Smarkm char *files_name; 52021495Sjmacd 52142660Smarkm files_name = this_node->parent; 52242660Smarkm if (!files_name) 52342660Smarkm files_name = this_node->filename; 52421495Sjmacd 52542660Smarkm this_fb = info_find_file (files_name); 52621495Sjmacd 52756160Sru /* If we already scanned this file, don't do that again. 52856160Sru In addition to being faster, this also avoids having 52956160Sru multiple identical entries in the *Apropos* menu. */ 53056160Sru for (i = 0; i < dir_index; i++) 53156160Sru if (FILENAME_CMP (this_fb->filename, dir_menu[i]->filename) == 0) 53256160Sru break; 53356160Sru if (i < dir_index) 53456160Sru { 53556160Sru if (dir_node_duplicated) 53656160Sru free (this_item->filename); 53756160Sru continue; 53856160Sru } 53956160Sru 54042660Smarkm if (this_fb && inform) 541146515Sru message_in_echo_area ((char *) _("Scanning indices of `%s'..."), 542146515Sru files_name, NULL); 54321495Sjmacd 54442660Smarkm this_index = info_indices_of_file_buffer (this_fb); 54542660Smarkm free (this_node); 54621495Sjmacd 54742660Smarkm if (this_fb && inform) 54842660Smarkm unmessage_in_echo_area (); 54921495Sjmacd } 55021495Sjmacd 55121495Sjmacd if (this_index) 55242660Smarkm { 55342660Smarkm /* Remember the filename which contains this set of references. */ 55442660Smarkm for (i = 0; this_index && this_index[i]; i++) 55542660Smarkm if (!this_index[i]->filename) 55642660Smarkm this_index[i]->filename = xstrdup (this_fb->filename); 55721495Sjmacd 55842660Smarkm /* Concatenate with the other indices. */ 55942660Smarkm all_indices = info_concatenate_references (all_indices, this_index); 56042660Smarkm } 56121495Sjmacd } 56221495Sjmacd 56321495Sjmacd info_free_references (dir_menu); 56421495Sjmacd 56521495Sjmacd /* Build a list of the references which contain SEARCH_STRING. */ 56621495Sjmacd if (all_indices) 56721495Sjmacd { 56821495Sjmacd REFERENCE *entry, **apropos_list = (REFERENCE **)NULL; 56921495Sjmacd int apropos_list_index = 0; 57021495Sjmacd int apropos_list_slots = 0; 57121495Sjmacd 57221495Sjmacd for (i = 0; (entry = all_indices[i]); i++) 57342660Smarkm { 57442660Smarkm if (string_in_line (search_string, entry->label) != -1) 57542660Smarkm { 57642660Smarkm add_pointer_to_array 57742660Smarkm (entry, apropos_list_index, apropos_list, apropos_list_slots, 57842660Smarkm 100, REFERENCE *); 57942660Smarkm } 58042660Smarkm else 58142660Smarkm { 58242660Smarkm maybe_free (entry->label); 58342660Smarkm maybe_free (entry->filename); 58442660Smarkm maybe_free (entry->nodename); 58542660Smarkm free (entry); 58642660Smarkm } 58742660Smarkm } 58821495Sjmacd 58921495Sjmacd free (all_indices); 59021495Sjmacd all_indices = apropos_list; 59121495Sjmacd } 59221495Sjmacd return (all_indices); 59321495Sjmacd} 59421495Sjmacd 59521495Sjmacd#define APROPOS_NONE \ 596146515Sru N_("No available info files have `%s' in their indices.") 59721495Sjmacd 59821495Sjmacdvoid 599146515Sruinfo_apropos (char *string) 60021495Sjmacd{ 60121495Sjmacd REFERENCE **apropos_list; 60221495Sjmacd 60321495Sjmacd apropos_list = apropos_in_all_indices (string, 0); 60421495Sjmacd 60521495Sjmacd if (!apropos_list) 606146515Sru info_error ((char *) _(APROPOS_NONE), string, NULL); 60721495Sjmacd else 60821495Sjmacd { 60921495Sjmacd register int i; 61021495Sjmacd REFERENCE *entry; 61121495Sjmacd 61221495Sjmacd for (i = 0; (entry = apropos_list[i]); i++) 61356160Sru fprintf (stdout, "\"(%s)%s\" -- %s\n", 61442660Smarkm entry->filename, entry->nodename, entry->label); 61521495Sjmacd } 61621495Sjmacd info_free_references (apropos_list); 61721495Sjmacd} 61821495Sjmacd 61921495Sjmacdstatic char *apropos_list_nodename = "*Apropos*"; 62021495Sjmacd 62121495SjmacdDECLARE_INFO_COMMAND (info_index_apropos, 62242660Smarkm _("Grovel all known info file's indices for a string and build a menu")) 62321495Sjmacd{ 62421495Sjmacd char *line; 62521495Sjmacd 626146515Sru line = info_read_in_echo_area (window, (char *) _("Index apropos: ")); 62721495Sjmacd 62821495Sjmacd window = active_window; 62921495Sjmacd 63021495Sjmacd /* User aborted? */ 63121495Sjmacd if (!line) 63221495Sjmacd { 63321495Sjmacd info_abort_key (window, 1, 1); 63421495Sjmacd return; 63521495Sjmacd } 63621495Sjmacd 63721495Sjmacd /* User typed something? */ 63821495Sjmacd if (*line) 63921495Sjmacd { 64021495Sjmacd REFERENCE **apropos_list; 64121495Sjmacd NODE *apropos_node; 64221495Sjmacd 64321495Sjmacd apropos_list = apropos_in_all_indices (line, 1); 64421495Sjmacd 64521495Sjmacd if (!apropos_list) 646146515Sru info_error ((char *) _(APROPOS_NONE), line, NULL); 64721495Sjmacd else 64842660Smarkm { 64942660Smarkm register int i; 65042660Smarkm char *line_buffer; 65121495Sjmacd 65242660Smarkm initialize_message_buffer (); 65342660Smarkm printf_to_message_buffer 654146515Sru ((char *) _("\n* Menu: Nodes whose indices contain `%s':\n"), 655146515Sru line, NULL, NULL); 65642660Smarkm line_buffer = (char *)xmalloc (500); 65721495Sjmacd 65842660Smarkm for (i = 0; apropos_list[i]; i++) 65942660Smarkm { 66042660Smarkm int len; 66156160Sru /* The label might be identical to that of another index 66256160Sru entry in another Info file. Therefore, we make the file 66356160Sru name part of the menu entry, to make them all distinct. */ 66456160Sru sprintf (line_buffer, "* %s [%s]: ", 66556160Sru apropos_list[i]->label, apropos_list[i]->filename); 66656160Sru len = pad_to (40, line_buffer); 66756160Sru sprintf (line_buffer + len, "(%s)%s.", 66842660Smarkm apropos_list[i]->filename, apropos_list[i]->nodename); 669146515Sru printf_to_message_buffer ("%s\n", line_buffer, NULL, NULL); 67042660Smarkm } 67142660Smarkm free (line_buffer); 67242660Smarkm } 67321495Sjmacd 67421495Sjmacd apropos_node = message_buffer_to_node (); 67521495Sjmacd add_gcable_pointer (apropos_node->contents); 67621495Sjmacd name_internal_node (apropos_node, apropos_list_nodename); 67721495Sjmacd 67821495Sjmacd /* Even though this is an internal node, we don't want the window 67942660Smarkm system to treat it specially. So we turn off the internalness 68042660Smarkm of it here. */ 68121495Sjmacd apropos_node->flags &= ~N_IsInternal; 68221495Sjmacd 68321495Sjmacd /* Find/Create a window to contain this node. */ 68421495Sjmacd { 68542660Smarkm WINDOW *new; 68642660Smarkm NODE *node; 68721495Sjmacd 68842660Smarkm set_remembered_pagetop_and_point (window); 68921495Sjmacd 69042660Smarkm /* If a window is visible and showing an apropos list already, 69142660Smarkm re-use it. */ 69242660Smarkm for (new = windows; new; new = new->next) 69342660Smarkm { 69442660Smarkm node = new->node; 69521495Sjmacd 69642660Smarkm if (internal_info_node_p (node) && 69742660Smarkm (strcmp (node->nodename, apropos_list_nodename) == 0)) 69842660Smarkm break; 69942660Smarkm } 70021495Sjmacd 70142660Smarkm /* If we couldn't find an existing window, try to use the next window 70242660Smarkm in the chain. */ 70342660Smarkm if (!new && window->next) 70442660Smarkm new = window->next; 70521495Sjmacd 70642660Smarkm /* If we still don't have a window, make a new one to contain 70742660Smarkm the list. */ 70842660Smarkm if (!new) 70942660Smarkm { 71042660Smarkm WINDOW *old_active; 71121495Sjmacd 71242660Smarkm old_active = active_window; 71342660Smarkm active_window = window; 71442660Smarkm new = window_make_window ((NODE *)NULL); 71542660Smarkm active_window = old_active; 71642660Smarkm } 71721495Sjmacd 71842660Smarkm /* If we couldn't make a new window, use this one. */ 71942660Smarkm if (!new) 72042660Smarkm new = window; 72121495Sjmacd 72242660Smarkm /* Lines do not wrap in this window. */ 72342660Smarkm new->flags |= W_NoWrap; 72421495Sjmacd 72542660Smarkm window_set_node_of_window (new, apropos_node); 72642660Smarkm remember_window_and_node (new, apropos_node); 72742660Smarkm active_window = new; 72821495Sjmacd } 72921495Sjmacd info_free_references (apropos_list); 73021495Sjmacd } 73121495Sjmacd free (line); 73221495Sjmacd 73321495Sjmacd if (!info_error_was_printed) 73421495Sjmacd window_clear_echo_area (); 73521495Sjmacd} 736