* fixincludes: Tweak fix for struct exception in math.h
[official-gcc.git] / texinfo / info / indices.c
blob6848884288bad2a6802485c7c59616fe3832be14
1 /* indices.c -- Commands for dealing with an Info file Index. */
3 /* This file is part of GNU Info, a program for reading online documentation
4 stored in Info format.
6 Copyright (C) 1993 Free Software Foundation, Inc.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2, or (at your option)
11 any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 Written by Brian Fox (bfox@ai.mit.edu). */
24 #include "info.h"
25 #include "indices.h"
27 /* User-visible variable controls the output of info-index-next. */
28 int show_index_match = 1;
30 /* In the Info sense, an index is a menu. This variable holds the last
31 parsed index. */
32 static REFERENCE **index_index = (REFERENCE **)NULL;
34 /* The offset of the most recently selected index element. */
35 static int index_offset = 0;
37 /* Variable which holds the last string searched for. */
38 static char *index_search = (char *)NULL;
40 /* A couple of "globals" describing where the initial index was found. */
41 static char *initial_index_filename = (char *)NULL;
42 static char *initial_index_nodename = (char *)NULL;
44 /* A structure associating index names with index offset ranges. */
45 typedef struct {
46 char *name; /* The nodename of this index. */
47 int first; /* The index in our list of the first entry. */
48 int last; /* The index in our list of the last entry. */
49 } INDEX_NAME_ASSOC;
51 /* An array associating index nodenames with index offset ranges. */
52 static INDEX_NAME_ASSOC **index_nodenames = (INDEX_NAME_ASSOC **)NULL;
53 static int index_nodenames_index = 0;
54 static int index_nodenames_slots = 0;
56 /* Add the name of NODE, and the range of the associated index elements
57 (passed in ARRAY) to index_nodenames. */
58 static void
59 add_index_to_index_nodenames (array, node)
60 REFERENCE **array;
61 NODE *node;
63 register int i, last;
64 INDEX_NAME_ASSOC *assoc;
66 for (last = 0; array[last]; last++);
67 assoc = (INDEX_NAME_ASSOC *)xmalloc (sizeof (INDEX_NAME_ASSOC));
68 assoc->name = strdup (node->nodename);
70 if (!index_nodenames_index)
72 assoc->first = 0;
73 assoc->last = last;
75 else
77 for (i = 0; index_nodenames[i + 1]; i++);
78 assoc->first = 1 + index_nodenames[i]->last;
79 assoc->last = assoc->first + last;
81 add_pointer_to_array
82 (assoc, index_nodenames_index, index_nodenames, index_nodenames_slots,
83 10, INDEX_NAME_ASSOC *);
86 /* Find and return the indices of WINDOW's file. The indices are defined
87 as the first node in the file containing the word "Index" and any
88 immediately following nodes whose names also contain "Index". All such
89 indices are concatenated and the result returned. If WINDOW's info file
90 doesn't have any indices, a NULL pointer is returned. */
91 REFERENCE **
92 info_indices_of_window (window)
93 WINDOW *window;
95 FILE_BUFFER *fb;
97 fb = file_buffer_of_window (window);
99 return (info_indices_of_file_buffer (fb));
102 REFERENCE **
103 info_indices_of_file_buffer (file_buffer)
104 FILE_BUFFER *file_buffer;
106 register int i;
107 REFERENCE **result = (REFERENCE **)NULL;
109 /* No file buffer, no indices. */
110 if (!file_buffer)
111 return ((REFERENCE **)NULL);
113 /* Reset globals describing where the index was found. */
114 maybe_free (initial_index_filename);
115 maybe_free (initial_index_nodename);
116 initial_index_filename = (char *)NULL;
117 initial_index_nodename = (char *)NULL;
119 if (index_nodenames)
121 for (i = 0; index_nodenames[i]; i++)
123 free (index_nodenames[i]->name);
124 free (index_nodenames[i]);
127 index_nodenames_index = 0;
128 index_nodenames[0] = (INDEX_NAME_ASSOC *)NULL;
131 /* Grovel the names of the nodes found in this file. */
132 if (file_buffer->tags)
134 TAG *tag;
136 for (i = 0; tag = file_buffer->tags[i]; i++)
138 if (string_in_line ("Index", tag->nodename) != -1)
140 NODE *node;
141 REFERENCE **menu;
143 /* Found one. Get its menu. */
144 node = info_get_node (tag->filename, tag->nodename);
145 if (!node)
146 continue;
148 /* Remember the filename and nodename of this index. */
149 initial_index_filename = strdup (file_buffer->filename);
150 initial_index_nodename = strdup (tag->nodename);
152 menu = info_menu_of_node (node);
154 /* If we have a menu, add this index's nodename and range
155 to our list of index_nodenames. */
156 if (menu)
158 add_index_to_index_nodenames (menu, node);
160 /* Concatenate the references found so far. */
161 result = info_concatenate_references (result, menu);
163 free (node);
168 /* If there is a result, clean it up so that every entry has a filename. */
169 for (i = 0; result && result[i]; i++)
170 if (!result[i]->filename)
171 result[i]->filename = strdup (file_buffer->filename);
173 return (result);
176 DECLARE_INFO_COMMAND (info_index_search,
177 "Look up a string in the index for this file")
179 FILE_BUFFER *fb;
180 char *line;
182 /* Reset the index offset, since this is not the info-index-next command. */
183 index_offset = 0;
185 /* The user is selecting a new search string, so flush the old one. */
186 maybe_free (index_search);
187 index_search = (char *)NULL;
189 /* If this window's file is not the same as the one that we last built an
190 index for, build and remember an index now. */
191 fb = file_buffer_of_window (window);
192 if (!initial_index_filename ||
193 (strcmp (initial_index_filename, fb->filename) != 0))
195 info_free_references (index_index);
196 window_message_in_echo_area ("Finding index entries...");
197 index_index = info_indices_of_file_buffer (fb);
200 /* If there is no index, quit now. */
201 if (!index_index)
203 info_error ("No indices found.");
204 return;
207 /* Okay, there is an index. Let the user select one of the members of it. */
208 line =
209 info_read_maybe_completing (window, "Index entry: ", index_index);
211 window = active_window;
213 /* User aborted? */
214 if (!line)
216 info_abort_key (active_window, 1, 0);
217 return;
220 /* Empty line means move to the Index node. */
221 if (!*line)
223 free (line);
225 if (initial_index_filename && initial_index_nodename)
227 NODE *node;
229 node =
230 info_get_node (initial_index_filename, initial_index_nodename);
231 set_remembered_pagetop_and_point (window);
232 window_set_node_of_window (window, node);
233 remember_window_and_node (window, node);
234 window_clear_echo_area ();
235 return;
239 /* The user typed either a completed index label, or a partial string.
240 Find an exact match, or, failing that, the first index entry containing
241 the partial string. So, we just call info_next_index_match () with minor
242 manipulation of INDEX_OFFSET. */
244 int old_offset;
246 /* Start the search right after/before this index. */
247 if (count < 0)
249 register int i;
250 for (i = 0; index_index[i]; i++);
251 index_offset = i;
253 else
254 index_offset = -1;
256 old_offset = index_offset;
258 /* The "last" string searched for is this one. */
259 index_search = line;
261 /* Find it, or error. */
262 info_next_index_match (window, count, 0);
264 /* If the search failed, return the index offset to where it belongs. */
265 if (index_offset == old_offset)
266 index_offset = 0;
270 DECLARE_INFO_COMMAND (info_next_index_match,
271 "Go to the next matching index item from the last `\\[index-search]' command")
273 register int i;
274 int partial, dir;
275 NODE *node;
277 /* If there is no previous search string, the user hasn't built an index
278 yet. */
279 if (!index_search)
281 info_error ("No previous index search string.");
282 return;
285 /* If there is no index, that is an error. */
286 if (!index_index)
288 info_error ("No index entries.");
289 return;
292 /* The direction of this search is controlled by the value of the
293 numeric argument. */
294 if (count < 0)
295 dir = -1;
296 else
297 dir = 1;
299 /* Search for the next occurence of index_search. First try to find
300 an exact match. */
301 partial = 0;
303 for (i = index_offset + dir; (i > -1) && (index_index[i]); i += dir)
304 if (strcmp (index_search, index_index[i]->label) == 0)
305 break;
307 /* If that failed, look for the next substring match. */
308 if ((i < 0) || (!index_index[i]))
310 for (i = index_offset + dir; (i > -1) && (index_index[i]); i += dir)
311 if (string_in_line (index_search, index_index[i]->label) != -1)
312 break;
314 if ((i > -1) && (index_index[i]))
315 partial = string_in_line (index_search, index_index[i]->label);
318 /* If that failed, print an error. */
319 if ((i < 0) || (!index_index[i]))
321 info_error ("No %sindex entries containing \"%s\".",
322 index_offset > 0 ? "more " : "", index_search);
323 return;
326 /* Okay, we found the next one. Move the offset to the current entry. */
327 index_offset = i;
329 /* Report to the user on what we have found. */
331 register int j;
332 char *name = "CAN'T SEE THIS";
333 char *match;
335 for (j = 0; index_nodenames[j]; j++)
337 if ((i >= index_nodenames[j]->first) &&
338 (i <= index_nodenames[j]->last))
340 name = index_nodenames[j]->name;
341 break;
345 /* If we had a partial match, indicate to the user which part of the
346 string matched. */
347 match = strdup (index_index[i]->label);
349 if (partial && show_index_match)
351 int j, ls, start, upper;
353 ls = strlen (index_search);
354 start = partial - ls;
355 upper = isupper (match[start]) ? 1 : 0;
357 for (j = 0; j < ls; j++)
358 if (upper)
359 match[j + start] = info_tolower (match[j + start]);
360 else
361 match[j + start] = info_toupper (match[j + start]);
365 char *format;
367 format = replace_in_documentation
368 ("Found \"%s\" in %s. (`\\[next-index-match]' tries to find next.)");
370 window_message_in_echo_area (format, match, name);
373 free (match);
376 /* Select the node corresponding to this index entry. */
377 node = info_get_node (index_index[i]->filename, index_index[i]->nodename);
379 if (!node)
381 info_error (CANT_FILE_NODE,
382 index_index[i]->filename, index_index[i]->nodename);
383 return;
386 set_remembered_pagetop_and_point (window);
387 window_set_node_of_window (window, node);
388 remember_window_and_node (window, node);
391 /* Try to find an occurence of LABEL in this node. */
393 long start, loc;
395 start = window->line_starts[1] - window->node->contents;
396 loc = info_target_search_node (node, index_index[i]->label, start);
398 if (loc != -1)
400 window->point = loc;
401 window_adjust_pagetop (window);
406 /* **************************************************************** */
407 /* */
408 /* Info APROPOS: Search every known index. */
409 /* */
410 /* **************************************************************** */
412 /* For every menu item in DIR, search the indices of that file for
413 SEARCH_STRING. */
414 REFERENCE **
415 apropos_in_all_indices (search_string, inform)
416 char *search_string;
417 int inform;
419 register int i, dir_index;
420 REFERENCE **all_indices = (REFERENCE **)NULL;
421 REFERENCE **dir_menu = (REFERENCE **)NULL;
422 NODE *dir_node;
423 int printed = 0;
425 dir_node = info_get_node ("dir", "Top");
426 if (dir_node)
427 dir_menu = info_menu_of_node (dir_node);
429 if (!dir_menu)
430 return;
432 /* For every menu item in DIR, get the associated node's file buffer and
433 read the indices of that file buffer. Gather all of the indices into
434 one large one. */
435 for (dir_index = 0; dir_menu[dir_index]; dir_index++)
437 REFERENCE **this_index, *this_item;
438 NODE *this_node;
439 FILE_BUFFER *this_fb;
441 this_item = dir_menu[dir_index];
443 if (!this_item->filename)
445 if (dir_node->parent)
446 this_item->filename = strdup (dir_node->parent);
447 else
448 this_item->filename = strdup (dir_node->filename);
451 /* Find this node. If we cannot find it, try using the label of the
452 entry as a file (i.e., "(LABEL)Top"). */
453 this_node = info_get_node (this_item->filename, this_item->nodename);
455 if (!this_node && this_item->nodename &&
456 (strcmp (this_item->label, this_item->nodename) == 0))
457 this_node = info_get_node (this_item->label, "Top");
459 if (!this_node)
460 continue;
462 /* Get the file buffer associated with this node. */
464 char *files_name;
466 files_name = this_node->parent;
467 if (!files_name)
468 files_name = this_node->filename;
470 this_fb = info_find_file (files_name);
472 if (this_fb && inform)
473 message_in_echo_area ("Scanning indices of \"%s\"...", files_name);
475 this_index = info_indices_of_file_buffer (this_fb);
476 free (this_node);
478 if (this_fb && inform)
479 unmessage_in_echo_area ();
482 if (this_index)
484 /* Remember the filename which contains this set of references. */
485 for (i = 0; this_index && this_index[i]; i++)
486 if (!this_index[i]->filename)
487 this_index[i]->filename = strdup (this_fb->filename);
489 /* Concatenate with the other indices. */
490 all_indices = info_concatenate_references (all_indices, this_index);
494 info_free_references (dir_menu);
496 /* Build a list of the references which contain SEARCH_STRING. */
497 if (all_indices)
499 REFERENCE *entry, **apropos_list = (REFERENCE **)NULL;
500 int apropos_list_index = 0;
501 int apropos_list_slots = 0;
503 for (i = 0; (entry = all_indices[i]); i++)
505 if (string_in_line (search_string, entry->label) != -1)
507 add_pointer_to_array
508 (entry, apropos_list_index, apropos_list, apropos_list_slots,
509 100, REFERENCE *);
511 else
513 maybe_free (entry->label);
514 maybe_free (entry->filename);
515 maybe_free (entry->nodename);
516 free (entry);
520 free (all_indices);
521 all_indices = apropos_list;
523 return (all_indices);
526 #define APROPOS_NONE \
527 "No available info files reference \"%s\" in their indices."
529 void
530 info_apropos (string)
531 char *string;
533 REFERENCE **apropos_list;
535 apropos_list = apropos_in_all_indices (string, 0);
537 if (!apropos_list)
539 info_error (APROPOS_NONE, string);
541 else
543 register int i;
544 REFERENCE *entry;
546 for (i = 0; (entry = apropos_list[i]); i++)
547 fprintf (stderr, "\"(%s)%s\" -- %s\n",
548 entry->filename, entry->nodename, entry->label);
550 info_free_references (apropos_list);
553 static char *apropos_list_nodename = "*Apropos*";
555 DECLARE_INFO_COMMAND (info_index_apropos,
556 "Grovel all known info file's indices for a string and build a menu")
558 char *line;
560 line = info_read_in_echo_area (window, "Index apropos: ");
562 window = active_window;
564 /* User aborted? */
565 if (!line)
567 info_abort_key (window, 1, 1);
568 return;
571 /* User typed something? */
572 if (*line)
574 REFERENCE **apropos_list;
575 NODE *apropos_node;
577 apropos_list = apropos_in_all_indices (line, 1);
579 if (!apropos_list)
581 info_error (APROPOS_NONE, line);
583 else
585 register int i;
586 char *line_buffer;
588 initialize_message_buffer ();
589 printf_to_message_buffer
590 ("\n* Menu: Nodes whoses indices contain \"%s\":\n", line);
591 line_buffer = (char *)xmalloc (500);
593 for (i = 0; apropos_list[i]; i++)
595 int len;
596 sprintf (line_buffer, "* (%s)%s::",
597 apropos_list[i]->filename, apropos_list[i]->nodename);
598 len = pad_to (36, line_buffer);
599 sprintf (line_buffer + len, "%s", apropos_list[i]->label);
600 printf_to_message_buffer ("%s\n", line_buffer);
602 free (line_buffer);
605 apropos_node = message_buffer_to_node ();
606 add_gcable_pointer (apropos_node->contents);
607 name_internal_node (apropos_node, apropos_list_nodename);
609 /* Even though this is an internal node, we don't want the window
610 system to treat it specially. So we turn off the internalness
611 of it here. */
612 apropos_node->flags &= ~N_IsInternal;
614 /* Find/Create a window to contain this node. */
616 WINDOW *new;
617 NODE *node;
619 set_remembered_pagetop_and_point (window);
621 /* If a window is visible and showing an apropos list already,
622 re-use it. */
623 for (new = windows; new; new = new->next)
625 node = new->node;
627 if (internal_info_node_p (node) &&
628 (strcmp (node->nodename, apropos_list_nodename) == 0))
629 break;
632 /* If we couldn't find an existing window, try to use the next window
633 in the chain. */
634 if (!new && window->next)
635 new = window->next;
637 /* If we still don't have a window, make a new one to contain
638 the list. */
639 if (!new)
641 WINDOW *old_active;
643 old_active = active_window;
644 active_window = window;
645 new = window_make_window ((NODE *)NULL);
646 active_window = old_active;
649 /* If we couldn't make a new window, use this one. */
650 if (!new)
651 new = window;
653 /* Lines do not wrap in this window. */
654 new->flags |= W_NoWrap;
656 window_set_node_of_window (new, apropos_node);
657 remember_window_and_node (new, apropos_node);
658 active_window = new;
660 info_free_references (apropos_list);
662 free (line);
664 if (!info_error_was_printed)
665 window_clear_echo_area ();