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