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