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