1 /* nodes.c -- how to get an Info file and node. 2 $Id: nodes.c,v 1.1.1.5 2006/07/17 16:03:43 espie Exp $ 3 4 Copyright (C) 1993, 1998, 1999, 2000, 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 25 #include "nodes.h" 26 #include "search.h" 27 #include "filesys.h" 28 #include "info-utils.h" 29 30 #if defined (HANDLE_MAN_PAGES) 31 # include "man.h" 32 #endif /* HANDLE_MAN_PAGES */ 33 34 static void forget_info_file (char *filename); 35 static void remember_info_file (FILE_BUFFER *file_buffer); 36 static void free_file_buffer_tags (FILE_BUFFER *file_buffer); 37 static void free_info_tag (TAG *tag); 38 static void get_nodes_of_tags_table (FILE_BUFFER *file_buffer, 39 SEARCH_BINDING *buffer_binding); 40 static void get_nodes_of_info_file (FILE_BUFFER *file_buffer); 41 static void get_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer, 42 SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding); 43 static void info_reload_file_buffer_contents (FILE_BUFFER *fb); 44 static char *adjust_nodestart (NODE *node, int min, int max); 45 static FILE_BUFFER *info_load_file_internal (char *filename, int get_tags); 46 static FILE_BUFFER *info_find_file_internal (char *filename, int get_tags); 47 static NODE *info_node_of_file_buffer_tags (FILE_BUFFER *file_buffer, 48 char *nodename); 49 50 static long get_node_length (SEARCH_BINDING *binding); 51 52 /* Magic number that RMS used to decide how much a tags table pointer could 53 be off by. I feel that it should be much smaller, like 4. */ 54 #define DEFAULT_INFO_FUDGE 1000 55 56 /* Passed to *_internal functions. INFO_GET_TAGS says to do what is 57 neccessary to fill in the nodes or tags arrays in FILE_BUFFER. */ 58 #define INFO_NO_TAGS 0 59 #define INFO_GET_TAGS 1 60 61 /* Global variables. */ 62 63 /* When non-zero, this is a string describing the recent file error. */ 64 char *info_recent_file_error = NULL; 65 66 /* The list of already loaded nodes. */ 67 FILE_BUFFER **info_loaded_files = NULL; 68 69 /* The number of slots currently allocated to LOADED_FILES. */ 70 int info_loaded_files_slots = 0; 71 72 /* Public functions for node manipulation. */ 73 74 /* Used to build `dir' menu from `localdir' files found in INFOPATH. */ 75 extern void maybe_build_dir_node (char *dirname); 76 77 /* Return a pointer to a NODE structure for the Info node (FILENAME)NODENAME. 78 If FILENAME is NULL, `dir' is used. 79 IF NODENAME is NULL, `Top' is used. 80 If the node cannot be found, return NULL. */ 81 NODE * 82 info_get_node (char *filename, char *nodename) 83 { 84 NODE *node; 85 FILE_BUFFER *file_buffer = NULL; 86 87 info_recent_file_error = NULL; 88 info_parse_node (nodename, DONT_SKIP_NEWLINES); 89 nodename = NULL; 90 91 if (info_parsed_filename) 92 filename = info_parsed_filename; 93 94 if (info_parsed_nodename) 95 nodename = info_parsed_nodename; 96 97 /* If FILENAME is not specified, it defaults to "dir". */ 98 if (!filename) 99 filename = "dir"; 100 101 /* If the file to be looked up is "dir", build the contents from all of 102 the "dir"s and "localdir"s found in INFOPATH. */ 103 if (is_dir_name (filename)) 104 maybe_build_dir_node (filename); 105 106 /* Find the correct info file, or give up. */ 107 file_buffer = info_find_file (filename); 108 if (!file_buffer) 109 { 110 if (filesys_error_number) 111 info_recent_file_error = 112 filesys_error_string (filename, filesys_error_number); 113 return NULL; 114 } 115 116 /* Look for the node. */ 117 node = info_get_node_of_file_buffer (nodename, file_buffer); 118 119 /* If the node not found was "Top", try again with different case. */ 120 if (!node && (nodename == NULL || strcasecmp (nodename, "Top") == 0)) 121 { 122 node = info_get_node_of_file_buffer ("Top", file_buffer); 123 if (!node) 124 node = info_get_node_of_file_buffer ("top", file_buffer); 125 if (!node) 126 node = info_get_node_of_file_buffer ("TOP", file_buffer); 127 } 128 129 return node; 130 } 131 132 /* Return a pointer to a NODE structure for the Info node NODENAME in 133 FILE_BUFFER. NODENAME can be passed as NULL, in which case the 134 nodename of "Top" is used. If the node cannot be found, return a 135 NULL pointer. */ 136 NODE * 137 info_get_node_of_file_buffer (char *nodename, FILE_BUFFER *file_buffer) 138 { 139 NODE *node = NULL; 140 141 /* If we are unable to find the file, we have to give up. There isn't 142 anything else we can do. */ 143 if (!file_buffer) 144 return NULL; 145 146 /* If the file buffer was gc'ed, reload the contents now. */ 147 if (!file_buffer->contents) 148 info_reload_file_buffer_contents (file_buffer); 149 150 /* If NODENAME is not specified, it defaults to "Top". */ 151 if (!nodename) 152 nodename = "Top"; 153 154 /* If the name of the node that we wish to find is exactly "*", then the 155 node body is the contents of the entire file. Create and return such 156 a node. */ 157 if (strcmp (nodename, "*") == 0) 158 { 159 node = (NODE *)xmalloc (sizeof (NODE)); 160 node->filename = file_buffer->fullpath; 161 node->parent = NULL; 162 node->nodename = xstrdup ("*"); 163 node->contents = file_buffer->contents; 164 node->nodelen = file_buffer->filesize; 165 node->flags = 0; 166 node->display_pos = 0; 167 } 168 #if defined (HANDLE_MAN_PAGES) 169 /* If the file buffer is the magic one associated with manpages, call 170 the manpage node finding function instead. */ 171 else if (file_buffer->flags & N_IsManPage) 172 { 173 node = get_manpage_node (file_buffer, nodename); 174 } 175 #endif /* HANDLE_MAN_PAGES */ 176 /* If this is the "main" info file, it might contain a tags table. Search 177 the tags table for an entry which matches the node that we want. If 178 there is a tags table, get the file which contains this node, but don't 179 bother building a node list for it. */ 180 else if (file_buffer->tags) 181 { 182 node = info_node_of_file_buffer_tags (file_buffer, nodename); 183 } 184 185 /* Return the results of our node search. */ 186 return node; 187 } 188 189 /* Locate the file named by FILENAME, and return the information structure 190 describing this file. The file may appear in our list of loaded files 191 already, or it may not. If it does not already appear, find the file, 192 and add it to the list of loaded files. If the file cannot be found, 193 return a NULL FILE_BUFFER *. */ 194 FILE_BUFFER * 195 info_find_file (char *filename) 196 { 197 return info_find_file_internal (filename, INFO_GET_TAGS); 198 } 199 200 /* Load the info file FILENAME, remembering information about it in a 201 file buffer. */ 202 FILE_BUFFER * 203 info_load_file (char *filename) 204 { 205 return info_load_file_internal (filename, INFO_GET_TAGS); 206 } 207 208 209 /* Private functions implementation. */ 210 211 /* The workhorse for info_find_file (). Non-zero 2nd argument says to 212 try to build a tags table (or otherwise glean the nodes) for this 213 file once found. By default, we build the tags table, but when this 214 function is called by info_get_node () when we already have a valid 215 tags table describing the nodes, it is unnecessary. */ 216 static FILE_BUFFER * 217 info_find_file_internal (char *filename, int get_tags) 218 { 219 int i; 220 FILE_BUFFER *file_buffer; 221 222 /* First try to find the file in our list of already loaded files. */ 223 if (info_loaded_files) 224 { 225 for (i = 0; (file_buffer = info_loaded_files[i]); i++) 226 if ((FILENAME_CMP (filename, file_buffer->filename) == 0) 227 || (FILENAME_CMP (filename, file_buffer->fullpath) == 0) 228 || (!IS_ABSOLUTE (filename) 229 && FILENAME_CMP (filename, 230 filename_non_directory (file_buffer->fullpath)) 231 == 0)) 232 { 233 struct stat new_info, *old_info; 234 235 /* This file is loaded. If the filename that we want is 236 specifically "dir", then simply return the file buffer. */ 237 if (is_dir_name (filename_non_directory (filename))) 238 return file_buffer; 239 240 #if defined (HANDLE_MAN_PAGES) 241 /* Do the same for the magic MANPAGE file. */ 242 if (file_buffer->flags & N_IsManPage) 243 return file_buffer; 244 #endif /* HANDLE_MAN_PAGES */ 245 246 /* The file appears to be already loaded, and is not "dir". Check 247 to see if it's changed since the last time it was loaded. */ 248 if (stat (file_buffer->fullpath, &new_info) == -1) 249 { 250 filesys_error_number = errno; 251 return NULL; 252 } 253 254 old_info = &file_buffer->finfo; 255 256 if (new_info.st_size != old_info->st_size 257 || new_info.st_mtime != old_info->st_mtime) 258 { 259 /* The file has changed. Forget that we ever had loaded it 260 in the first place. */ 261 forget_info_file (filename); 262 break; 263 } 264 else 265 { 266 /* The info file exists, and has not changed since the last 267 time it was loaded. If the caller requested a nodes list 268 for this file, and there isn't one here, build the nodes 269 for this file_buffer. In any case, return the file_buffer 270 object. */ 271 if (!file_buffer->contents) 272 { 273 /* The file's contents have been gc'ed. Reload it. */ 274 info_reload_file_buffer_contents (file_buffer); 275 if (!file_buffer->contents) 276 return NULL; 277 } 278 279 if (get_tags && !file_buffer->tags) 280 build_tags_and_nodes (file_buffer); 281 282 return file_buffer; 283 } 284 } 285 } 286 287 /* The file wasn't loaded. Try to load it now. */ 288 #if defined (HANDLE_MAN_PAGES) 289 /* If the name of the file that we want is our special file buffer for 290 Unix manual pages, then create the file buffer, and return it now. */ 291 if (strcasecmp (filename, MANPAGE_FILE_BUFFER_NAME) == 0) 292 file_buffer = create_manpage_file_buffer (); 293 else 294 #endif /* HANDLE_MAN_PAGES */ 295 file_buffer = info_load_file_internal (filename, get_tags); 296 297 /* If the file was loaded, remember the name under which it was found. */ 298 if (file_buffer) 299 remember_info_file (file_buffer); 300 301 return file_buffer; 302 } 303 304 /* The workhorse function for info_load_file (). Non-zero second argument 305 says to build a list of tags (or nodes) for this file. This is the 306 default behaviour when info_load_file () is called, but it is not 307 necessary when loading a subfile for which we already have tags. */ 308 static FILE_BUFFER * 309 info_load_file_internal (char *filename, int get_tags) 310 { 311 char *fullpath, *contents; 312 long filesize; 313 struct stat finfo; 314 int retcode, compressed; 315 FILE_BUFFER *file_buffer = NULL; 316 317 /* Get the full pathname of this file, as known by the info system. 318 That is to say, search along INFOPATH and expand tildes, etc. */ 319 fullpath = info_find_fullpath (filename); 320 321 /* Did we actually find the file? */ 322 retcode = stat (fullpath, &finfo); 323 324 /* If the file referenced by the name returned from info_find_fullpath () 325 doesn't exist, then try again with the last part of the filename 326 appearing in lowercase. */ 327 /* This is probably not needed at all on those systems which define 328 FILENAME_CMP to be strcasecmp. But let's do it anyway, lest some 329 network redirector supports case sensitivity. */ 330 if (retcode < 0) 331 { 332 char *lowered_name; 333 char *tmp_basename; 334 335 lowered_name = xstrdup (filename); 336 tmp_basename = filename_non_directory (lowered_name); 337 338 while (*tmp_basename) 339 { 340 if (isupper (*tmp_basename)) 341 *tmp_basename = tolower (*tmp_basename); 342 343 tmp_basename++; 344 } 345 346 fullpath = info_find_fullpath (lowered_name); 347 348 retcode = stat (fullpath, &finfo); 349 free (lowered_name); 350 } 351 352 /* If the file wasn't found, give up, returning a NULL pointer. */ 353 if (retcode < 0) 354 { 355 filesys_error_number = errno; 356 return NULL; 357 } 358 359 /* Otherwise, try to load the file. */ 360 contents = filesys_read_info_file (fullpath, &filesize, &finfo, &compressed); 361 362 if (!contents) 363 return NULL; 364 365 /* The file was found, and can be read. Allocate FILE_BUFFER and fill 366 in the various members. */ 367 file_buffer = make_file_buffer (); 368 file_buffer->filename = xstrdup (filename); 369 file_buffer->fullpath = xstrdup (fullpath); 370 file_buffer->finfo = finfo; 371 file_buffer->filesize = filesize; 372 file_buffer->contents = contents; 373 if (compressed) 374 file_buffer->flags |= N_IsCompressed; 375 376 /* If requested, build the tags and nodes for this file buffer. */ 377 if (get_tags) 378 build_tags_and_nodes (file_buffer); 379 380 return file_buffer; 381 } 382 383 /* Grovel FILE_BUFFER->contents finding tags and nodes, and filling in the 384 various slots. This can also be used to rebuild a tag or node table. */ 385 void 386 build_tags_and_nodes (FILE_BUFFER *file_buffer) 387 { 388 SEARCH_BINDING binding; 389 long position; 390 391 free_file_buffer_tags (file_buffer); 392 file_buffer->flags &= ~N_HasTagsTable; 393 394 /* See if there is a tags table in this info file. */ 395 binding.buffer = file_buffer->contents; 396 binding.start = file_buffer->filesize; 397 binding.end = binding.start - 1000; 398 if (binding.end < 0) 399 binding.end = 0; 400 binding.flags = S_FoldCase; 401 402 position = search_backward (TAGS_TABLE_END_LABEL, &binding); 403 404 /* If there is a tag table, find the start of it, and grovel over it 405 extracting tag information. */ 406 if (position != -1) 407 while (1) 408 { 409 long tags_table_begin, tags_table_end; 410 411 binding.end = position; 412 binding.start = binding.end - 5 - strlen (TAGS_TABLE_END_LABEL); 413 if (binding.start < 0) 414 binding.start = 0; 415 416 position = find_node_separator (&binding); 417 418 /* For this test, (and all others here) failure indicates a bogus 419 tags table. Grovel the file. */ 420 if (position == -1) 421 break; 422 423 /* Remember the end of the tags table. */ 424 binding.start = position; 425 tags_table_end = binding.start; 426 binding.end = 0; 427 428 /* Locate the start of the tags table. */ 429 position = search_backward (TAGS_TABLE_BEG_LABEL, &binding); 430 431 if (position == -1) 432 break; 433 434 binding.end = position; 435 binding.start = binding.end - 5 - strlen (TAGS_TABLE_BEG_LABEL); 436 position = find_node_separator (&binding); 437 438 if (position == -1) 439 break; 440 441 /* The file contains a valid tags table. Fill the FILE_BUFFER's 442 tags member. */ 443 file_buffer->flags |= N_HasTagsTable; 444 tags_table_begin = position; 445 446 /* If this isn't an indirect tags table, just remember the nodes 447 described locally in this tags table. Note that binding.end 448 is pointing to just after the beginning label. */ 449 binding.start = binding.end; 450 binding.end = file_buffer->filesize; 451 452 if (!looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, &binding)) 453 { 454 binding.start = tags_table_begin; 455 binding.end = tags_table_end; 456 get_nodes_of_tags_table (file_buffer, &binding); 457 return; 458 } 459 else 460 { 461 /* This is an indirect tags table. Build TAGS member. */ 462 SEARCH_BINDING indirect; 463 464 indirect.start = tags_table_begin; 465 indirect.end = 0; 466 indirect.buffer = binding.buffer; 467 indirect.flags = S_FoldCase; 468 469 position = search_backward (INDIRECT_TAGS_TABLE_LABEL, &indirect); 470 471 if (position == -1) 472 { 473 /* This file is malformed. Give up. */ 474 return; 475 } 476 477 indirect.start = position; 478 indirect.end = tags_table_begin; 479 binding.start = tags_table_begin; 480 binding.end = tags_table_end; 481 get_tags_of_indirect_tags_table (file_buffer, &indirect, &binding); 482 return; 483 } 484 } 485 486 /* This file doesn't contain any kind of tags table. Grovel the 487 file and build node entries for it. */ 488 get_nodes_of_info_file (file_buffer); 489 } 490 491 /* Search through FILE_BUFFER->contents building an array of TAG *, 492 one entry per each node present in the file. Store the tags in 493 FILE_BUFFER->tags, and the number of allocated slots in 494 FILE_BUFFER->tags_slots. */ 495 static void 496 get_nodes_of_info_file (FILE_BUFFER *file_buffer) 497 { 498 long nodestart; 499 int tags_index = 0; 500 SEARCH_BINDING binding; 501 502 binding.buffer = file_buffer->contents; 503 binding.start = 0; 504 binding.end = file_buffer->filesize; 505 binding.flags = S_FoldCase; 506 507 while ((nodestart = find_node_separator (&binding)) != -1) 508 { 509 int start, end; 510 char *nodeline; 511 TAG *entry; 512 int anchor = 0; 513 514 /* Skip past the characters just found. */ 515 binding.start = nodestart; 516 binding.start += skip_node_separator (binding.buffer + binding.start); 517 518 /* Move to the start of the line defining the node. */ 519 nodeline = binding.buffer + binding.start; 520 521 /* Find "Node:" */ 522 start = string_in_line (INFO_NODE_LABEL, nodeline); 523 /* No Node:. Maybe it's a Ref:. */ 524 if (start == -1) 525 { 526 start = string_in_line (INFO_REF_LABEL, nodeline); 527 if (start != -1) 528 anchor = 1; 529 } 530 531 /* If not there, this is not the start of a node. */ 532 if (start == -1) 533 continue; 534 535 /* Find the start of the nodename. */ 536 start += skip_whitespace (nodeline + start); 537 538 /* Find the end of the nodename. */ 539 end = start + 540 skip_node_characters (nodeline + start, DONT_SKIP_NEWLINES); 541 542 /* Okay, we have isolated the node name, and we know where the 543 node starts. Remember this information. */ 544 entry = xmalloc (sizeof (TAG)); 545 entry->nodename = xmalloc (1 + (end - start)); 546 strncpy (entry->nodename, nodeline + start, end - start); 547 entry->nodename[end - start] = 0; 548 entry->nodestart = nodestart; 549 if (anchor) 550 entry->nodelen = 0; 551 else 552 { 553 SEARCH_BINDING node_body; 554 555 node_body.buffer = binding.buffer + binding.start; 556 node_body.start = 0; 557 node_body.end = binding.end - binding.start; 558 node_body.flags = S_FoldCase; 559 entry->nodelen = get_node_length (&node_body); 560 } 561 562 entry->filename = file_buffer->fullpath; 563 564 /* Add this tag to the array of tag structures in this FILE_BUFFER. */ 565 add_pointer_to_array (entry, tags_index, file_buffer->tags, 566 file_buffer->tags_slots, 100, TAG *); 567 } 568 } 569 570 /* Return the length of the node which starts at BINDING. */ 571 static long 572 get_node_length (SEARCH_BINDING *binding) 573 { 574 int i; 575 char *body; 576 577 /* [A node] ends with either a ^_, a ^L, or end of file. */ 578 for (i = binding->start, body = binding->buffer; i < binding->end; i++) 579 { 580 if (body[i] == INFO_FF || body[i] == INFO_COOKIE) 581 break; 582 } 583 return i - binding->start; 584 } 585 586 /* Build and save the array of nodes in FILE_BUFFER by searching through the 587 contents of BUFFER_BINDING for a tags table, and groveling the contents. */ 588 static void 589 get_nodes_of_tags_table (FILE_BUFFER *file_buffer, 590 SEARCH_BINDING *buffer_binding) 591 { 592 int name_offset; 593 SEARCH_BINDING *tmp_search; 594 long position; 595 int tags_index = 0; 596 597 tmp_search = copy_binding (buffer_binding); 598 599 /* Find the start of the tags table. */ 600 position = find_tags_table (tmp_search); 601 602 /* If none, we're all done. */ 603 if (position == -1) 604 return; 605 606 /* Move to one character before the start of the actual table. */ 607 tmp_search->start = position; 608 tmp_search->start += skip_node_separator 609 (tmp_search->buffer + tmp_search->start); 610 tmp_search->start += strlen (TAGS_TABLE_BEG_LABEL); 611 tmp_search->start--; 612 613 /* The tag table consists of lines containing node names and positions. 614 Do each line until we find one that doesn't contain a node name. */ 615 while ((position = search_forward ("\n", tmp_search)) != -1) 616 { 617 TAG *entry; 618 char *nodedef; 619 unsigned p; 620 int anchor = 0; 621 622 /* Prepare to skip this line. */ 623 tmp_search->start = position; 624 tmp_search->start++; 625 626 /* Skip past informative "(Indirect)" tags table line. */ 627 if (!tags_index && looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, tmp_search)) 628 continue; 629 630 /* Find the label preceding the node name. */ 631 name_offset = 632 string_in_line (INFO_NODE_LABEL, tmp_search->buffer + tmp_search->start); 633 634 /* If no node label, maybe it's an anchor. */ 635 if (name_offset == -1) 636 { 637 name_offset = string_in_line (INFO_REF_LABEL, 638 tmp_search->buffer + tmp_search->start); 639 if (name_offset != -1) 640 anchor = 1; 641 } 642 643 /* If not there, not a defining line, so we must be out of the 644 tags table. */ 645 if (name_offset == -1) 646 break; 647 648 entry = xmalloc (sizeof (TAG)); 649 650 /* Find the beginning of the node definition. */ 651 tmp_search->start += name_offset; 652 nodedef = tmp_search->buffer + tmp_search->start; 653 nodedef += skip_whitespace (nodedef); 654 655 /* Move past the node's name in this tag to the TAGSEP character. */ 656 for (p = 0; nodedef[p] && nodedef[p] != INFO_TAGSEP; p++) 657 ; 658 if (nodedef[p] != INFO_TAGSEP) 659 continue; 660 661 entry->nodename = xmalloc (p + 1); 662 strncpy (entry->nodename, nodedef, p); 663 entry->nodename[p] = 0; 664 p++; 665 entry->nodestart = atol (nodedef + p); 666 667 /* If a node, we don't know the length yet, but if it's an 668 anchor, the length is 0. */ 669 entry->nodelen = anchor ? 0 : -1; 670 671 /* The filename of this node is currently known as the same as the 672 name of this file. */ 673 entry->filename = file_buffer->fullpath; 674 675 /* Add this node structure to the array of node structures in this 676 FILE_BUFFER. */ 677 add_pointer_to_array (entry, tags_index, file_buffer->tags, 678 file_buffer->tags_slots, 100, TAG *); 679 } 680 free (tmp_search); 681 } 682 683 /* A structure used only in `get_tags_of_indirect_tags_table' to hold onto 684 an intermediate value. */ 685 typedef struct { 686 char *filename; 687 long first_byte; 688 } SUBFILE; 689 690 /* Remember in FILE_BUFFER the nodenames, subfilenames, and offsets within the 691 subfiles of every node which appears in TAGS_BINDING. The 2nd argument is 692 a binding surrounding the indirect files list. */ 693 static void 694 get_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer, 695 SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding) 696 { 697 int i; 698 SUBFILE **subfiles = NULL; 699 int subfiles_index = 0, subfiles_slots = 0; 700 TAG *entry; 701 702 /* First get the list of tags from the tags table. Then lookup the 703 associated file in the indirect list for each tag, and update it. */ 704 get_nodes_of_tags_table (file_buffer, tags_binding); 705 706 /* We have the list of tags in file_buffer->tags. Get the list of 707 subfiles from the indirect table. */ 708 { 709 char *start, *end, *line; 710 SUBFILE *subfile; 711 712 start = indirect_binding->buffer + indirect_binding->start; 713 end = indirect_binding->buffer + indirect_binding->end; 714 line = start; 715 716 while (line < end) 717 { 718 int colon; 719 720 colon = string_in_line (":", line); 721 722 if (colon == -1) 723 break; 724 725 subfile = (SUBFILE *)xmalloc (sizeof (SUBFILE)); 726 subfile->filename = (char *)xmalloc (colon); 727 strncpy (subfile->filename, line, colon - 1); 728 subfile->filename[colon - 1] = 0; 729 subfile->first_byte = (long) atol (line + colon); 730 731 add_pointer_to_array 732 (subfile, subfiles_index, subfiles, subfiles_slots, 10, SUBFILE *); 733 734 while (*line++ != '\n'); 735 } 736 } 737 738 /* If we have successfully built the indirect files table, then 739 merge the information in the two tables. */ 740 if (!subfiles) 741 { 742 free_file_buffer_tags (file_buffer); 743 return; 744 } 745 else 746 { 747 int tags_index; 748 long header_length; 749 SEARCH_BINDING binding; 750 751 /* Find the length of the header of the file containing the indirect 752 tags table. This header appears at the start of every file. We 753 want the absolute position of each node within each subfile, so 754 we subtract the start of the containing subfile from the logical 755 position of the node, and then add the length of the header in. */ 756 binding.buffer = file_buffer->contents; 757 binding.start = 0; 758 binding.end = file_buffer->filesize; 759 binding.flags = S_FoldCase; 760 761 header_length = find_node_separator (&binding); 762 if (header_length == -1) 763 header_length = 0; 764 765 /* Build the file buffer's list of subfiles. */ 766 { 767 char *containing_dir = xstrdup (file_buffer->fullpath); 768 char *temp = filename_non_directory (containing_dir); 769 int len_containing_dir; 770 771 if (temp > containing_dir) 772 { 773 if (HAVE_DRIVE (file_buffer->fullpath) && 774 temp == containing_dir + 2) 775 { 776 /* Avoid converting "d:foo" into "d:/foo" below. */ 777 *temp = '.'; 778 temp += 2; 779 } 780 temp[-1] = 0; 781 } 782 783 len_containing_dir = strlen (containing_dir); 784 785 for (i = 0; subfiles[i]; i++); 786 787 file_buffer->subfiles = (char **) xmalloc ((1 + i) * sizeof (char *)); 788 789 for (i = 0; subfiles[i]; i++) 790 { 791 char *fullpath; 792 793 fullpath = (char *) xmalloc 794 (2 + strlen (subfiles[i]->filename) + len_containing_dir); 795 796 sprintf (fullpath, "%s/%s", 797 containing_dir, subfiles[i]->filename); 798 799 file_buffer->subfiles[i] = fullpath; 800 } 801 file_buffer->subfiles[i] = NULL; 802 free (containing_dir); 803 } 804 805 /* For each node in the file's tags table, remember the starting 806 position. */ 807 for (tags_index = 0; (entry = file_buffer->tags[tags_index]); 808 tags_index++) 809 { 810 for (i = 0; 811 subfiles[i] && entry->nodestart >= subfiles[i]->first_byte; 812 i++); 813 814 /* If the Info file containing the indirect tags table is 815 malformed, then give up. */ 816 if (!i) 817 { 818 /* The Info file containing the indirect tags table is 819 malformed. Give up. */ 820 for (i = 0; subfiles[i]; i++) 821 { 822 free (subfiles[i]->filename); 823 free (subfiles[i]); 824 free (file_buffer->subfiles[i]); 825 } 826 file_buffer->subfiles = NULL; 827 free_file_buffer_tags (file_buffer); 828 return; 829 } 830 831 /* SUBFILES[i] is the index of the first subfile whose logical 832 first byte is greater than the logical offset of this node's 833 starting position. This means that the subfile directly 834 preceding this one is the one containing the node. */ 835 836 entry->filename = file_buffer->subfiles[i - 1]; 837 entry->nodestart -= subfiles[i - 1]->first_byte; 838 entry->nodestart += header_length; 839 } 840 841 /* We have successfully built the tags table. Remember that it 842 was indirect. */ 843 file_buffer->flags |= N_TagsIndirect; 844 } 845 846 /* Free the structures assigned to SUBFILES. Free the names as well 847 as the structures themselves, then finally, the array. */ 848 for (i = 0; subfiles[i]; i++) 849 { 850 free (subfiles[i]->filename); 851 free (subfiles[i]); 852 } 853 free (subfiles); 854 } 855 856 857 /* Return the node that contains TAG in FILE_BUFFER, else 858 (pathologically) NULL. Called from info_node_of_file_buffer_tags. */ 859 static NODE * 860 find_node_of_anchor (FILE_BUFFER *file_buffer, TAG *tag) 861 { 862 int anchor_pos, node_pos; 863 TAG *node_tag; 864 NODE *node; 865 866 /* Look through the tag list for the anchor. */ 867 for (anchor_pos = 0; file_buffer->tags[anchor_pos]; anchor_pos++) 868 { 869 TAG *t = file_buffer->tags[anchor_pos]; 870 if (t->nodestart == tag->nodestart) 871 break; 872 } 873 874 /* Should not happen, because we should always find the anchor. */ 875 if (!file_buffer->tags[anchor_pos]) 876 return NULL; 877 878 /* We've found the anchor. Look backwards in the tag table for the 879 preceding node (we're assuming the tags are given in order), 880 skipping over any preceding anchors. */ 881 for (node_pos = anchor_pos - 1; 882 node_pos >= 0 && file_buffer->tags[node_pos]->nodelen == 0; 883 node_pos--) 884 ; 885 886 /* An info file with an anchor before any nodes is pathological, but 887 it's possible, so don't crash. */ 888 if (node_pos < 0) 889 return NULL; 890 891 /* We have the tag for the node that contained the anchor tag. */ 892 node_tag = file_buffer->tags[node_pos]; 893 894 /* Look up the node name in the tag table to get the actual node. 895 This is a recursive call, but it can't recurse again, because we 896 call it with a real node. */ 897 node = info_node_of_file_buffer_tags (file_buffer, node_tag->nodename); 898 899 /* Start displaying the node at the anchor position. */ 900 if (node) 901 { /* The nodestart for real nodes is three characters before the `F' 902 in the `File:' line (a newline, the CTRL-_, and another 903 newline). The nodestart for anchors is the actual position. 904 But we offset by only 2, rather than 3, because if an anchor is 905 at the beginning of a paragraph, it's nicer for it to end up on 906 the beginning of the first line of the paragraph rather than 907 the blank line before it. (makeinfo has no way of knowing that 908 a paragraph is going to start, so we can't fix it there.) */ 909 node->display_pos = file_buffer->tags[anchor_pos]->nodestart 910 - (node_tag->nodestart + 2); 911 912 /* Otherwise an anchor at the end of a node ends up displaying at 913 the end of the last line of the node (way over on the right of 914 the screen), which looks wrong. */ 915 if (node->display_pos >= (unsigned long) node->nodelen) 916 node->display_pos = node->nodelen - 1; 917 918 /* Don't search in the node for the xref text, it's not there. */ 919 node->flags |= N_FromAnchor; 920 } 921 922 return node; 923 } 924 925 926 /* Return the node from FILE_BUFFER which matches NODENAME by searching 927 the tags table in FILE_BUFFER, or NULL. */ 928 static NODE * 929 info_node_of_file_buffer_tags (FILE_BUFFER *file_buffer, char *nodename) 930 { 931 TAG *tag; 932 int i; 933 934 /* If no tags at all (possibly a misformatted info file), quit. */ 935 if (!file_buffer->tags) { 936 return NULL; 937 } 938 939 for (i = 0; (tag = file_buffer->tags[i]); i++) 940 if (strcmp (nodename, tag->nodename) == 0) 941 { 942 FILE_BUFFER *subfile = info_find_file_internal (tag->filename, 943 INFO_NO_TAGS); 944 if (!subfile) 945 return NULL; 946 947 if (!subfile->contents) 948 { 949 info_reload_file_buffer_contents (subfile); 950 if (!subfile->contents) 951 return NULL; 952 } 953 954 /* If we were able to find this file and load it, then return 955 the node within it. */ 956 { 957 NODE *node = xmalloc (sizeof (NODE)); 958 node->filename = subfile->fullpath; 959 node->parent = NULL; 960 node->nodename = tag->nodename; 961 node->contents = subfile->contents + tag->nodestart; 962 node->display_pos = 0; 963 node->flags = 0; 964 965 if (file_buffer->flags & N_HasTagsTable) 966 { 967 node->flags |= N_HasTagsTable; 968 969 if (file_buffer->flags & N_TagsIndirect) 970 { 971 node->flags |= N_TagsIndirect; 972 node->parent = file_buffer->fullpath; 973 } 974 } 975 976 if (subfile->flags & N_IsCompressed) 977 node->flags |= N_IsCompressed; 978 979 /* If TAG->nodelen hasn't been calculated yet, then we aren't 980 in a position to trust the entry pointer. Adjust things so 981 that ENTRY->nodestart gets the exact address of the start of 982 the node separator which starts this node, and NODE->contents 983 gets the address of the line defining this node. If we cannot 984 do that, the node isn't really here. */ 985 if (tag->nodelen == -1) 986 { 987 int min, max; 988 char *node_sep; 989 SEARCH_BINDING node_body; 990 char *buff_end; 991 992 min = max = DEFAULT_INFO_FUDGE; 993 994 if (tag->nodestart < DEFAULT_INFO_FUDGE) 995 min = tag->nodestart; 996 997 if (DEFAULT_INFO_FUDGE > 998 (subfile->filesize - tag->nodestart)) 999 max = subfile->filesize - tag->nodestart; 1000 1001 /* NODE_SEP gets the address of the separator which defines 1002 this node, or NULL if the node wasn't found. 1003 NODE->contents is side-effected to point to right after 1004 the separator. */ 1005 node_sep = adjust_nodestart (node, min, max); 1006 if (node_sep == NULL) 1007 { 1008 free (node); 1009 return NULL; 1010 } 1011 /* Readjust tag->nodestart. */ 1012 tag->nodestart = node_sep - subfile->contents; 1013 1014 /* Calculate the length of the current node. */ 1015 buff_end = subfile->contents + subfile->filesize; 1016 1017 node_body.buffer = node->contents; 1018 node_body.start = 0; 1019 node_body.end = buff_end - node_body.buffer; 1020 node_body.flags = 0; 1021 tag->nodelen = get_node_length (&node_body); 1022 node->nodelen = tag->nodelen; 1023 } 1024 1025 else if (tag->nodelen == 0) /* anchor, return containing node */ 1026 { 1027 free (node); 1028 node = find_node_of_anchor (file_buffer, tag); 1029 } 1030 1031 else 1032 { 1033 /* Since we know the length of this node, we have already 1034 adjusted tag->nodestart to point to the exact start of 1035 it. Simply skip the node separator. */ 1036 node->contents += skip_node_separator (node->contents); 1037 node->nodelen = tag->nodelen; 1038 } 1039 1040 return node; 1041 } 1042 } 1043 1044 /* There was a tag table for this file, and the node wasn't found. 1045 Return NULL, since this file doesn't contain the desired node. */ 1046 return NULL; 1047 } 1048 1049 /* Managing file_buffers, nodes, and tags. */ 1050 1051 /* Create a new, empty file buffer. */ 1052 FILE_BUFFER * 1053 make_file_buffer (void) 1054 { 1055 FILE_BUFFER *file_buffer = xmalloc (sizeof (FILE_BUFFER)); 1056 1057 file_buffer->filename = file_buffer->fullpath = NULL; 1058 file_buffer->contents = NULL; 1059 file_buffer->tags = NULL; 1060 file_buffer->subfiles = NULL; 1061 file_buffer->tags_slots = 0; 1062 file_buffer->flags = 0; 1063 1064 return file_buffer; 1065 } 1066 1067 /* Add FILE_BUFFER to our list of already loaded info files. */ 1068 static void 1069 remember_info_file (FILE_BUFFER *file_buffer) 1070 { 1071 int i; 1072 1073 for (i = 0; info_loaded_files && info_loaded_files[i]; i++) 1074 ; 1075 1076 add_pointer_to_array (file_buffer, i, info_loaded_files, 1077 info_loaded_files_slots, 10, FILE_BUFFER *); 1078 } 1079 1080 /* Forget the contents, tags table, nodes list, and names of FILENAME. */ 1081 static void 1082 forget_info_file (char *filename) 1083 { 1084 int i; 1085 FILE_BUFFER *file_buffer; 1086 1087 if (!info_loaded_files) 1088 return; 1089 1090 for (i = 0; (file_buffer = info_loaded_files[i]); i++) 1091 if (FILENAME_CMP (filename, file_buffer->filename) == 0 1092 || FILENAME_CMP (filename, file_buffer->fullpath) == 0) 1093 { 1094 free (file_buffer->filename); 1095 free (file_buffer->fullpath); 1096 1097 if (file_buffer->contents) 1098 free (file_buffer->contents); 1099 1100 /* free_file_buffer_tags () also kills the subfiles list, since 1101 the subfiles list is only of use in conjunction with tags. */ 1102 free_file_buffer_tags (file_buffer); 1103 1104 /* Move rest of list down. */ 1105 while (info_loaded_files[i + 1]) 1106 { 1107 info_loaded_files[i] = info_loaded_files[i + 1]; 1108 i++; 1109 } 1110 info_loaded_files[i] = 0; 1111 1112 break; 1113 } 1114 } 1115 1116 /* Free the tags (if any) associated with FILE_BUFFER. */ 1117 static void 1118 free_file_buffer_tags (FILE_BUFFER *file_buffer) 1119 { 1120 int i; 1121 1122 if (file_buffer->tags) 1123 { 1124 TAG *tag; 1125 1126 for (i = 0; (tag = file_buffer->tags[i]); i++) 1127 free_info_tag (tag); 1128 1129 free (file_buffer->tags); 1130 file_buffer->tags = NULL; 1131 file_buffer->tags_slots = 0; 1132 } 1133 1134 if (file_buffer->subfiles) 1135 { 1136 for (i = 0; file_buffer->subfiles[i]; i++) 1137 free (file_buffer->subfiles[i]); 1138 1139 free (file_buffer->subfiles); 1140 file_buffer->subfiles = NULL; 1141 } 1142 } 1143 1144 /* Free the data associated with TAG, as well as TAG itself. */ 1145 static void 1146 free_info_tag (TAG *tag) 1147 { 1148 free (tag->nodename); 1149 1150 /* We don't free tag->filename, because that filename is part of the 1151 subfiles list for the containing FILE_BUFFER. free_info_tags () 1152 will free the subfiles when it is appropriate. */ 1153 1154 free (tag); 1155 } 1156 1157 /* Load the contents of FILE_BUFFER->contents. This function is called 1158 when a file buffer was loaded, and then in order to conserve memory, the 1159 file buffer's contents were freed and the pointer was zero'ed. Note that 1160 the file was already loaded at least once successfully, so the tags and/or 1161 nodes members are still correctly filled. */ 1162 static void 1163 info_reload_file_buffer_contents (FILE_BUFFER *fb) 1164 { 1165 int is_compressed; 1166 1167 #if defined (HANDLE_MAN_PAGES) 1168 /* If this is the magic manpage node, don't try to reload, just give up. */ 1169 if (fb->flags & N_IsManPage) 1170 return; 1171 #endif 1172 1173 fb->flags &= ~N_IsCompressed; 1174 1175 /* Let the filesystem do all the work for us. */ 1176 fb->contents = 1177 filesys_read_info_file (fb->fullpath, &(fb->filesize), &(fb->finfo), 1178 &is_compressed); 1179 if (is_compressed) 1180 fb->flags |= N_IsCompressed; 1181 } 1182 1183 /* Return the actual starting memory location of NODE, side-effecting 1184 NODE->contents. MIN and MAX are bounds for a search if one is necessary. 1185 Because of the way that tags are implemented, the physical nodestart may 1186 not actually be where the tag says it is. If that is the case, but the 1187 node was found anyway, set N_UpdateTags in NODE->flags. If the node is 1188 found, return non-zero. NODE->contents is returned positioned right after 1189 the node separator that precedes this node, while the return value is 1190 position directly on the separator that precedes this node. If the node 1191 could not be found, return a NULL pointer. */ 1192 static char * 1193 adjust_nodestart (NODE *node, int min, int max) 1194 { 1195 long position; 1196 SEARCH_BINDING node_body; 1197 1198 /* Define the node body. */ 1199 node_body.buffer = node->contents; 1200 node_body.start = 0; 1201 node_body.end = max; 1202 node_body.flags = 0; 1203 1204 /* Try the optimal case first. Who knows? This file may actually be 1205 formatted (mostly) correctly. */ 1206 if (node_body.buffer[0] != INFO_COOKIE && min > 2) 1207 node_body.buffer -= 3; 1208 1209 position = find_node_separator (&node_body); 1210 1211 /* If we found a node start, then check it out. */ 1212 if (position != -1) 1213 { 1214 int sep_len; 1215 1216 sep_len = skip_node_separator (node->contents); 1217 1218 /* If we managed to skip a node separator, then check for this node 1219 being the right one. */ 1220 if (sep_len != 0) 1221 { 1222 char *nodedef, *nodestart; 1223 int offset; 1224 1225 nodestart = node_body.buffer + position + sep_len; 1226 nodedef = nodestart; 1227 offset = string_in_line (INFO_NODE_LABEL, nodedef); 1228 1229 if (offset != -1) 1230 { 1231 nodedef += offset; 1232 nodedef += skip_whitespace (nodedef); 1233 offset = skip_node_characters (nodedef, DONT_SKIP_NEWLINES); 1234 if (((unsigned int) offset == strlen (node->nodename)) && 1235 (strncmp (node->nodename, nodedef, offset) == 0)) 1236 { 1237 node->contents = nodestart; 1238 return node_body.buffer + position; 1239 } 1240 } 1241 } 1242 } 1243 1244 /* Oh well, I guess we have to try to find it in a larger area. */ 1245 node_body.buffer = node->contents - min; 1246 node_body.start = 0; 1247 node_body.end = min + max; 1248 node_body.flags = 0; 1249 1250 position = find_node_in_binding (node->nodename, &node_body); 1251 1252 /* If the node couldn't be found, we lose big. */ 1253 if (position == -1) 1254 return NULL; 1255 1256 /* Otherwise, the node was found, but the tags table could need updating 1257 (if we used a tag to get here, that is). Set the flag in NODE->flags. */ 1258 node->contents = node_body.buffer + position; 1259 node->contents += skip_node_separator (node->contents); 1260 if (node->flags & N_HasTagsTable) 1261 node->flags |= N_UpdateTags; 1262 return node_body.buffer + position; 1263 } 1264