home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / OS2 / gnuinfo.zip / info / indices.c < prev    next >
C/C++ Source or Header  |  1997-07-24  |  21KB  |  728 lines

  1. /* indices.c -- Commands for dealing with an Info file Index.
  2.    $Id: indices.c,v 1.6 1997/07/24 21:25:53 karl Exp $
  3.  
  4.    Copyright (C) 1993, 97 Free Software Foundation, Inc.
  5.  
  6.    This program is free software; you can redistribute it and/or modify
  7.    it under the terms of the GNU General Public License as published by
  8.    the Free Software Foundation; either version 2, or (at your option)
  9.    any later version.
  10.  
  11.    This program is distributed in the hope that it will be useful,
  12.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.    GNU General Public License for more details.
  15.  
  16.    You should have received a copy of the GNU General Public License
  17.    along with this program; if not, write to the Free Software
  18.    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19.  
  20.    Written by Brian Fox (bfox@ai.mit.edu). */
  21.  
  22. #include "info.h"
  23. #include "indices.h"
  24.  
  25. /* User-visible variable controls the output of info-index-next. */
  26. int show_index_match = 1;
  27.  
  28. /* In the Info sense, an index is a menu.  This variable holds the last
  29.    parsed index. */
  30. static REFERENCE **index_index = (REFERENCE **)NULL;
  31.  
  32. /* The offset of the most recently selected index element. */
  33. static int index_offset = 0;
  34.  
  35. /* Variable which holds the last string searched for. */
  36. static char *index_search = (char *)NULL;
  37.  
  38. /* A couple of "globals" describing where the initial index was found. */
  39. static char *initial_index_filename = (char *)NULL;
  40. static char *initial_index_nodename = (char *)NULL;
  41.  
  42. /* A structure associating index names with index offset ranges. */
  43. typedef struct {
  44.   char *name;                   /* The nodename of this index. */
  45.   int first;                    /* The index in our list of the first entry. */
  46.   int last;                     /* The index in our list of the last entry. */
  47. } INDEX_NAME_ASSOC;
  48.  
  49. /* An array associating index nodenames with index offset ranges. */
  50. static INDEX_NAME_ASSOC **index_nodenames = (INDEX_NAME_ASSOC **)NULL;
  51. static int index_nodenames_index = 0;
  52. static int index_nodenames_slots = 0;
  53.  
  54. /* Add the name of NODE, and the range of the associated index elements
  55.    (passed in ARRAY) to index_nodenames. */
  56. static void
  57. add_index_to_index_nodenames (array, node)
  58.      REFERENCE **array;
  59.      NODE *node;
  60. {
  61.   register int i, last;
  62.   INDEX_NAME_ASSOC *assoc;
  63.  
  64.   for (last = 0; array[last]; last++);
  65.   assoc = (INDEX_NAME_ASSOC *)xmalloc (sizeof (INDEX_NAME_ASSOC));
  66.   assoc->name = xstrdup (node->nodename);
  67.  
  68.   if (!index_nodenames_index)
  69.     {
  70.       assoc->first = 0;
  71.       assoc->last = last;
  72.     }
  73.   else
  74.     {
  75.       for (i = 0; index_nodenames[i + 1]; i++);
  76.       assoc->first = 1 + index_nodenames[i]->last;
  77.       assoc->last = assoc->first + last;
  78.     }
  79.   add_pointer_to_array
  80.     (assoc, index_nodenames_index, index_nodenames, index_nodenames_slots,
  81.      10, INDEX_NAME_ASSOC *);
  82. }
  83.  
  84. /* Find and return the indices of WINDOW's file.  The indices are defined
  85.    as the first node in the file containing the word "Index" and any
  86.    immediately following nodes whose names also contain "Index".  All such
  87.    indices are concatenated and the result returned.  If WINDOW's info file
  88.    doesn't have any indices, a NULL pointer is returned. */
  89. REFERENCE **
  90. info_indices_of_window (window)
  91.      WINDOW *window;
  92. {
  93.   FILE_BUFFER *fb;
  94.  
  95.   fb = file_buffer_of_window (window);
  96.  
  97.   return (info_indices_of_file_buffer (fb));
  98. }
  99.  
  100. REFERENCE **
  101. info_indices_of_file_buffer (file_buffer)
  102.      FILE_BUFFER *file_buffer;
  103. {
  104.   register int i;
  105.   REFERENCE **result = (REFERENCE **)NULL;
  106.  
  107.   /* No file buffer, no indices. */
  108.   if (!file_buffer)
  109.     return ((REFERENCE **)NULL);
  110.  
  111.   /* Reset globals describing where the index was found. */
  112.   maybe_free (initial_index_filename);
  113.   maybe_free (initial_index_nodename);
  114.   initial_index_filename = (char *)NULL;
  115.   initial_index_nodename = (char *)NULL;
  116.  
  117.   if (index_nodenames)
  118.     {
  119.       for (i = 0; index_nodenames[i]; i++)
  120.         {
  121.           free (index_nodenames[i]->name);
  122.           free (index_nodenames[i]);
  123.         }
  124.  
  125.       index_nodenames_index = 0;
  126.       index_nodenames[0] = (INDEX_NAME_ASSOC *)NULL;
  127.     }
  128.  
  129.   /* Grovel the names of the nodes found in this file. */
  130.   if (file_buffer->tags)
  131.     {
  132.       TAG *tag;
  133.  
  134.       for (i = 0; (tag = file_buffer->tags[i]); i++)
  135.         {
  136.           if (string_in_line ("Index", tag->nodename) != -1)
  137.             {
  138.               NODE *node;
  139.               REFERENCE **menu;
  140.  
  141.               /* Found one.  Get its menu. */
  142.               node = info_get_node (tag->filename, tag->nodename);
  143.               if (!node)
  144.                 continue;
  145.  
  146.               /* Remember the filename and nodename of this index. */
  147.               initial_index_filename = xstrdup (file_buffer->filename);
  148.               initial_index_nodename = xstrdup (tag->nodename);
  149.  
  150.               menu = info_menu_of_node (node);
  151.  
  152.               /* If we have a menu, add this index's nodename and range
  153.                  to our list of index_nodenames. */
  154.               if (menu)
  155.                 {
  156.                   add_index_to_index_nodenames (menu, node);
  157.  
  158.                   /* Concatenate the references found so far. */
  159.                   result = info_concatenate_references (result, menu);
  160.                 }
  161.               free (node);
  162.             }
  163.         }
  164.     }
  165.  
  166.   /* If there is a result, clean it up so that every entry has a filename. */
  167.   for (i = 0; result && result[i]; i++)
  168.     if (!result[i]->filename)
  169.       result[i]->filename = xstrdup (file_buffer->filename);
  170.  
  171.   return (result);
  172. }
  173.  
  174. DECLARE_INFO_COMMAND (info_index_search,
  175.    _("Look up a string in the index for this file"))
  176. {
  177.   do_info_index_search (window, count, 0);
  178. }
  179.  
  180. /* Look up SEARCH_STRING in the index for this file.  If SEARCH_STRING
  181.    is NULL, prompt user for input.  */ 
  182. void
  183. do_info_index_search (window, count, search_string)
  184.      WINDOW *window;
  185.      int count;
  186.      char *search_string;
  187. {
  188.   FILE_BUFFER *fb;
  189.   char *line;
  190.  
  191.   /* Reset the index offset, since this is not the info-index-next command. */
  192.   index_offset = 0;
  193.  
  194.   /* The user is selecting a new search string, so flush the old one. */
  195.   maybe_free (index_search);
  196.   index_search = (char *)NULL;
  197.  
  198.   /* If this window's file is not the same as the one that we last built an
  199.      index for, build and remember an index now. */
  200.   fb = file_buffer_of_window (window);
  201.   if (!initial_index_filename ||
  202.       (strcmp (initial_index_filename, fb->filename) != 0))
  203.     {
  204.       info_free_references (index_index);
  205.       window_message_in_echo_area (_("Finding index entries..."));
  206.       index_index = info_indices_of_file_buffer (fb);
  207.     }
  208.  
  209.   /* If there is no index, quit now. */
  210.   if (!index_index)
  211.     {
  212.       info_error (_("No indices found."));
  213.       return;
  214.     }
  215.  
  216.   /* Okay, there is an index.  Look for SEARCH_STRING, or, if it is
  217.      empty, prompt for one.  */
  218.   if (search_string && *search_string)
  219.     line = xstrdup (search_string);
  220.   else
  221.     {
  222.       line = info_read_maybe_completing (window, _("Index entry: "),
  223.                                          index_index);
  224.       window = active_window;
  225.  
  226.       /* User aborted? */
  227.       if (!line)
  228.         {
  229.           info_abort_key (active_window, 1, 0);
  230.           return;
  231.         }
  232.  
  233.       /* Empty line means move to the Index node. */
  234.       if (!*line)
  235.         {
  236.           free (line);
  237.  
  238.           if (initial_index_filename && initial_index_nodename)
  239.             {
  240.               NODE *node;
  241.  
  242.               node = info_get_node (initial_index_filename,
  243.                                     initial_index_nodename);
  244.               set_remembered_pagetop_and_point (window);
  245.               window_set_node_of_window (window, node);
  246.               remember_window_and_node (window, node);
  247.               window_clear_echo_area ();
  248.               return;
  249.             }
  250.         }
  251.     }
  252.  
  253.   /* The user typed either a completed index label, or a partial string.
  254.      Find an exact match, or, failing that, the first index entry containing
  255.      the partial string.  So, we just call info_next_index_match () with minor
  256.      manipulation of INDEX_OFFSET. */
  257.   {
  258.     int old_offset;
  259.  
  260.     /* Start the search right after/before this index. */
  261.     if (count < 0)
  262.       {
  263.         register int i;
  264.         for (i = 0; index_index[i]; i++);
  265.         index_offset = i;
  266.       }
  267.     else
  268.       index_offset = -1;
  269.  
  270.     old_offset = index_offset;
  271.  
  272.     /* The "last" string searched for is this one. */
  273.     index_search = line;
  274.  
  275.     /* Find it, or error. */
  276.     info_next_index_match (window, count, 0);
  277.  
  278.     /* If the search failed, return the index offset to where it belongs. */
  279.     if (index_offset == old_offset)
  280.       index_offset = 0;
  281.   }
  282. }
  283.  
  284. int
  285. index_entry_exists (window, string)
  286.      WINDOW *window;
  287.      char *string;
  288. {
  289.   register int i;
  290.   FILE_BUFFER *fb;
  291.  
  292.   /* If there is no previous search string, the user hasn't built an index
  293.      yet. */
  294.   if (!string)
  295.     return 0;
  296.  
  297.   fb = file_buffer_of_window (window);
  298.   if (!initial_index_filename
  299.       || (strcmp (initial_index_filename, fb->filename) != 0))
  300.     {
  301.       info_free_references (index_index);
  302.       index_index = info_indices_of_file_buffer (fb);
  303.     }
  304.  
  305.   /* If there is no index, that is an error. */
  306.   if (!index_index)
  307.     return 0;
  308.  
  309.   for (i = 0; (i > -1) && (index_index[i]); i++)
  310.     if (strcmp (string, index_index[i]->label) == 0)
  311.       break;
  312.  
  313.   /* If that failed, look for the next substring match. */
  314.   if ((i < 0) || (!index_index[i]))
  315.     {
  316.       for (i = 0; (i > -1) && (index_index[i]); i++)
  317.         if (string_in_line (string, index_index[i]->label) != -1)
  318.           break;
  319.  
  320.       if ((i > -1) && (index_index[i]))
  321.         string_in_line (string, index_index[i]->label);
  322.     }
  323.  
  324.   /* If that failed, return 0. */
  325.   if ((i < 0) || (!index_index[i]))
  326.     return 0;
  327.  
  328.   return 1;
  329. }
  330.  
  331. DECLARE_INFO_COMMAND (info_next_index_match,
  332.  _("Go to the next matching index item from the last `\\[index-search]' command"))
  333. {
  334.   register int i;
  335.   int partial, dir;
  336.   NODE *node;
  337.  
  338.   /* If there is no previous search string, the user hasn't built an index
  339.      yet. */
  340.   if (!index_search)
  341.     {
  342.       info_error (_("No previous index search string."));
  343.       return;
  344.     }
  345.  
  346.   /* If there is no index, that is an error. */
  347.   if (!index_index)
  348.     {
  349.       info_error (_("No index entries."));
  350.       return;
  351.     }
  352.  
  353.   /* The direction of this search is controlled by the value of the
  354.      numeric argument. */
  355.   if (count < 0)
  356.     dir = -1;
  357.   else
  358.     dir = 1;
  359.  
  360.   /* Search for the next occurence of index_search.  First try to find
  361.      an exact match. */
  362.   partial = 0;
  363.  
  364.   for (i = index_offset + dir; (i > -1) && (index_index[i]); i += dir)
  365.     if (strcmp (index_search, index_index[i]->label) == 0)
  366.       break;
  367.  
  368.   /* If that failed, look for the next substring match. */
  369.   if ((i < 0) || (!index_index[i]))
  370.     {
  371.       for (i = index_offset + dir; (i > -1) && (index_index[i]); i += dir)
  372.         if (string_in_line (index_search, index_index[i]->label) != -1)
  373.           break;
  374.  
  375.       if ((i > -1) && (index_index[i]))
  376.         partial = string_in_line (index_search, index_index[i]->label);
  377.     }
  378.  
  379.   /* If that failed, print an error. */
  380.   if ((i < 0) || (!index_index[i]))
  381.     {
  382.       info_error (_("No %sindex entries containing \"%s\"."),
  383.                   index_offset > 0 ? _("more ") : "", index_search);
  384.       return;
  385.     }
  386.  
  387.   /* Okay, we found the next one.  Move the offset to the current entry. */
  388.   index_offset = i;
  389.  
  390.   /* Report to the user on what we have found. */
  391.   {
  392.     register int j;
  393.     char *name = _("CAN'T SEE THIS");
  394.     char *match;
  395.  
  396.     for (j = 0; index_nodenames[j]; j++)
  397.       {
  398.         if ((i >= index_nodenames[j]->first) &&
  399.             (i <= index_nodenames[j]->last))
  400.           {
  401.             name = index_nodenames[j]->name;
  402.             break;
  403.           }
  404.       }
  405.  
  406.     /* If we had a partial match, indicate to the user which part of the
  407.        string matched. */
  408.     match = xstrdup (index_index[i]->label);
  409.  
  410.     if (partial && show_index_match)
  411.       {
  412.         int j, ls, start, upper;
  413.  
  414.         ls = strlen (index_search);
  415.         start = partial - ls;
  416.         upper = isupper (match[start]) ? 1 : 0;
  417.  
  418.         for (j = 0; j < ls; j++)
  419.           if (upper)
  420.             match[j + start] = info_tolower (match[j + start]);
  421.           else
  422.             match[j + start] = info_toupper (match[j + start]);
  423.       }
  424.  
  425.     {
  426.       char *format;
  427.  
  428.       format = replace_in_documentation
  429.         (_("Found \"%s\" in %s. (`\\[next-index-match]' tries to find next.)"));
  430.  
  431.       window_message_in_echo_area (format, match, name);
  432.     }
  433.  
  434.     free (match);
  435.   }
  436.  
  437.   /* Select the node corresponding to this index entry. */
  438.   node = info_get_node (index_index[i]->filename, index_index[i]->nodename);
  439.  
  440.   if (!node)
  441.     {
  442.       info_error (CANT_FILE_NODE,
  443.                   index_index[i]->filename, index_index[i]->nodename);
  444.       return;
  445.     }
  446.  
  447.   set_remembered_pagetop_and_point (window);
  448.   window_set_node_of_window (window, node);
  449.   remember_window_and_node (window, node);
  450.  
  451.  
  452.   /* Try to find an occurence of LABEL in this node. */
  453.   {
  454.     long start, loc;
  455.  
  456.     start = window->line_starts[1] - window->node->contents;
  457.     loc = info_target_search_node (node, index_index[i]->label, start);
  458.  
  459.     if (loc != -1)
  460.       {
  461.         window->point = loc;
  462.         window_adjust_pagetop (window);
  463.       }
  464.   }
  465. }
  466.  
  467. /* **************************************************************** */
  468. /*                                                                  */
  469. /*                 Info APROPOS: Search every known index.          */
  470. /*                                                                  */
  471. /* **************************************************************** */
  472.  
  473. /* For every menu item in DIR, search the indices of that file for
  474.    SEARCH_STRING. */
  475. REFERENCE **
  476. apropos_in_all_indices (search_string, inform)
  477.      char *search_string;
  478.      int inform;
  479. {
  480.   register int i, dir_index;
  481.   REFERENCE **all_indices = (REFERENCE **)NULL;
  482.   REFERENCE **dir_menu = (REFERENCE **)NULL;
  483.   NODE *dir_node;
  484.  
  485.   dir_node = info_get_node ("dir", "Top");
  486.   if (dir_node)
  487.     dir_menu = info_menu_of_node (dir_node);
  488.  
  489.   if (!dir_menu)
  490.     return NULL;
  491.  
  492.   /* For every menu item in DIR, get the associated node's file buffer and
  493.      read the indices of that file buffer.  Gather all of the indices into
  494.      one large one. */
  495.   for (dir_index = 0; dir_menu[dir_index]; dir_index++)
  496.     {
  497.       REFERENCE **this_index, *this_item;
  498.       NODE *this_node;
  499.       FILE_BUFFER *this_fb;
  500.  
  501.       this_item = dir_menu[dir_index];
  502.  
  503.       if (!this_item->filename)
  504.         {
  505.           if (dir_node->parent)
  506.             this_item->filename = xstrdup (dir_node->parent);
  507.           else
  508.             this_item->filename = xstrdup (dir_node->filename);
  509.         }
  510.  
  511.       /* Find this node.  If we cannot find it, try using the label of the
  512.          entry as a file (i.e., "(LABEL)Top"). */
  513.       this_node = info_get_node (this_item->filename, this_item->nodename);
  514.  
  515.       if (!this_node && this_item->nodename &&
  516.           (strcmp (this_item->label, this_item->nodename) == 0))
  517.         this_node = info_get_node (this_item->label, "Top");
  518.  
  519.       if (!this_node)
  520.         continue;
  521.  
  522.       /* Get the file buffer associated with this node. */
  523.       {
  524.         char *files_name;
  525.  
  526.         files_name = this_node->parent;
  527.         if (!files_name)
  528.           files_name = this_node->filename;
  529.  
  530.         this_fb = info_find_file (files_name);
  531.  
  532.         if (this_fb && inform)
  533.           message_in_echo_area (_("Scanning indices of \"%s\"..."), files_name);
  534.  
  535.         this_index = info_indices_of_file_buffer (this_fb);
  536.         free (this_node);
  537.  
  538.         if (this_fb && inform)
  539.           unmessage_in_echo_area ();
  540.       }
  541.  
  542.       if (this_index)
  543.         {
  544.           /* Remember the filename which contains this set of references. */
  545.           for (i = 0; this_index && this_index[i]; i++)
  546.             if (!this_index[i]->filename)
  547.               this_index[i]->filename = xstrdup (this_fb->filename);
  548.  
  549.           /* Concatenate with the other indices.  */
  550.           all_indices = info_concatenate_references (all_indices, this_index);
  551.         }
  552.     }
  553.  
  554.   info_free_references (dir_menu);
  555.  
  556.   /* Build a list of the references which contain SEARCH_STRING. */
  557.   if (all_indices)
  558.     {
  559.       REFERENCE *entry, **apropos_list = (REFERENCE **)NULL;
  560.       int apropos_list_index = 0;
  561.       int apropos_list_slots = 0;
  562.  
  563.       for (i = 0; (entry = all_indices[i]); i++)
  564.         {
  565.           if (string_in_line (search_string, entry->label) != -1)
  566.             {
  567.               add_pointer_to_array
  568.                 (entry, apropos_list_index, apropos_list, apropos_list_slots,
  569.                  100, REFERENCE *);
  570.             }
  571.           else
  572.             {
  573.               maybe_free (entry->label);
  574.               maybe_free (entry->filename);
  575.               maybe_free (entry->nodename);
  576.               free (entry);
  577.             }
  578.         }
  579.  
  580.       free (all_indices);
  581.       all_indices = apropos_list;
  582.     }
  583.   return (all_indices);
  584. }
  585.  
  586. #define APROPOS_NONE \
  587.    _("No available info files reference \"%s\" in their indices.")
  588.  
  589. void
  590. info_apropos (string)
  591.      char *string;
  592. {
  593.   REFERENCE **apropos_list;
  594.  
  595.   apropos_list = apropos_in_all_indices (string, 0);
  596.  
  597.   if (!apropos_list)
  598.     {
  599.       info_error (APROPOS_NONE, string);
  600.     }
  601.   else
  602.     {
  603.       register int i;
  604.       REFERENCE *entry;
  605.  
  606.       for (i = 0; (entry = apropos_list[i]); i++)
  607.         fprintf (stderr, "\"(%s)%s\" -- %s\n",
  608.                  entry->filename, entry->nodename, entry->label);
  609.     }
  610.   info_free_references (apropos_list);
  611. }
  612.  
  613. static char *apropos_list_nodename = "*Apropos*";
  614.  
  615. DECLARE_INFO_COMMAND (info_index_apropos,
  616.    _("Grovel all known info file's indices for a string and build a menu"))
  617. {
  618.   char *line;
  619.  
  620.   line = info_read_in_echo_area (window, _("Index apropos: "));
  621.  
  622.   window = active_window;
  623.  
  624.   /* User aborted? */
  625.   if (!line)
  626.     {
  627.       info_abort_key (window, 1, 1);
  628.       return;
  629.     }
  630.  
  631.   /* User typed something? */
  632.   if (*line)
  633.     {
  634.       REFERENCE **apropos_list;
  635.       NODE *apropos_node;
  636.  
  637.       apropos_list = apropos_in_all_indices (line, 1);
  638.  
  639.       if (!apropos_list)
  640.         {
  641.           info_error (APROPOS_NONE, line);
  642.         }
  643.       else
  644.         {
  645.           register int i;
  646.           char *line_buffer;
  647.  
  648.           initialize_message_buffer ();
  649.           printf_to_message_buffer
  650.             (_("\n* Menu: Nodes whoses indices contain \"%s\":\n"), line);
  651.           line_buffer = (char *)xmalloc (500);
  652.  
  653.           for (i = 0; apropos_list[i]; i++)
  654.             {
  655.               int len;
  656.               sprintf (line_buffer, "* (%s)%s::",
  657.                        apropos_list[i]->filename, apropos_list[i]->nodename);
  658.               len = pad_to (36, line_buffer);
  659.               sprintf (line_buffer + len, "%s", apropos_list[i]->label);
  660.               printf_to_message_buffer ("%s\n", line_buffer);
  661.             }
  662.           free (line_buffer);
  663.         }
  664.  
  665.       apropos_node = message_buffer_to_node ();
  666.       add_gcable_pointer (apropos_node->contents);
  667.       name_internal_node (apropos_node, apropos_list_nodename);
  668.  
  669.       /* Even though this is an internal node, we don't want the window
  670.          system to treat it specially.  So we turn off the internalness
  671.          of it here. */
  672.       apropos_node->flags &= ~N_IsInternal;
  673.  
  674.       /* Find/Create a window to contain this node. */
  675.       {
  676.         WINDOW *new;
  677.         NODE *node;
  678.  
  679.         set_remembered_pagetop_and_point (window);
  680.  
  681.         /* If a window is visible and showing an apropos list already,
  682.            re-use it. */
  683.         for (new = windows; new; new = new->next)
  684.           {
  685.             node = new->node;
  686.  
  687.             if (internal_info_node_p (node) &&
  688.                 (strcmp (node->nodename, apropos_list_nodename) == 0))
  689.               break;
  690.           }
  691.  
  692.         /* If we couldn't find an existing window, try to use the next window
  693.            in the chain. */
  694.         if (!new && window->next)
  695.           new = window->next;
  696.  
  697.         /* If we still don't have a window, make a new one to contain
  698.            the list. */
  699.         if (!new)
  700.           {
  701.             WINDOW *old_active;
  702.  
  703.             old_active = active_window;
  704.             active_window = window;
  705.             new = window_make_window ((NODE *)NULL);
  706.             active_window = old_active;
  707.           }
  708.  
  709.         /* If we couldn't make a new window, use this one. */
  710.         if (!new)
  711.           new = window;
  712.  
  713.         /* Lines do not wrap in this window. */
  714.         new->flags |= W_NoWrap;
  715.  
  716.         window_set_node_of_window (new, apropos_node);
  717.         remember_window_and_node (new, apropos_node);
  718.         active_window = new;
  719.       }
  720.       info_free_references (apropos_list);
  721.     }
  722.   free (line);
  723.  
  724.   if (!info_error_was_printed)
  725.     window_clear_echo_area ();
  726. }
  727.  
  728.