* decl.c (pop_cp_function_context): Don't call free on a NULL
[official-gcc.git] / texinfo / info / indices.c
blob03e4b35a75a0a2ce8d8bcd54295cfd529dfb3e55
1 /* indices.c -- Commands for dealing with an Info file Index.
2 $Id: indices.c,v 1.1.1.2 1998/03/22 20:42:33 law Exp $
4 Copyright (C) 1993, 97 Free Software Foundation, Inc.
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.
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.
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.
20 Written by Brian Fox (bfox@ai.mit.edu). */
22 #include "info.h"
23 #include "indices.h"
25 /* User-visible variable controls the output of info-index-next. */
26 int show_index_match = 1;
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;
32 /* The offset of the most recently selected index element. */
33 static int index_offset = 0;
35 /* Variable which holds the last string searched for. */
36 static char *index_search = (char *)NULL;
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;
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;
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;
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;
61 register int i, last;
62 INDEX_NAME_ASSOC *assoc;
64 for (last = 0; array[last]; last++);
65 assoc = (INDEX_NAME_ASSOC *)xmalloc (sizeof (INDEX_NAME_ASSOC));
66 assoc->name = xstrdup (node->nodename);
68 if (!index_nodenames_index)
70 assoc->first = 0;
71 assoc->last = last;
73 else
75 for (i = 0; index_nodenames[i + 1]; i++);
76 assoc->first = 1 + index_nodenames[i]->last;
77 assoc->last = assoc->first + last;
79 add_pointer_to_array
80 (assoc, index_nodenames_index, index_nodenames, index_nodenames_slots,
81 10, INDEX_NAME_ASSOC *);
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;
93 FILE_BUFFER *fb;
95 fb = file_buffer_of_window (window);
97 return (info_indices_of_file_buffer (fb));
100 REFERENCE **
101 info_indices_of_file_buffer (file_buffer)
102 FILE_BUFFER *file_buffer;
104 register int i;
105 REFERENCE **result = (REFERENCE **)NULL;
107 /* No file buffer, no indices. */
108 if (!file_buffer)
109 return ((REFERENCE **)NULL);
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;
117 if (index_nodenames)
119 for (i = 0; index_nodenames[i]; i++)
121 free (index_nodenames[i]->name);
122 free (index_nodenames[i]);
125 index_nodenames_index = 0;
126 index_nodenames[0] = (INDEX_NAME_ASSOC *)NULL;
129 /* Grovel the names of the nodes found in this file. */
130 if (file_buffer->tags)
132 TAG *tag;
134 for (i = 0; (tag = file_buffer->tags[i]); i++)
136 if (string_in_line ("Index", tag->nodename) != -1)
138 NODE *node;
139 REFERENCE **menu;
141 /* Found one. Get its menu. */
142 node = info_get_node (tag->filename, tag->nodename);
143 if (!node)
144 continue;
146 /* Remember the filename and nodename of this index. */
147 initial_index_filename = xstrdup (file_buffer->filename);
148 initial_index_nodename = xstrdup (tag->nodename);
150 menu = info_menu_of_node (node);
152 /* If we have a menu, add this index's nodename and range
153 to our list of index_nodenames. */
154 if (menu)
156 add_index_to_index_nodenames (menu, node);
158 /* Concatenate the references found so far. */
159 result = info_concatenate_references (result, menu);
161 free (node);
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);
171 return (result);
174 DECLARE_INFO_COMMAND (info_index_search,
175 _("Look up a string in the index for this file"))
177 do_info_index_search (window, count, 0);
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;
188 FILE_BUFFER *fb;
189 char *line;
191 /* Reset the index offset, since this is not the info-index-next command. */
192 index_offset = 0;
194 /* The user is selecting a new search string, so flush the old one. */
195 maybe_free (index_search);
196 index_search = (char *)NULL;
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))
204 info_free_references (index_index);
205 window_message_in_echo_area (_("Finding index entries..."));
206 index_index = info_indices_of_file_buffer (fb);
209 /* If there is no index, quit now. */
210 if (!index_index)
212 info_error (_("No indices found."));
213 return;
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
222 line = info_read_maybe_completing (window, _("Index entry: "),
223 index_index);
224 window = active_window;
226 /* User aborted? */
227 if (!line)
229 info_abort_key (active_window, 1, 0);
230 return;
233 /* Empty line means move to the Index node. */
234 if (!*line)
236 free (line);
238 if (initial_index_filename && initial_index_nodename)
240 NODE *node;
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;
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. */
258 int old_offset;
260 /* Start the search right after/before this index. */
261 if (count < 0)
263 register int i;
264 for (i = 0; index_index[i]; i++);
265 index_offset = i;
267 else
268 index_offset = -1;
270 old_offset = index_offset;
272 /* The "last" string searched for is this one. */
273 index_search = line;
275 /* Find it, or error. */
276 info_next_index_match (window, count, 0);
278 /* If the search failed, return the index offset to where it belongs. */
279 if (index_offset == old_offset)
280 index_offset = 0;
285 index_entry_exists (window, string)
286 WINDOW *window;
287 char *string;
289 register int i;
290 FILE_BUFFER *fb;
292 /* If there is no previous search string, the user hasn't built an index
293 yet. */
294 if (!string)
295 return 0;
297 fb = file_buffer_of_window (window);
298 if (!initial_index_filename
299 || (strcmp (initial_index_filename, fb->filename) != 0))
301 info_free_references (index_index);
302 index_index = info_indices_of_file_buffer (fb);
305 /* If there is no index, that is an error. */
306 if (!index_index)
307 return 0;
309 for (i = 0; (i > -1) && (index_index[i]); i++)
310 if (strcmp (string, index_index[i]->label) == 0)
311 break;
313 /* If that failed, look for the next substring match. */
314 if ((i < 0) || (!index_index[i]))
316 for (i = 0; (i > -1) && (index_index[i]); i++)
317 if (string_in_line (string, index_index[i]->label) != -1)
318 break;
320 if ((i > -1) && (index_index[i]))
321 string_in_line (string, index_index[i]->label);
324 /* If that failed, return 0. */
325 if ((i < 0) || (!index_index[i]))
326 return 0;
328 return 1;
331 DECLARE_INFO_COMMAND (info_next_index_match,
332 _("Go to the next matching index item from the last `\\[index-search]' command"))
334 register int i;
335 int partial, dir;
336 NODE *node;
338 /* If there is no previous search string, the user hasn't built an index
339 yet. */
340 if (!index_search)
342 info_error (_("No previous index search string."));
343 return;
346 /* If there is no index, that is an error. */
347 if (!index_index)
349 info_error (_("No index entries."));
350 return;
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;
360 /* Search for the next occurence of index_search. First try to find
361 an exact match. */
362 partial = 0;
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;
368 /* If that failed, look for the next substring match. */
369 if ((i < 0) || (!index_index[i]))
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;
375 if ((i > -1) && (index_index[i]))
376 partial = string_in_line (index_search, index_index[i]->label);
379 /* If that failed, print an error. */
380 if ((i < 0) || (!index_index[i]))
382 info_error (_("No %sindex entries containing \"%s\"."),
383 index_offset > 0 ? _("more ") : "", index_search);
384 return;
387 /* Okay, we found the next one. Move the offset to the current entry. */
388 index_offset = i;
390 /* Report to the user on what we have found. */
392 register int j;
393 char *name = _("CAN'T SEE THIS");
394 char *match;
396 for (j = 0; index_nodenames[j]; j++)
398 if ((i >= index_nodenames[j]->first) &&
399 (i <= index_nodenames[j]->last))
401 name = index_nodenames[j]->name;
402 break;
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);
410 if (partial && show_index_match)
412 int j, ls, start, upper;
414 ls = strlen (index_search);
415 start = partial - ls;
416 upper = isupper (match[start]) ? 1 : 0;
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]);
426 char *format;
428 format = replace_in_documentation
429 (_("Found \"%s\" in %s. (`\\[next-index-match]' tries to find next.)"));
431 window_message_in_echo_area (format, match, name);
434 free (match);
437 /* Select the node corresponding to this index entry. */
438 node = info_get_node (index_index[i]->filename, index_index[i]->nodename);
440 if (!node)
442 info_error (CANT_FILE_NODE,
443 index_index[i]->filename, index_index[i]->nodename);
444 return;
447 set_remembered_pagetop_and_point (window);
448 window_set_node_of_window (window, node);
449 remember_window_and_node (window, node);
452 /* Try to find an occurence of LABEL in this node. */
454 long start, loc;
456 start = window->line_starts[1] - window->node->contents;
457 loc = info_target_search_node (node, index_index[i]->label, start);
459 if (loc != -1)
461 window->point = loc;
462 window_adjust_pagetop (window);
467 /* **************************************************************** */
468 /* */
469 /* Info APROPOS: Search every known index. */
470 /* */
471 /* **************************************************************** */
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;
480 register int i, dir_index;
481 REFERENCE **all_indices = (REFERENCE **)NULL;
482 REFERENCE **dir_menu = (REFERENCE **)NULL;
483 NODE *dir_node;
485 dir_node = info_get_node ("dir", "Top");
486 if (dir_node)
487 dir_menu = info_menu_of_node (dir_node);
489 if (!dir_menu)
490 return NULL;
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++)
497 REFERENCE **this_index, *this_item;
498 NODE *this_node;
499 FILE_BUFFER *this_fb;
501 this_item = dir_menu[dir_index];
503 if (!this_item->filename)
505 if (dir_node->parent)
506 this_item->filename = xstrdup (dir_node->parent);
507 else
508 this_item->filename = xstrdup (dir_node->filename);
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);
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");
519 if (!this_node)
520 continue;
522 /* Get the file buffer associated with this node. */
524 char *files_name;
526 files_name = this_node->parent;
527 if (!files_name)
528 files_name = this_node->filename;
530 this_fb = info_find_file (files_name);
532 if (this_fb && inform)
533 message_in_echo_area (_("Scanning indices of \"%s\"..."), files_name);
535 this_index = info_indices_of_file_buffer (this_fb);
536 free (this_node);
538 if (this_fb && inform)
539 unmessage_in_echo_area ();
542 if (this_index)
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);
549 /* Concatenate with the other indices. */
550 all_indices = info_concatenate_references (all_indices, this_index);
554 info_free_references (dir_menu);
556 /* Build a list of the references which contain SEARCH_STRING. */
557 if (all_indices)
559 REFERENCE *entry, **apropos_list = (REFERENCE **)NULL;
560 int apropos_list_index = 0;
561 int apropos_list_slots = 0;
563 for (i = 0; (entry = all_indices[i]); i++)
565 if (string_in_line (search_string, entry->label) != -1)
567 add_pointer_to_array
568 (entry, apropos_list_index, apropos_list, apropos_list_slots,
569 100, REFERENCE *);
571 else
573 maybe_free (entry->label);
574 maybe_free (entry->filename);
575 maybe_free (entry->nodename);
576 free (entry);
580 free (all_indices);
581 all_indices = apropos_list;
583 return (all_indices);
586 #define APROPOS_NONE \
587 _("No available info files reference \"%s\" in their indices.")
589 void
590 info_apropos (string)
591 char *string;
593 REFERENCE **apropos_list;
595 apropos_list = apropos_in_all_indices (string, 0);
597 if (!apropos_list)
599 info_error (APROPOS_NONE, string);
601 else
603 register int i;
604 REFERENCE *entry;
606 for (i = 0; (entry = apropos_list[i]); i++)
607 fprintf (stderr, "\"(%s)%s\" -- %s\n",
608 entry->filename, entry->nodename, entry->label);
610 info_free_references (apropos_list);
613 static char *apropos_list_nodename = "*Apropos*";
615 DECLARE_INFO_COMMAND (info_index_apropos,
616 _("Grovel all known info file's indices for a string and build a menu"))
618 char *line;
620 line = info_read_in_echo_area (window, _("Index apropos: "));
622 window = active_window;
624 /* User aborted? */
625 if (!line)
627 info_abort_key (window, 1, 1);
628 return;
631 /* User typed something? */
632 if (*line)
634 REFERENCE **apropos_list;
635 NODE *apropos_node;
637 apropos_list = apropos_in_all_indices (line, 1);
639 if (!apropos_list)
641 info_error (APROPOS_NONE, line);
643 else
645 register int i;
646 char *line_buffer;
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);
653 for (i = 0; apropos_list[i]; i++)
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);
662 free (line_buffer);
665 apropos_node = message_buffer_to_node ();
666 add_gcable_pointer (apropos_node->contents);
667 name_internal_node (apropos_node, apropos_list_nodename);
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;
674 /* Find/Create a window to contain this node. */
676 WINDOW *new;
677 NODE *node;
679 set_remembered_pagetop_and_point (window);
681 /* If a window is visible and showing an apropos list already,
682 re-use it. */
683 for (new = windows; new; new = new->next)
685 node = new->node;
687 if (internal_info_node_p (node) &&
688 (strcmp (node->nodename, apropos_list_nodename) == 0))
689 break;
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;
697 /* If we still don't have a window, make a new one to contain
698 the list. */
699 if (!new)
701 WINDOW *old_active;
703 old_active = active_window;
704 active_window = window;
705 new = window_make_window ((NODE *)NULL);
706 active_window = old_active;
709 /* If we couldn't make a new window, use this one. */
710 if (!new)
711 new = window;
713 /* Lines do not wrap in this window. */
714 new->flags |= W_NoWrap;
716 window_set_node_of_window (new, apropos_node);
717 remember_window_and_node (new, apropos_node);
718 active_window = new;
720 info_free_references (apropos_list);
722 free (line);
724 if (!info_error_was_printed)
725 window_clear_echo_area ();