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