| File: | src/gnu/usr.bin/texinfo/info/indices.c |
| Warning: | line 375, column 7 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
| |||
| 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
| |||
| 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
| |||
| 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
| |||
| 374 | { | |||
| 375 | info_error ((char *) _("No %sindex entries containing `%s'.")((const char *) ("No %sindex entries containing `%s'.")), | |||
| ||||
| 376 | index_offset
| |||
| 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 | } |