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