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