156160Sru/* nodes.c -- how to get an Info file and node. 2146515Sru $Id: nodes.c,v 1.4 2004/04/11 17:56:46 karl Exp $ 321495Sjmacd 4146515Sru Copyright (C) 1993, 1998, 1999, 2000, 2002, 2003, 2004 Free Software 5116525Sru Foundation, Inc. 621495Sjmacd 721495Sjmacd This program is free software; you can redistribute it and/or modify 821495Sjmacd it under the terms of the GNU General Public License as published by 921495Sjmacd the Free Software Foundation; either version 2, or (at your option) 1021495Sjmacd any later version. 1121495Sjmacd 1221495Sjmacd This program is distributed in the hope that it will be useful, 1321495Sjmacd but WITHOUT ANY WARRANTY; without even the implied warranty of 1421495Sjmacd MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1521495Sjmacd GNU General Public License for more details. 1621495Sjmacd 1721495Sjmacd You should have received a copy of the GNU General Public License 1821495Sjmacd along with this program; if not, write to the Free Software 1921495Sjmacd Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 2021495Sjmacd 21146515Sru Originally written by Brian Fox (bfox@ai.mit.edu). */ 2221495Sjmacd 2342660Smarkm#include "info.h" 2442660Smarkm 2521495Sjmacd#include "nodes.h" 2621495Sjmacd#include "search.h" 2721495Sjmacd#include "filesys.h" 2821495Sjmacd#include "info-utils.h" 2921495Sjmacd 3021495Sjmacd#if defined (HANDLE_MAN_PAGES) 3121495Sjmacd# include "man.h" 3221495Sjmacd#endif /* HANDLE_MAN_PAGES */ 3321495Sjmacd 34146515Srustatic void forget_info_file (char *filename); 35146515Srustatic void remember_info_file (FILE_BUFFER *file_buffer); 36146515Srustatic void free_file_buffer_tags (FILE_BUFFER *file_buffer); 37146515Srustatic void free_info_tag (TAG *tag); 38146515Srustatic void get_nodes_of_tags_table (FILE_BUFFER *file_buffer, 39146515Sru SEARCH_BINDING *buffer_binding); 40146515Srustatic void get_nodes_of_info_file (FILE_BUFFER *file_buffer); 41146515Srustatic void get_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer, 42146515Sru SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding); 43146515Srustatic void info_reload_file_buffer_contents (FILE_BUFFER *fb); 44146515Srustatic char *adjust_nodestart (NODE *node, int min, int max); 45146515Srustatic FILE_BUFFER *info_load_file_internal (char *filename, int get_tags); 46146515Srustatic FILE_BUFFER *info_find_file_internal (char *filename, int get_tags); 47146515Srustatic NODE *info_node_of_file_buffer_tags (FILE_BUFFER *file_buffer, 48146515Sru char *nodename); 4921495Sjmacd 50146515Srustatic long get_node_length (SEARCH_BINDING *binding); 5121495Sjmacd 5221495Sjmacd/* Magic number that RMS used to decide how much a tags table pointer could 5356160Sru be off by. I feel that it should be much smaller, like 4. */ 5421495Sjmacd#define DEFAULT_INFO_FUDGE 1000 5521495Sjmacd 5621495Sjmacd/* Passed to *_internal functions. INFO_GET_TAGS says to do what is 5721495Sjmacd neccessary to fill in the nodes or tags arrays in FILE_BUFFER. */ 5821495Sjmacd#define INFO_NO_TAGS 0 5921495Sjmacd#define INFO_GET_TAGS 1 6021495Sjmacd 6156160Sru/* Global variables. */ 6221495Sjmacd 6321495Sjmacd/* When non-zero, this is a string describing the recent file error. */ 6456160Sruchar *info_recent_file_error = NULL; 6521495Sjmacd 6621495Sjmacd/* The list of already loaded nodes. */ 6756160SruFILE_BUFFER **info_loaded_files = NULL; 6821495Sjmacd 6921495Sjmacd/* The number of slots currently allocated to LOADED_FILES. */ 7021495Sjmacdint info_loaded_files_slots = 0; 7121495Sjmacd 7256160Sru/* Public functions for node manipulation. */ 7321495Sjmacd 7456160Sru/* Used to build `dir' menu from `localdir' files found in INFOPATH. */ 75146515Sruextern void maybe_build_dir_node (char *dirname); 7621495Sjmacd 7721495Sjmacd/* Return a pointer to a NODE structure for the Info node (FILENAME)NODENAME. 7856160Sru If FILENAME is NULL, `dir' is used. 7956160Sru IF NODENAME is NULL, `Top' is used. 8056160Sru If the node cannot be found, return NULL. */ 8121495SjmacdNODE * 82146515Sruinfo_get_node (char *filename, char *nodename) 8321495Sjmacd{ 8421495Sjmacd NODE *node; 8556160Sru FILE_BUFFER *file_buffer = NULL; 8621495Sjmacd 8756160Sru info_recent_file_error = NULL; 8821495Sjmacd info_parse_node (nodename, DONT_SKIP_NEWLINES); 8956160Sru nodename = NULL; 9021495Sjmacd 9121495Sjmacd if (info_parsed_filename) 9221495Sjmacd filename = info_parsed_filename; 9321495Sjmacd 9421495Sjmacd if (info_parsed_nodename) 9521495Sjmacd nodename = info_parsed_nodename; 9621495Sjmacd 9721495Sjmacd /* If FILENAME is not specified, it defaults to "dir". */ 9821495Sjmacd if (!filename) 9921495Sjmacd filename = "dir"; 10021495Sjmacd 10121495Sjmacd /* If the file to be looked up is "dir", build the contents from all of 10221495Sjmacd the "dir"s and "localdir"s found in INFOPATH. */ 10356160Sru if (is_dir_name (filename)) 10421495Sjmacd maybe_build_dir_node (filename); 10521495Sjmacd 10656160Sru /* Find the correct info file, or give up. */ 10721495Sjmacd file_buffer = info_find_file (filename); 10821495Sjmacd if (!file_buffer) 10921495Sjmacd { 11021495Sjmacd if (filesys_error_number) 11142660Smarkm info_recent_file_error = 11242660Smarkm filesys_error_string (filename, filesys_error_number); 11356160Sru return NULL; 11421495Sjmacd } 11521495Sjmacd 11656160Sru /* Look for the node. */ 11721495Sjmacd node = info_get_node_of_file_buffer (nodename, file_buffer); 11856160Sru 11956160Sru /* If the node not found was "Top", try again with different case. */ 12021495Sjmacd if (!node && (nodename == NULL || strcasecmp (nodename, "Top") == 0)) 12121495Sjmacd { 12221495Sjmacd node = info_get_node_of_file_buffer ("Top", file_buffer); 12321495Sjmacd if (!node) 12442660Smarkm node = info_get_node_of_file_buffer ("top", file_buffer); 12521495Sjmacd if (!node) 12642660Smarkm node = info_get_node_of_file_buffer ("TOP", file_buffer); 12721495Sjmacd } 12856160Sru 12956160Sru return node; 13021495Sjmacd} 13121495Sjmacd 13221495Sjmacd/* Return a pointer to a NODE structure for the Info node NODENAME in 13321495Sjmacd FILE_BUFFER. NODENAME can be passed as NULL, in which case the 13421495Sjmacd nodename of "Top" is used. If the node cannot be found, return a 13521495Sjmacd NULL pointer. */ 13621495SjmacdNODE * 137146515Sruinfo_get_node_of_file_buffer (char *nodename, FILE_BUFFER *file_buffer) 13821495Sjmacd{ 13956160Sru NODE *node = NULL; 14021495Sjmacd 14121495Sjmacd /* If we are unable to find the file, we have to give up. There isn't 14221495Sjmacd anything else we can do. */ 14321495Sjmacd if (!file_buffer) 14456160Sru return NULL; 14521495Sjmacd 14621495Sjmacd /* If the file buffer was gc'ed, reload the contents now. */ 14721495Sjmacd if (!file_buffer->contents) 14821495Sjmacd info_reload_file_buffer_contents (file_buffer); 14921495Sjmacd 15021495Sjmacd /* If NODENAME is not specified, it defaults to "Top". */ 15121495Sjmacd if (!nodename) 15221495Sjmacd nodename = "Top"; 15321495Sjmacd 15421495Sjmacd /* If the name of the node that we wish to find is exactly "*", then the 15521495Sjmacd node body is the contents of the entire file. Create and return such 15621495Sjmacd a node. */ 15721495Sjmacd if (strcmp (nodename, "*") == 0) 15821495Sjmacd { 15921495Sjmacd node = (NODE *)xmalloc (sizeof (NODE)); 16021495Sjmacd node->filename = file_buffer->fullpath; 16156160Sru node->parent = NULL; 16242660Smarkm node->nodename = xstrdup ("*"); 16321495Sjmacd node->contents = file_buffer->contents; 16421495Sjmacd node->nodelen = file_buffer->filesize; 16521495Sjmacd node->flags = 0; 16656160Sru node->display_pos = 0; 16721495Sjmacd } 16821495Sjmacd#if defined (HANDLE_MAN_PAGES) 16921495Sjmacd /* If the file buffer is the magic one associated with manpages, call 17021495Sjmacd the manpage node finding function instead. */ 17121495Sjmacd else if (file_buffer->flags & N_IsManPage) 17221495Sjmacd { 17342660Smarkm node = get_manpage_node (file_buffer, nodename); 17421495Sjmacd } 17521495Sjmacd#endif /* HANDLE_MAN_PAGES */ 17621495Sjmacd /* If this is the "main" info file, it might contain a tags table. Search 17721495Sjmacd the tags table for an entry which matches the node that we want. If 17821495Sjmacd there is a tags table, get the file which contains this node, but don't 17921495Sjmacd bother building a node list for it. */ 18021495Sjmacd else if (file_buffer->tags) 18121495Sjmacd { 18221495Sjmacd node = info_node_of_file_buffer_tags (file_buffer, nodename); 18321495Sjmacd } 18421495Sjmacd 18521495Sjmacd /* Return the results of our node search. */ 18656160Sru return node; 18721495Sjmacd} 18821495Sjmacd 18921495Sjmacd/* Locate the file named by FILENAME, and return the information structure 19021495Sjmacd describing this file. The file may appear in our list of loaded files 19121495Sjmacd already, or it may not. If it does not already appear, find the file, 19221495Sjmacd and add it to the list of loaded files. If the file cannot be found, 19321495Sjmacd return a NULL FILE_BUFFER *. */ 19421495SjmacdFILE_BUFFER * 195146515Sruinfo_find_file (char *filename) 19621495Sjmacd{ 19756160Sru return info_find_file_internal (filename, INFO_GET_TAGS); 19821495Sjmacd} 19921495Sjmacd 20021495Sjmacd/* Load the info file FILENAME, remembering information about it in a 20121495Sjmacd file buffer. */ 20221495SjmacdFILE_BUFFER * 203146515Sruinfo_load_file (char *filename) 20421495Sjmacd{ 20556160Sru return info_load_file_internal (filename, INFO_GET_TAGS); 20621495Sjmacd} 20721495Sjmacd 20821495Sjmacd 20956160Sru/* Private functions implementation. */ 21021495Sjmacd 21121495Sjmacd/* The workhorse for info_find_file (). Non-zero 2nd argument says to 21221495Sjmacd try to build a tags table (or otherwise glean the nodes) for this 21321495Sjmacd file once found. By default, we build the tags table, but when this 21421495Sjmacd function is called by info_get_node () when we already have a valid 21521495Sjmacd tags table describing the nodes, it is unnecessary. */ 21621495Sjmacdstatic FILE_BUFFER * 217146515Sruinfo_find_file_internal (char *filename, int get_tags) 21821495Sjmacd{ 21956160Sru int i; 22056160Sru FILE_BUFFER *file_buffer; 22121495Sjmacd 22221495Sjmacd /* First try to find the file in our list of already loaded files. */ 22321495Sjmacd if (info_loaded_files) 22421495Sjmacd { 22542660Smarkm for (i = 0; (file_buffer = info_loaded_files[i]); i++) 22693139Sru if ((FILENAME_CMP (filename, file_buffer->filename) == 0) 22793139Sru || (FILENAME_CMP (filename, file_buffer->fullpath) == 0) 22893139Sru || (!IS_ABSOLUTE (filename) 22993139Sru && FILENAME_CMP (filename, 23093139Sru filename_non_directory (file_buffer->fullpath)) 23193139Sru == 0)) 23242660Smarkm { 23342660Smarkm struct stat new_info, *old_info; 23421495Sjmacd 23542660Smarkm /* This file is loaded. If the filename that we want is 23642660Smarkm specifically "dir", then simply return the file buffer. */ 23756160Sru if (is_dir_name (filename_non_directory (filename))) 23856160Sru return file_buffer; 23921495Sjmacd 24021495Sjmacd#if defined (HANDLE_MAN_PAGES) 24142660Smarkm /* Do the same for the magic MANPAGE file. */ 24242660Smarkm if (file_buffer->flags & N_IsManPage) 24356160Sru return file_buffer; 24421495Sjmacd#endif /* HANDLE_MAN_PAGES */ 24521495Sjmacd 24693139Sru /* The file appears to be already loaded, and is not "dir". Check 24793139Sru to see if it's changed since the last time it was loaded. */ 24842660Smarkm if (stat (file_buffer->fullpath, &new_info) == -1) 24942660Smarkm { 25042660Smarkm filesys_error_number = errno; 25156160Sru return NULL; 25242660Smarkm } 25321495Sjmacd 25442660Smarkm old_info = &file_buffer->finfo; 25521495Sjmacd 25693139Sru if (new_info.st_size != old_info->st_size 25793139Sru || new_info.st_mtime != old_info->st_mtime) 25842660Smarkm { 25942660Smarkm /* The file has changed. Forget that we ever had loaded it 26042660Smarkm in the first place. */ 26142660Smarkm forget_info_file (filename); 26242660Smarkm break; 26342660Smarkm } 26442660Smarkm else 26542660Smarkm { 26642660Smarkm /* The info file exists, and has not changed since the last 26742660Smarkm time it was loaded. If the caller requested a nodes list 26842660Smarkm for this file, and there isn't one here, build the nodes 26942660Smarkm for this file_buffer. In any case, return the file_buffer 27042660Smarkm object. */ 27193139Sru if (!file_buffer->contents) 27293139Sru { 27393139Sru /* The file's contents have been gc'ed. Reload it. */ 27493139Sru info_reload_file_buffer_contents (file_buffer); 27593139Sru if (!file_buffer->contents) 27693139Sru return NULL; 27793139Sru } 27856160Sru 27942660Smarkm if (get_tags && !file_buffer->tags) 28042660Smarkm build_tags_and_nodes (file_buffer); 28121495Sjmacd 28256160Sru return file_buffer; 28342660Smarkm } 28442660Smarkm } 28521495Sjmacd } 28621495Sjmacd 28721495Sjmacd /* The file wasn't loaded. Try to load it now. */ 28821495Sjmacd#if defined (HANDLE_MAN_PAGES) 28921495Sjmacd /* If the name of the file that we want is our special file buffer for 29021495Sjmacd Unix manual pages, then create the file buffer, and return it now. */ 29121495Sjmacd if (strcasecmp (filename, MANPAGE_FILE_BUFFER_NAME) == 0) 29221495Sjmacd file_buffer = create_manpage_file_buffer (); 29321495Sjmacd else 29421495Sjmacd#endif /* HANDLE_MAN_PAGES */ 29521495Sjmacd file_buffer = info_load_file_internal (filename, get_tags); 29621495Sjmacd 29721495Sjmacd /* If the file was loaded, remember the name under which it was found. */ 29821495Sjmacd if (file_buffer) 29921495Sjmacd remember_info_file (file_buffer); 30021495Sjmacd 30156160Sru return file_buffer; 30221495Sjmacd} 30321495Sjmacd 30421495Sjmacd/* The workhorse function for info_load_file (). Non-zero second argument 30521495Sjmacd says to build a list of tags (or nodes) for this file. This is the 30621495Sjmacd default behaviour when info_load_file () is called, but it is not 30721495Sjmacd necessary when loading a subfile for which we already have tags. */ 30821495Sjmacdstatic FILE_BUFFER * 309146515Sruinfo_load_file_internal (char *filename, int get_tags) 31021495Sjmacd{ 31121495Sjmacd char *fullpath, *contents; 31221495Sjmacd long filesize; 31321495Sjmacd struct stat finfo; 31456160Sru int retcode, compressed; 31556160Sru FILE_BUFFER *file_buffer = NULL; 31621495Sjmacd 31721495Sjmacd /* Get the full pathname of this file, as known by the info system. 31821495Sjmacd That is to say, search along INFOPATH and expand tildes, etc. */ 31921495Sjmacd fullpath = info_find_fullpath (filename); 32021495Sjmacd 32121495Sjmacd /* Did we actually find the file? */ 32221495Sjmacd retcode = stat (fullpath, &finfo); 32321495Sjmacd 32421495Sjmacd /* If the file referenced by the name returned from info_find_fullpath () 32521495Sjmacd doesn't exist, then try again with the last part of the filename 32621495Sjmacd appearing in lowercase. */ 32756160Sru /* This is probably not needed at all on those systems which define 32856160Sru FILENAME_CMP to be strcasecmp. But let's do it anyway, lest some 32956160Sru network redirector supports case sensitivity. */ 33021495Sjmacd if (retcode < 0) 33121495Sjmacd { 33221495Sjmacd char *lowered_name; 333146515Sru char *tmp_basename; 33421495Sjmacd 33542660Smarkm lowered_name = xstrdup (filename); 336146515Sru tmp_basename = filename_non_directory (lowered_name); 33721495Sjmacd 338146515Sru while (*tmp_basename) 33942660Smarkm { 340146515Sru if (isupper (*tmp_basename)) 341146515Sru *tmp_basename = tolower (*tmp_basename); 34221495Sjmacd 343146515Sru tmp_basename++; 34442660Smarkm } 34521495Sjmacd 34621495Sjmacd fullpath = info_find_fullpath (lowered_name); 34721495Sjmacd 34821495Sjmacd retcode = stat (fullpath, &finfo); 349146515Sru free (lowered_name); 35021495Sjmacd } 35121495Sjmacd 35221495Sjmacd /* If the file wasn't found, give up, returning a NULL pointer. */ 35321495Sjmacd if (retcode < 0) 35421495Sjmacd { 35521495Sjmacd filesys_error_number = errno; 35656160Sru return NULL; 35721495Sjmacd } 35821495Sjmacd 35921495Sjmacd /* Otherwise, try to load the file. */ 36056160Sru contents = filesys_read_info_file (fullpath, &filesize, &finfo, &compressed); 36121495Sjmacd 36221495Sjmacd if (!contents) 36356160Sru return NULL; 36421495Sjmacd 36521495Sjmacd /* The file was found, and can be read. Allocate FILE_BUFFER and fill 36621495Sjmacd in the various members. */ 36721495Sjmacd file_buffer = make_file_buffer (); 36842660Smarkm file_buffer->filename = xstrdup (filename); 36942660Smarkm file_buffer->fullpath = xstrdup (fullpath); 37021495Sjmacd file_buffer->finfo = finfo; 37121495Sjmacd file_buffer->filesize = filesize; 37221495Sjmacd file_buffer->contents = contents; 37356160Sru if (compressed) 37421495Sjmacd file_buffer->flags |= N_IsCompressed; 37521495Sjmacd 37621495Sjmacd /* If requested, build the tags and nodes for this file buffer. */ 37721495Sjmacd if (get_tags) 37821495Sjmacd build_tags_and_nodes (file_buffer); 37921495Sjmacd 38056160Sru return file_buffer; 38121495Sjmacd} 38256160Sru 38321495Sjmacd/* Grovel FILE_BUFFER->contents finding tags and nodes, and filling in the 38421495Sjmacd various slots. This can also be used to rebuild a tag or node table. */ 38521495Sjmacdvoid 386146515Srubuild_tags_and_nodes (FILE_BUFFER *file_buffer) 38721495Sjmacd{ 38821495Sjmacd SEARCH_BINDING binding; 38921495Sjmacd long position; 39021495Sjmacd 39121495Sjmacd free_file_buffer_tags (file_buffer); 39221495Sjmacd file_buffer->flags &= ~N_HasTagsTable; 39321495Sjmacd 39421495Sjmacd /* See if there is a tags table in this info file. */ 39521495Sjmacd binding.buffer = file_buffer->contents; 39621495Sjmacd binding.start = file_buffer->filesize; 39721495Sjmacd binding.end = binding.start - 1000; 39821495Sjmacd if (binding.end < 0) 39921495Sjmacd binding.end = 0; 40021495Sjmacd binding.flags = S_FoldCase; 40121495Sjmacd 40221495Sjmacd position = search_backward (TAGS_TABLE_END_LABEL, &binding); 40321495Sjmacd 40421495Sjmacd /* If there is a tag table, find the start of it, and grovel over it 40521495Sjmacd extracting tag information. */ 40621495Sjmacd if (position != -1) 40721495Sjmacd while (1) 40821495Sjmacd { 40942660Smarkm long tags_table_begin, tags_table_end; 41021495Sjmacd 41142660Smarkm binding.end = position; 41242660Smarkm binding.start = binding.end - 5 - strlen (TAGS_TABLE_END_LABEL); 41342660Smarkm if (binding.start < 0) 41442660Smarkm binding.start = 0; 41521495Sjmacd 41642660Smarkm position = find_node_separator (&binding); 41721495Sjmacd 41842660Smarkm /* For this test, (and all others here) failure indicates a bogus 41942660Smarkm tags table. Grovel the file. */ 42042660Smarkm if (position == -1) 42142660Smarkm break; 42221495Sjmacd 42342660Smarkm /* Remember the end of the tags table. */ 42442660Smarkm binding.start = position; 42542660Smarkm tags_table_end = binding.start; 42642660Smarkm binding.end = 0; 42721495Sjmacd 42842660Smarkm /* Locate the start of the tags table. */ 42942660Smarkm position = search_backward (TAGS_TABLE_BEG_LABEL, &binding); 43021495Sjmacd 43142660Smarkm if (position == -1) 43242660Smarkm break; 43321495Sjmacd 43442660Smarkm binding.end = position; 43542660Smarkm binding.start = binding.end - 5 - strlen (TAGS_TABLE_BEG_LABEL); 43642660Smarkm position = find_node_separator (&binding); 43721495Sjmacd 43842660Smarkm if (position == -1) 43942660Smarkm break; 44021495Sjmacd 44142660Smarkm /* The file contains a valid tags table. Fill the FILE_BUFFER's 44242660Smarkm tags member. */ 44342660Smarkm file_buffer->flags |= N_HasTagsTable; 44442660Smarkm tags_table_begin = position; 44521495Sjmacd 44642660Smarkm /* If this isn't an indirect tags table, just remember the nodes 44742660Smarkm described locally in this tags table. Note that binding.end 44842660Smarkm is pointing to just after the beginning label. */ 44942660Smarkm binding.start = binding.end; 45042660Smarkm binding.end = file_buffer->filesize; 45121495Sjmacd 45242660Smarkm if (!looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, &binding)) 45342660Smarkm { 45442660Smarkm binding.start = tags_table_begin; 45542660Smarkm binding.end = tags_table_end; 45642660Smarkm get_nodes_of_tags_table (file_buffer, &binding); 45742660Smarkm return; 45842660Smarkm } 45942660Smarkm else 46042660Smarkm { 46142660Smarkm /* This is an indirect tags table. Build TAGS member. */ 46242660Smarkm SEARCH_BINDING indirect; 46321495Sjmacd 46442660Smarkm indirect.start = tags_table_begin; 46542660Smarkm indirect.end = 0; 46642660Smarkm indirect.buffer = binding.buffer; 46742660Smarkm indirect.flags = S_FoldCase; 46821495Sjmacd 46942660Smarkm position = search_backward (INDIRECT_TAGS_TABLE_LABEL, &indirect); 47021495Sjmacd 47142660Smarkm if (position == -1) 47242660Smarkm { 47342660Smarkm /* This file is malformed. Give up. */ 47442660Smarkm return; 47542660Smarkm } 47621495Sjmacd 47742660Smarkm indirect.start = position; 47842660Smarkm indirect.end = tags_table_begin; 47942660Smarkm binding.start = tags_table_begin; 48042660Smarkm binding.end = tags_table_end; 48142660Smarkm get_tags_of_indirect_tags_table (file_buffer, &indirect, &binding); 48242660Smarkm return; 48342660Smarkm } 48421495Sjmacd } 48521495Sjmacd 48621495Sjmacd /* This file doesn't contain any kind of tags table. Grovel the 48721495Sjmacd file and build node entries for it. */ 48821495Sjmacd get_nodes_of_info_file (file_buffer); 48921495Sjmacd} 49021495Sjmacd 49121495Sjmacd/* Search through FILE_BUFFER->contents building an array of TAG *, 49221495Sjmacd one entry per each node present in the file. Store the tags in 49321495Sjmacd FILE_BUFFER->tags, and the number of allocated slots in 49421495Sjmacd FILE_BUFFER->tags_slots. */ 49521495Sjmacdstatic void 496146515Sruget_nodes_of_info_file (FILE_BUFFER *file_buffer) 49721495Sjmacd{ 49821495Sjmacd long nodestart; 49921495Sjmacd int tags_index = 0; 50021495Sjmacd SEARCH_BINDING binding; 50121495Sjmacd 50221495Sjmacd binding.buffer = file_buffer->contents; 50321495Sjmacd binding.start = 0; 50421495Sjmacd binding.end = file_buffer->filesize; 50521495Sjmacd binding.flags = S_FoldCase; 50621495Sjmacd 50721495Sjmacd while ((nodestart = find_node_separator (&binding)) != -1) 50821495Sjmacd { 50921495Sjmacd int start, end; 51021495Sjmacd char *nodeline; 51121495Sjmacd TAG *entry; 51256160Sru int anchor = 0; 51321495Sjmacd 51421495Sjmacd /* Skip past the characters just found. */ 51521495Sjmacd binding.start = nodestart; 51621495Sjmacd binding.start += skip_node_separator (binding.buffer + binding.start); 51721495Sjmacd 51821495Sjmacd /* Move to the start of the line defining the node. */ 51921495Sjmacd nodeline = binding.buffer + binding.start; 52021495Sjmacd 52121495Sjmacd /* Find "Node:" */ 52221495Sjmacd start = string_in_line (INFO_NODE_LABEL, nodeline); 52356160Sru /* No Node:. Maybe it's a Ref:. */ 52456160Sru if (start == -1) 52556160Sru { 52656160Sru start = string_in_line (INFO_REF_LABEL, nodeline); 52756160Sru if (start != -1) 52856160Sru anchor = 1; 52956160Sru } 53021495Sjmacd 53121495Sjmacd /* If not there, this is not the start of a node. */ 53221495Sjmacd if (start == -1) 53342660Smarkm continue; 53421495Sjmacd 53521495Sjmacd /* Find the start of the nodename. */ 53621495Sjmacd start += skip_whitespace (nodeline + start); 53721495Sjmacd 53821495Sjmacd /* Find the end of the nodename. */ 53921495Sjmacd end = start + 54042660Smarkm skip_node_characters (nodeline + start, DONT_SKIP_NEWLINES); 54121495Sjmacd 54221495Sjmacd /* Okay, we have isolated the node name, and we know where the 54356160Sru node starts. Remember this information. */ 54456160Sru entry = xmalloc (sizeof (TAG)); 54556160Sru entry->nodename = xmalloc (1 + (end - start)); 54621495Sjmacd strncpy (entry->nodename, nodeline + start, end - start); 54756160Sru entry->nodename[end - start] = 0; 54821495Sjmacd entry->nodestart = nodestart; 54956160Sru if (anchor) 55056160Sru entry->nodelen = 0; 55156160Sru else 55256160Sru { 55356160Sru SEARCH_BINDING node_body; 55421495Sjmacd 55556160Sru node_body.buffer = binding.buffer + binding.start; 55656160Sru node_body.start = 0; 55756160Sru node_body.end = binding.end - binding.start; 55856160Sru node_body.flags = S_FoldCase; 55956160Sru entry->nodelen = get_node_length (&node_body); 56056160Sru } 56121495Sjmacd 56221495Sjmacd entry->filename = file_buffer->fullpath; 56321495Sjmacd 56421495Sjmacd /* Add this tag to the array of tag structures in this FILE_BUFFER. */ 56521495Sjmacd add_pointer_to_array (entry, tags_index, file_buffer->tags, 56642660Smarkm file_buffer->tags_slots, 100, TAG *); 56721495Sjmacd } 56821495Sjmacd} 56921495Sjmacd 57021495Sjmacd/* Return the length of the node which starts at BINDING. */ 57121495Sjmacdstatic long 572146515Sruget_node_length (SEARCH_BINDING *binding) 57321495Sjmacd{ 57456160Sru int i; 57521495Sjmacd char *body; 57621495Sjmacd 57756160Sru /* [A node] ends with either a ^_, a ^L, or end of file. */ 57821495Sjmacd for (i = binding->start, body = binding->buffer; i < binding->end; i++) 57921495Sjmacd { 58021495Sjmacd if (body[i] == INFO_FF || body[i] == INFO_COOKIE) 58142660Smarkm break; 58221495Sjmacd } 58356160Sru return i - binding->start; 58421495Sjmacd} 58521495Sjmacd 58621495Sjmacd/* Build and save the array of nodes in FILE_BUFFER by searching through the 58721495Sjmacd contents of BUFFER_BINDING for a tags table, and groveling the contents. */ 58821495Sjmacdstatic void 589146515Sruget_nodes_of_tags_table (FILE_BUFFER *file_buffer, 590146515Sru SEARCH_BINDING *buffer_binding) 59121495Sjmacd{ 59256160Sru int name_offset; 593146515Sru SEARCH_BINDING *tmp_search; 59421495Sjmacd long position; 59556160Sru int tags_index = 0; 59621495Sjmacd 597146515Sru tmp_search = copy_binding (buffer_binding); 59821495Sjmacd 59921495Sjmacd /* Find the start of the tags table. */ 600146515Sru position = find_tags_table (tmp_search); 60121495Sjmacd 60221495Sjmacd /* If none, we're all done. */ 60321495Sjmacd if (position == -1) 60421495Sjmacd return; 60521495Sjmacd 60621495Sjmacd /* Move to one character before the start of the actual table. */ 607146515Sru tmp_search->start = position; 608146515Sru tmp_search->start += skip_node_separator 609146515Sru (tmp_search->buffer + tmp_search->start); 610146515Sru tmp_search->start += strlen (TAGS_TABLE_BEG_LABEL); 611146515Sru tmp_search->start--; 61221495Sjmacd 61321495Sjmacd /* The tag table consists of lines containing node names and positions. 61421495Sjmacd Do each line until we find one that doesn't contain a node name. */ 615146515Sru while ((position = search_forward ("\n", tmp_search)) != -1) 61621495Sjmacd { 61721495Sjmacd TAG *entry; 61821495Sjmacd char *nodedef; 61956160Sru unsigned p; 62056160Sru int anchor = 0; 62121495Sjmacd 62221495Sjmacd /* Prepare to skip this line. */ 623146515Sru tmp_search->start = position; 624146515Sru tmp_search->start++; 62521495Sjmacd 62621495Sjmacd /* Skip past informative "(Indirect)" tags table line. */ 627146515Sru if (!tags_index && looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, tmp_search)) 62842660Smarkm continue; 62921495Sjmacd 63021495Sjmacd /* Find the label preceding the node name. */ 63156160Sru name_offset = 632146515Sru string_in_line (INFO_NODE_LABEL, tmp_search->buffer + tmp_search->start); 63321495Sjmacd 63456160Sru /* If no node label, maybe it's an anchor. */ 63556160Sru if (name_offset == -1) 63656160Sru { 63756160Sru name_offset = string_in_line (INFO_REF_LABEL, 638146515Sru tmp_search->buffer + tmp_search->start); 63956160Sru if (name_offset != -1) 64056160Sru anchor = 1; 64156160Sru } 64256160Sru 64321495Sjmacd /* If not there, not a defining line, so we must be out of the 64456160Sru tags table. */ 64556160Sru if (name_offset == -1) 64642660Smarkm break; 64721495Sjmacd 64856160Sru entry = xmalloc (sizeof (TAG)); 64956160Sru 65056160Sru /* Find the beginning of the node definition. */ 651146515Sru tmp_search->start += name_offset; 652146515Sru nodedef = tmp_search->buffer + tmp_search->start; 65321495Sjmacd nodedef += skip_whitespace (nodedef); 65421495Sjmacd 65556160Sru /* Move past the node's name in this tag to the TAGSEP character. */ 65656160Sru for (p = 0; nodedef[p] && nodedef[p] != INFO_TAGSEP; p++) 65756160Sru ; 65856160Sru if (nodedef[p] != INFO_TAGSEP) 65942660Smarkm continue; 66021495Sjmacd 66156160Sru entry->nodename = xmalloc (p + 1); 66256160Sru strncpy (entry->nodename, nodedef, p); 66356160Sru entry->nodename[p] = 0; 66456160Sru p++; 66556160Sru entry->nodestart = atol (nodedef + p); 66621495Sjmacd 66756160Sru /* If a node, we don't know the length yet, but if it's an 66856160Sru anchor, the length is 0. */ 66956160Sru entry->nodelen = anchor ? 0 : -1; 67021495Sjmacd 67121495Sjmacd /* The filename of this node is currently known as the same as the 67242660Smarkm name of this file. */ 67321495Sjmacd entry->filename = file_buffer->fullpath; 67421495Sjmacd 67521495Sjmacd /* Add this node structure to the array of node structures in this 67642660Smarkm FILE_BUFFER. */ 67721495Sjmacd add_pointer_to_array (entry, tags_index, file_buffer->tags, 67842660Smarkm file_buffer->tags_slots, 100, TAG *); 67921495Sjmacd } 680146515Sru free (tmp_search); 68121495Sjmacd} 68221495Sjmacd 68356160Sru/* A structure used only in `get_tags_of_indirect_tags_table' to hold onto 68421495Sjmacd an intermediate value. */ 68521495Sjmacdtypedef struct { 68621495Sjmacd char *filename; 68721495Sjmacd long first_byte; 68821495Sjmacd} SUBFILE; 68921495Sjmacd 69021495Sjmacd/* Remember in FILE_BUFFER the nodenames, subfilenames, and offsets within the 69121495Sjmacd subfiles of every node which appears in TAGS_BINDING. The 2nd argument is 69221495Sjmacd a binding surrounding the indirect files list. */ 69321495Sjmacdstatic void 694146515Sruget_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer, 695146515Sru SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding) 69621495Sjmacd{ 69756160Sru int i; 69856160Sru SUBFILE **subfiles = NULL; 69921495Sjmacd int subfiles_index = 0, subfiles_slots = 0; 70021495Sjmacd TAG *entry; 70121495Sjmacd 70221495Sjmacd /* First get the list of tags from the tags table. Then lookup the 70321495Sjmacd associated file in the indirect list for each tag, and update it. */ 70421495Sjmacd get_nodes_of_tags_table (file_buffer, tags_binding); 70521495Sjmacd 70621495Sjmacd /* We have the list of tags in file_buffer->tags. Get the list of 70721495Sjmacd subfiles from the indirect table. */ 70821495Sjmacd { 70921495Sjmacd char *start, *end, *line; 71021495Sjmacd SUBFILE *subfile; 71121495Sjmacd 71221495Sjmacd start = indirect_binding->buffer + indirect_binding->start; 71321495Sjmacd end = indirect_binding->buffer + indirect_binding->end; 71421495Sjmacd line = start; 71521495Sjmacd 71621495Sjmacd while (line < end) 71721495Sjmacd { 71842660Smarkm int colon; 71921495Sjmacd 72042660Smarkm colon = string_in_line (":", line); 72121495Sjmacd 72242660Smarkm if (colon == -1) 72342660Smarkm break; 72421495Sjmacd 72542660Smarkm subfile = (SUBFILE *)xmalloc (sizeof (SUBFILE)); 72642660Smarkm subfile->filename = (char *)xmalloc (colon); 72742660Smarkm strncpy (subfile->filename, line, colon - 1); 72856160Sru subfile->filename[colon - 1] = 0; 72942660Smarkm subfile->first_byte = (long) atol (line + colon); 73021495Sjmacd 73142660Smarkm add_pointer_to_array 73242660Smarkm (subfile, subfiles_index, subfiles, subfiles_slots, 10, SUBFILE *); 73321495Sjmacd 73442660Smarkm while (*line++ != '\n'); 73521495Sjmacd } 73621495Sjmacd } 73721495Sjmacd 73821495Sjmacd /* If we have successfully built the indirect files table, then 73921495Sjmacd merge the information in the two tables. */ 74021495Sjmacd if (!subfiles) 74121495Sjmacd { 74221495Sjmacd free_file_buffer_tags (file_buffer); 74321495Sjmacd return; 74421495Sjmacd } 74521495Sjmacd else 74621495Sjmacd { 74756160Sru int tags_index; 74821495Sjmacd long header_length; 74921495Sjmacd SEARCH_BINDING binding; 75021495Sjmacd 75121495Sjmacd /* Find the length of the header of the file containing the indirect 75242660Smarkm tags table. This header appears at the start of every file. We 75342660Smarkm want the absolute position of each node within each subfile, so 75442660Smarkm we subtract the start of the containing subfile from the logical 75542660Smarkm position of the node, and then add the length of the header in. */ 75621495Sjmacd binding.buffer = file_buffer->contents; 75721495Sjmacd binding.start = 0; 75821495Sjmacd binding.end = file_buffer->filesize; 75921495Sjmacd binding.flags = S_FoldCase; 76021495Sjmacd 76121495Sjmacd header_length = find_node_separator (&binding); 76221495Sjmacd if (header_length == -1) 76342660Smarkm header_length = 0; 76421495Sjmacd 76521495Sjmacd /* Build the file buffer's list of subfiles. */ 76621495Sjmacd { 76756160Sru char *containing_dir = xstrdup (file_buffer->fullpath); 76893139Sru char *temp = filename_non_directory (containing_dir); 76942660Smarkm int len_containing_dir; 77021495Sjmacd 77193139Sru if (temp > containing_dir) 77293139Sru { 77393139Sru if (HAVE_DRIVE (file_buffer->fullpath) && 77493139Sru temp == containing_dir + 2) 77593139Sru { 77693139Sru /* Avoid converting "d:foo" into "d:/foo" below. */ 77793139Sru *temp = '.'; 77893139Sru temp += 2; 77993139Sru } 78093139Sru temp[-1] = 0; 78193139Sru } 78221495Sjmacd 78342660Smarkm len_containing_dir = strlen (containing_dir); 78421495Sjmacd 78542660Smarkm for (i = 0; subfiles[i]; i++); 78621495Sjmacd 78742660Smarkm file_buffer->subfiles = (char **) xmalloc ((1 + i) * sizeof (char *)); 78821495Sjmacd 78942660Smarkm for (i = 0; subfiles[i]; i++) 79042660Smarkm { 79142660Smarkm char *fullpath; 79221495Sjmacd 79342660Smarkm fullpath = (char *) xmalloc 79442660Smarkm (2 + strlen (subfiles[i]->filename) + len_containing_dir); 79521495Sjmacd 79642660Smarkm sprintf (fullpath, "%s/%s", 79742660Smarkm containing_dir, subfiles[i]->filename); 79821495Sjmacd 79942660Smarkm file_buffer->subfiles[i] = fullpath; 80042660Smarkm } 80156160Sru file_buffer->subfiles[i] = NULL; 80242660Smarkm free (containing_dir); 80321495Sjmacd } 80421495Sjmacd 80521495Sjmacd /* For each node in the file's tags table, remember the starting 80642660Smarkm position. */ 80742660Smarkm for (tags_index = 0; (entry = file_buffer->tags[tags_index]); 80842660Smarkm tags_index++) 80942660Smarkm { 81042660Smarkm for (i = 0; 81142660Smarkm subfiles[i] && entry->nodestart >= subfiles[i]->first_byte; 81242660Smarkm i++); 81321495Sjmacd 81442660Smarkm /* If the Info file containing the indirect tags table is 81542660Smarkm malformed, then give up. */ 81642660Smarkm if (!i) 81742660Smarkm { 81842660Smarkm /* The Info file containing the indirect tags table is 81942660Smarkm malformed. Give up. */ 82042660Smarkm for (i = 0; subfiles[i]; i++) 82142660Smarkm { 82242660Smarkm free (subfiles[i]->filename); 82342660Smarkm free (subfiles[i]); 82442660Smarkm free (file_buffer->subfiles[i]); 82542660Smarkm } 82656160Sru file_buffer->subfiles = NULL; 82742660Smarkm free_file_buffer_tags (file_buffer); 82842660Smarkm return; 82942660Smarkm } 83021495Sjmacd 83142660Smarkm /* SUBFILES[i] is the index of the first subfile whose logical 83242660Smarkm first byte is greater than the logical offset of this node's 83342660Smarkm starting position. This means that the subfile directly 83442660Smarkm preceding this one is the one containing the node. */ 83521495Sjmacd 83642660Smarkm entry->filename = file_buffer->subfiles[i - 1]; 83756160Sru entry->nodestart -= subfiles[i - 1]->first_byte; 83842660Smarkm entry->nodestart += header_length; 83942660Smarkm } 84021495Sjmacd 84121495Sjmacd /* We have successfully built the tags table. Remember that it 84242660Smarkm was indirect. */ 84321495Sjmacd file_buffer->flags |= N_TagsIndirect; 84421495Sjmacd } 84521495Sjmacd 84621495Sjmacd /* Free the structures assigned to SUBFILES. Free the names as well 84721495Sjmacd as the structures themselves, then finally, the array. */ 84821495Sjmacd for (i = 0; subfiles[i]; i++) 84921495Sjmacd { 85021495Sjmacd free (subfiles[i]->filename); 85121495Sjmacd free (subfiles[i]); 85221495Sjmacd } 85321495Sjmacd free (subfiles); 85421495Sjmacd} 85521495Sjmacd 85656160Sru 85756160Sru/* Return the node that contains TAG in FILE_BUFFER, else 85856160Sru (pathologically) NULL. Called from info_node_of_file_buffer_tags. */ 85956160Srustatic NODE * 860146515Srufind_node_of_anchor (FILE_BUFFER *file_buffer, TAG *tag) 86156160Sru{ 86256160Sru int anchor_pos, node_pos; 86356160Sru TAG *node_tag; 86456160Sru NODE *node; 865116525Sru 86656160Sru /* Look through the tag list for the anchor. */ 86756160Sru for (anchor_pos = 0; file_buffer->tags[anchor_pos]; anchor_pos++) 86856160Sru { 86956160Sru TAG *t = file_buffer->tags[anchor_pos]; 87056160Sru if (t->nodestart == tag->nodestart) 871116525Sru break; 87256160Sru } 873116525Sru 87456160Sru /* Should not happen, because we should always find the anchor. */ 87556160Sru if (!file_buffer->tags[anchor_pos]) 87656160Sru return NULL; 877116525Sru 87856160Sru /* We've found the anchor. Look backwards in the tag table for the 87956160Sru preceding node (we're assuming the tags are given in order), 88056160Sru skipping over any preceding anchors. */ 88156160Sru for (node_pos = anchor_pos - 1; 88256160Sru node_pos >= 0 && file_buffer->tags[node_pos]->nodelen == 0; 88356160Sru node_pos--) 88456160Sru ; 885116525Sru 88656160Sru /* An info file with an anchor before any nodes is pathological, but 88756160Sru it's possible, so don't crash. */ 88856160Sru if (node_pos < 0) 88956160Sru return NULL; 890116525Sru 89156160Sru /* We have the tag for the node that contained the anchor tag. */ 892116525Sru node_tag = file_buffer->tags[node_pos]; 89356160Sru 89456160Sru /* Look up the node name in the tag table to get the actual node. 89556160Sru This is a recursive call, but it can't recurse again, because we 89656160Sru call it with a real node. */ 89756160Sru node = info_node_of_file_buffer_tags (file_buffer, node_tag->nodename); 898116525Sru 89956160Sru /* Start displaying the node at the anchor position. */ 90056160Sru if (node) 90156160Sru { /* The nodestart for real nodes is three characters before the `F' 90256160Sru in the `File:' line (a newline, the CTRL-_, and another 90356160Sru newline). The nodestart for anchors is the actual position. 90456160Sru But we offset by only 2, rather than 3, because if an anchor is 90556160Sru at the beginning of a paragraph, it's nicer for it to end up on 90656160Sru the beginning of the first line of the paragraph rather than 90756160Sru the blank line before it. (makeinfo has no way of knowing that 90856160Sru a paragraph is going to start, so we can't fix it there.) */ 90956160Sru node->display_pos = file_buffer->tags[anchor_pos]->nodestart 91056160Sru - (node_tag->nodestart + 2); 91156160Sru 91256160Sru /* Otherwise an anchor at the end of a node ends up displaying at 91356160Sru the end of the last line of the node (way over on the right of 91456160Sru the screen), which looks wrong. */ 915146515Sru if (node->display_pos >= (unsigned long) node->nodelen) 91656160Sru node->display_pos = node->nodelen - 1; 917116525Sru 91856160Sru /* Don't search in the node for the xref text, it's not there. */ 91956160Sru node->flags |= N_FromAnchor; 92056160Sru } 921116525Sru 92256160Sru return node; 92356160Sru} 92456160Sru 92556160Sru 92621495Sjmacd/* Return the node from FILE_BUFFER which matches NODENAME by searching 92756160Sru the tags table in FILE_BUFFER, or NULL. */ 92821495Sjmacdstatic NODE * 929146515Sruinfo_node_of_file_buffer_tags (FILE_BUFFER *file_buffer, char *nodename) 93021495Sjmacd{ 93121495Sjmacd TAG *tag; 93256160Sru int i; 93321495Sjmacd 93493139Sru /* If no tags at all (possibly a misformatted info file), quit. */ 93593139Sru if (!file_buffer->tags) { 93693139Sru return NULL; 93793139Sru } 938116525Sru 939116525Sru for (i = 0; (tag = file_buffer->tags[i]); i++) 94021495Sjmacd if (strcmp (nodename, tag->nodename) == 0) 94121495Sjmacd { 94293139Sru FILE_BUFFER *subfile = info_find_file_internal (tag->filename, 94393139Sru INFO_NO_TAGS); 94442660Smarkm if (!subfile) 94556160Sru return NULL; 94621495Sjmacd 94742660Smarkm if (!subfile->contents) 94842660Smarkm { 94942660Smarkm info_reload_file_buffer_contents (subfile); 95042660Smarkm if (!subfile->contents) 95156160Sru return NULL; 95242660Smarkm } 95321495Sjmacd 95442660Smarkm /* If we were able to find this file and load it, then return 95542660Smarkm the node within it. */ 95642660Smarkm { 95756160Sru NODE *node = xmalloc (sizeof (NODE)); 95856160Sru node->filename = subfile->fullpath; 95956160Sru node->parent = NULL; 96056160Sru node->nodename = tag->nodename; 96156160Sru node->contents = subfile->contents + tag->nodestart; 96256160Sru node->display_pos = 0; 96356160Sru node->flags = 0; 96421495Sjmacd 96542660Smarkm if (file_buffer->flags & N_HasTagsTable) 96642660Smarkm { 96742660Smarkm node->flags |= N_HasTagsTable; 96821495Sjmacd 96942660Smarkm if (file_buffer->flags & N_TagsIndirect) 97042660Smarkm { 97142660Smarkm node->flags |= N_TagsIndirect; 97242660Smarkm node->parent = file_buffer->fullpath; 97342660Smarkm } 97442660Smarkm } 97521495Sjmacd 97642660Smarkm if (subfile->flags & N_IsCompressed) 97742660Smarkm node->flags |= N_IsCompressed; 97821495Sjmacd 97942660Smarkm /* If TAG->nodelen hasn't been calculated yet, then we aren't 98042660Smarkm in a position to trust the entry pointer. Adjust things so 98142660Smarkm that ENTRY->nodestart gets the exact address of the start of 98242660Smarkm the node separator which starts this node, and NODE->contents 98342660Smarkm gets the address of the line defining this node. If we cannot 98442660Smarkm do that, the node isn't really here. */ 98542660Smarkm if (tag->nodelen == -1) 98642660Smarkm { 98742660Smarkm int min, max; 98842660Smarkm char *node_sep; 98942660Smarkm SEARCH_BINDING node_body; 99042660Smarkm char *buff_end; 99121495Sjmacd 99242660Smarkm min = max = DEFAULT_INFO_FUDGE; 99321495Sjmacd 99442660Smarkm if (tag->nodestart < DEFAULT_INFO_FUDGE) 99542660Smarkm min = tag->nodestart; 99621495Sjmacd 99742660Smarkm if (DEFAULT_INFO_FUDGE > 99842660Smarkm (subfile->filesize - tag->nodestart)) 99942660Smarkm max = subfile->filesize - tag->nodestart; 100021495Sjmacd 100142660Smarkm /* NODE_SEP gets the address of the separator which defines 100256160Sru this node, or NULL if the node wasn't found. 100342660Smarkm NODE->contents is side-effected to point to right after 100442660Smarkm the separator. */ 100542660Smarkm node_sep = adjust_nodestart (node, min, max); 100656160Sru if (node_sep == NULL) 100742660Smarkm { 100842660Smarkm free (node); 100956160Sru return NULL; 101042660Smarkm } 101142660Smarkm /* Readjust tag->nodestart. */ 101242660Smarkm tag->nodestart = node_sep - subfile->contents; 101321495Sjmacd 101442660Smarkm /* Calculate the length of the current node. */ 101542660Smarkm buff_end = subfile->contents + subfile->filesize; 101621495Sjmacd 101742660Smarkm node_body.buffer = node->contents; 101842660Smarkm node_body.start = 0; 101942660Smarkm node_body.end = buff_end - node_body.buffer; 102042660Smarkm node_body.flags = 0; 102142660Smarkm tag->nodelen = get_node_length (&node_body); 102256160Sru node->nodelen = tag->nodelen; 102342660Smarkm } 102456160Sru 102556160Sru else if (tag->nodelen == 0) /* anchor, return containing node */ 102656160Sru { 102756160Sru free (node); 102856160Sru node = find_node_of_anchor (file_buffer, tag); 102956160Sru } 1030116525Sru 103142660Smarkm else 103242660Smarkm { 103342660Smarkm /* Since we know the length of this node, we have already 103442660Smarkm adjusted tag->nodestart to point to the exact start of 103542660Smarkm it. Simply skip the node separator. */ 103642660Smarkm node->contents += skip_node_separator (node->contents); 103756160Sru node->nodelen = tag->nodelen; 103842660Smarkm } 103921495Sjmacd 104056160Sru return node; 104142660Smarkm } 104221495Sjmacd } 104321495Sjmacd 104421495Sjmacd /* There was a tag table for this file, and the node wasn't found. 104521495Sjmacd Return NULL, since this file doesn't contain the desired node. */ 104656160Sru return NULL; 104721495Sjmacd} 104821495Sjmacd 104956160Sru/* Managing file_buffers, nodes, and tags. */ 105021495Sjmacd 105121495Sjmacd/* Create a new, empty file buffer. */ 105221495SjmacdFILE_BUFFER * 1053146515Srumake_file_buffer (void) 105421495Sjmacd{ 105556160Sru FILE_BUFFER *file_buffer = xmalloc (sizeof (FILE_BUFFER)); 105621495Sjmacd 105756160Sru file_buffer->filename = file_buffer->fullpath = NULL; 105856160Sru file_buffer->contents = NULL; 105956160Sru file_buffer->tags = NULL; 106056160Sru file_buffer->subfiles = NULL; 106121495Sjmacd file_buffer->tags_slots = 0; 106221495Sjmacd file_buffer->flags = 0; 106321495Sjmacd 106456160Sru return file_buffer; 106521495Sjmacd} 106621495Sjmacd 106721495Sjmacd/* Add FILE_BUFFER to our list of already loaded info files. */ 106821495Sjmacdstatic void 1069146515Sruremember_info_file (FILE_BUFFER *file_buffer) 107021495Sjmacd{ 107121495Sjmacd int i; 107221495Sjmacd 107321495Sjmacd for (i = 0; info_loaded_files && info_loaded_files[i]; i++) 107421495Sjmacd ; 107521495Sjmacd 107621495Sjmacd add_pointer_to_array (file_buffer, i, info_loaded_files, 107742660Smarkm info_loaded_files_slots, 10, FILE_BUFFER *); 107821495Sjmacd} 107921495Sjmacd 108021495Sjmacd/* Forget the contents, tags table, nodes list, and names of FILENAME. */ 108121495Sjmacdstatic void 1082146515Sruforget_info_file (char *filename) 108321495Sjmacd{ 108456160Sru int i; 108521495Sjmacd FILE_BUFFER *file_buffer; 108621495Sjmacd 108721495Sjmacd if (!info_loaded_files) 108821495Sjmacd return; 108921495Sjmacd 1090116525Sru for (i = 0; (file_buffer = info_loaded_files[i]); i++) 109156160Sru if (FILENAME_CMP (filename, file_buffer->filename) == 0 109256160Sru || FILENAME_CMP (filename, file_buffer->fullpath) == 0) 109321495Sjmacd { 109442660Smarkm free (file_buffer->filename); 109542660Smarkm free (file_buffer->fullpath); 109621495Sjmacd 109742660Smarkm if (file_buffer->contents) 109842660Smarkm free (file_buffer->contents); 1099116525Sru 110056160Sru /* free_file_buffer_tags () also kills the subfiles list, since 110156160Sru the subfiles list is only of use in conjunction with tags. */ 110242660Smarkm free_file_buffer_tags (file_buffer); 110321495Sjmacd 110456160Sru /* Move rest of list down. */ 110556160Sru while (info_loaded_files[i + 1]) 110656160Sru { 110756160Sru info_loaded_files[i] = info_loaded_files[i + 1]; 110856160Sru i++; 110956160Sru } 111056160Sru info_loaded_files[i] = 0; 111121495Sjmacd 111242660Smarkm break; 111321495Sjmacd } 111421495Sjmacd} 111521495Sjmacd 111621495Sjmacd/* Free the tags (if any) associated with FILE_BUFFER. */ 111721495Sjmacdstatic void 1118146515Srufree_file_buffer_tags (FILE_BUFFER *file_buffer) 111921495Sjmacd{ 112056160Sru int i; 112121495Sjmacd 112221495Sjmacd if (file_buffer->tags) 112321495Sjmacd { 112456160Sru TAG *tag; 112521495Sjmacd 112642660Smarkm for (i = 0; (tag = file_buffer->tags[i]); i++) 112742660Smarkm free_info_tag (tag); 112821495Sjmacd 112921495Sjmacd free (file_buffer->tags); 113056160Sru file_buffer->tags = NULL; 113121495Sjmacd file_buffer->tags_slots = 0; 113221495Sjmacd } 113321495Sjmacd 113421495Sjmacd if (file_buffer->subfiles) 113521495Sjmacd { 113621495Sjmacd for (i = 0; file_buffer->subfiles[i]; i++) 113742660Smarkm free (file_buffer->subfiles[i]); 113821495Sjmacd 113921495Sjmacd free (file_buffer->subfiles); 114056160Sru file_buffer->subfiles = NULL; 114121495Sjmacd } 114221495Sjmacd} 114321495Sjmacd 114421495Sjmacd/* Free the data associated with TAG, as well as TAG itself. */ 114521495Sjmacdstatic void 1146146515Srufree_info_tag (TAG *tag) 114721495Sjmacd{ 114821495Sjmacd free (tag->nodename); 114921495Sjmacd 115021495Sjmacd /* We don't free tag->filename, because that filename is part of the 115121495Sjmacd subfiles list for the containing FILE_BUFFER. free_info_tags () 115221495Sjmacd will free the subfiles when it is appropriate. */ 115321495Sjmacd 115421495Sjmacd free (tag); 115521495Sjmacd} 115621495Sjmacd 115721495Sjmacd/* Load the contents of FILE_BUFFER->contents. This function is called 115821495Sjmacd when a file buffer was loaded, and then in order to conserve memory, the 115921495Sjmacd file buffer's contents were freed and the pointer was zero'ed. Note that 116021495Sjmacd the file was already loaded at least once successfully, so the tags and/or 116121495Sjmacd nodes members are still correctly filled. */ 116221495Sjmacdstatic void 1163146515Sruinfo_reload_file_buffer_contents (FILE_BUFFER *fb) 116421495Sjmacd{ 116556160Sru int is_compressed; 116621495Sjmacd 116721495Sjmacd#if defined (HANDLE_MAN_PAGES) 116821495Sjmacd /* If this is the magic manpage node, don't try to reload, just give up. */ 116921495Sjmacd if (fb->flags & N_IsManPage) 117021495Sjmacd return; 117121495Sjmacd#endif 117221495Sjmacd 117321495Sjmacd fb->flags &= ~N_IsCompressed; 117421495Sjmacd 117521495Sjmacd /* Let the filesystem do all the work for us. */ 117621495Sjmacd fb->contents = 117756160Sru filesys_read_info_file (fb->fullpath, &(fb->filesize), &(fb->finfo), 117893139Sru &is_compressed); 117956160Sru if (is_compressed) 118021495Sjmacd fb->flags |= N_IsCompressed; 118121495Sjmacd} 118221495Sjmacd 118321495Sjmacd/* Return the actual starting memory location of NODE, side-effecting 118421495Sjmacd NODE->contents. MIN and MAX are bounds for a search if one is necessary. 118521495Sjmacd Because of the way that tags are implemented, the physical nodestart may 118621495Sjmacd not actually be where the tag says it is. If that is the case, but the 118721495Sjmacd node was found anyway, set N_UpdateTags in NODE->flags. If the node is 118821495Sjmacd found, return non-zero. NODE->contents is returned positioned right after 118921495Sjmacd the node separator that precedes this node, while the return value is 119021495Sjmacd position directly on the separator that precedes this node. If the node 119121495Sjmacd could not be found, return a NULL pointer. */ 119221495Sjmacdstatic char * 1193146515Sruadjust_nodestart (NODE *node, int min, int max) 119421495Sjmacd{ 119521495Sjmacd long position; 119621495Sjmacd SEARCH_BINDING node_body; 119721495Sjmacd 119821495Sjmacd /* Define the node body. */ 119921495Sjmacd node_body.buffer = node->contents; 120021495Sjmacd node_body.start = 0; 120121495Sjmacd node_body.end = max; 120221495Sjmacd node_body.flags = 0; 120321495Sjmacd 120421495Sjmacd /* Try the optimal case first. Who knows? This file may actually be 120521495Sjmacd formatted (mostly) correctly. */ 120621495Sjmacd if (node_body.buffer[0] != INFO_COOKIE && min > 2) 120721495Sjmacd node_body.buffer -= 3; 120821495Sjmacd 120921495Sjmacd position = find_node_separator (&node_body); 121021495Sjmacd 121121495Sjmacd /* If we found a node start, then check it out. */ 121221495Sjmacd if (position != -1) 121321495Sjmacd { 121421495Sjmacd int sep_len; 121521495Sjmacd 121621495Sjmacd sep_len = skip_node_separator (node->contents); 121721495Sjmacd 121821495Sjmacd /* If we managed to skip a node separator, then check for this node 121942660Smarkm being the right one. */ 122021495Sjmacd if (sep_len != 0) 122142660Smarkm { 122242660Smarkm char *nodedef, *nodestart; 122342660Smarkm int offset; 122421495Sjmacd 122542660Smarkm nodestart = node_body.buffer + position + sep_len; 122642660Smarkm nodedef = nodestart; 122742660Smarkm offset = string_in_line (INFO_NODE_LABEL, nodedef); 122821495Sjmacd 122942660Smarkm if (offset != -1) 123042660Smarkm { 123142660Smarkm nodedef += offset; 123242660Smarkm nodedef += skip_whitespace (nodedef); 123342660Smarkm offset = skip_node_characters (nodedef, DONT_SKIP_NEWLINES); 1234146515Sru if (((unsigned int) offset == strlen (node->nodename)) && 123542660Smarkm (strncmp (node->nodename, nodedef, offset) == 0)) 123642660Smarkm { 123742660Smarkm node->contents = nodestart; 123856160Sru return node_body.buffer + position; 123942660Smarkm } 124042660Smarkm } 124142660Smarkm } 124221495Sjmacd } 124321495Sjmacd 124421495Sjmacd /* Oh well, I guess we have to try to find it in a larger area. */ 124521495Sjmacd node_body.buffer = node->contents - min; 124621495Sjmacd node_body.start = 0; 124721495Sjmacd node_body.end = min + max; 124821495Sjmacd node_body.flags = 0; 124921495Sjmacd 125021495Sjmacd position = find_node_in_binding (node->nodename, &node_body); 125121495Sjmacd 125221495Sjmacd /* If the node couldn't be found, we lose big. */ 125321495Sjmacd if (position == -1) 125456160Sru return NULL; 125521495Sjmacd 125621495Sjmacd /* Otherwise, the node was found, but the tags table could need updating 125721495Sjmacd (if we used a tag to get here, that is). Set the flag in NODE->flags. */ 125821495Sjmacd node->contents = node_body.buffer + position; 125921495Sjmacd node->contents += skip_node_separator (node->contents); 126021495Sjmacd if (node->flags & N_HasTagsTable) 126121495Sjmacd node->flags |= N_UpdateTags; 126256160Sru return node_body.buffer + position; 126321495Sjmacd} 1264