1 /* nodes.c -- How to get an Info file and node. */
3 /* This file is part of GNU Info, a program for reading online documentation
6 Copyright (C) 1993 Free Software Foundation, Inc.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2, or (at your option)
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 Written by Brian Fox (bfox@ai.mit.edu). */
26 #include <sys/types.h>
27 #if defined (HAVE_SYS_FILE_H)
29 #endif /* HAVE_SYS_FILE_H */
30 #include <sys/errno.h>
32 #if defined (HAVE_STRING_H)
34 #endif /* HAVE_STRING_H */
38 #include "info-utils.h"
40 #if defined (HANDLE_MAN_PAGES)
42 #endif /* HANDLE_MAN_PAGES */
44 #if !defined (O_RDONLY)
45 #if defined (HAVE_SYS_FCNTL_H)
46 #include <sys/fcntl.h>
47 #else /* !HAVE_SYS_FCNTL_H */
49 #endif /* !HAVE_SYS_FCNTL_H */
50 #endif /* !O_RDONLY */
56 /* **************************************************************** */
58 /* Functions Static to this File */
60 /* **************************************************************** */
62 static void forget_info_file (), remember_info_file ();
63 static void free_file_buffer_tags (), free_info_tag ();
64 static void get_nodes_of_tags_table (), get_nodes_of_info_file ();
65 static void get_tags_of_indirect_tags_table ();
66 static void info_reload_file_buffer_contents ();
67 static char *adjust_nodestart ();
68 static FILE_BUFFER *info_load_file_internal (), *info_find_file_internal ();
69 static NODE *info_node_of_file_buffer_tags ();
71 static long get_node_length ();
73 /* Magic number that RMS used to decide how much a tags table pointer could
74 be off by. I feel that it should be much smaller, like on the order of
76 #define DEFAULT_INFO_FUDGE 1000
78 /* Passed to *_internal functions. INFO_GET_TAGS says to do what is
79 neccessary to fill in the nodes or tags arrays in FILE_BUFFER. */
80 #define INFO_NO_TAGS 0
81 #define INFO_GET_TAGS 1
83 /* **************************************************************** */
85 /* Global Variables */
87 /* **************************************************************** */
89 /* When non-zero, this is a string describing the recent file error. */
90 char *info_recent_file_error = (char *)NULL;
92 /* The list of already loaded nodes. */
93 FILE_BUFFER **info_loaded_files = (FILE_BUFFER **)NULL;
95 /* The number of slots currently allocated to LOADED_FILES. */
96 int info_loaded_files_slots = 0;
98 /* **************************************************************** */
100 /* Public Functions for Node Manipulation */
102 /* **************************************************************** */
104 /* Used to build "dir" menu from "localdir" files found in INFOPATH. */
105 extern void maybe_build_dir_node ();
107 /* Return a pointer to a NODE structure for the Info node (FILENAME)NODENAME.
108 FILENAME can be passed as NULL, in which case the filename of "dir" is used.
109 NODENAME can be passed as NULL, in which case the nodename of "Top" is used.
110 If the node cannot be found, return a NULL pointer. */
112 info_get_node (filename, nodename)
113 char *filename, *nodename;
115 FILE_BUFFER *file_buffer;
118 file_buffer = (FILE_BUFFER *)NULL;
119 info_recent_file_error = (char *)NULL;
121 info_parse_node (nodename, DONT_SKIP_NEWLINES);
122 nodename = (char *)NULL;
124 if (info_parsed_filename)
125 filename = info_parsed_filename;
127 if (info_parsed_nodename)
128 nodename = info_parsed_nodename;
130 /* If FILENAME is not specified, it defaults to "dir". */
134 /* If the file to be looked up is "dir", build the contents from all of
135 the "dir"s and "localdir"s found in INFOPATH. */
136 if (strcasecmp (filename, "dir") == 0)
137 maybe_build_dir_node (filename);
139 /* Find the correct info file. */
140 file_buffer = info_find_file (filename);
144 if (filesys_error_number)
145 info_recent_file_error =
146 filesys_error_string (filename, filesys_error_number);
147 return ((NODE *)NULL);
150 node = info_get_node_of_file_buffer (nodename, file_buffer);
151 /* If the node looked for was "Top", try again looking for the node under
152 a slightly different name. */
153 if (!node && (nodename == NULL || strcasecmp (nodename, "Top") == 0))
155 node = info_get_node_of_file_buffer ("Top", file_buffer);
157 node = info_get_node_of_file_buffer ("top", file_buffer);
159 node = info_get_node_of_file_buffer ("TOP", file_buffer);
164 /* Return a pointer to a NODE structure for the Info node NODENAME in
165 FILE_BUFFER. NODENAME can be passed as NULL, in which case the
166 nodename of "Top" is used. If the node cannot be found, return a
169 info_get_node_of_file_buffer (nodename, file_buffer)
171 FILE_BUFFER *file_buffer;
173 NODE *node = (NODE *)NULL;
175 /* If we are unable to find the file, we have to give up. There isn't
176 anything else we can do. */
178 return ((NODE *)NULL);
180 /* If the file buffer was gc'ed, reload the contents now. */
181 if (!file_buffer->contents)
182 info_reload_file_buffer_contents (file_buffer);
184 /* If NODENAME is not specified, it defaults to "Top". */
188 /* If the name of the node that we wish to find is exactly "*", then the
189 node body is the contents of the entire file. Create and return such
191 if (strcmp (nodename, "*") == 0)
193 node = (NODE *)xmalloc (sizeof (NODE));
194 node->filename = file_buffer->fullpath;
195 node->parent = (char *)NULL;
196 node->nodename = strdup ("*");
197 node->contents = file_buffer->contents;
198 node->nodelen = file_buffer->filesize;
201 #if defined (HANDLE_MAN_PAGES)
202 /* If the file buffer is the magic one associated with manpages, call
203 the manpage node finding function instead. */
204 else if (file_buffer->flags & N_IsManPage)
206 node = get_manpage_node (file_buffer, nodename);
208 #endif /* HANDLE_MAN_PAGES */
209 /* If this is the "main" info file, it might contain a tags table. Search
210 the tags table for an entry which matches the node that we want. If
211 there is a tags table, get the file which contains this node, but don't
212 bother building a node list for it. */
213 else if (file_buffer->tags)
215 node = info_node_of_file_buffer_tags (file_buffer, nodename);
218 /* Return the results of our node search. */
222 /* Locate the file named by FILENAME, and return the information structure
223 describing this file. The file may appear in our list of loaded files
224 already, or it may not. If it does not already appear, find the file,
225 and add it to the list of loaded files. If the file cannot be found,
226 return a NULL FILE_BUFFER *. */
228 info_find_file (filename)
231 return (info_find_file_internal (filename, INFO_GET_TAGS));
234 /* Load the info file FILENAME, remembering information about it in a
237 info_load_file (filename)
240 return (info_load_file_internal (filename, INFO_GET_TAGS));
244 /* **************************************************************** */
246 /* Private Functions Implementation */
248 /* **************************************************************** */
250 /* The workhorse for info_find_file (). Non-zero 2nd argument says to
251 try to build a tags table (or otherwise glean the nodes) for this
252 file once found. By default, we build the tags table, but when this
253 function is called by info_get_node () when we already have a valid
254 tags table describing the nodes, it is unnecessary. */
256 info_find_file_internal (filename, get_tags)
261 register FILE_BUFFER *file_buffer;
263 /* First try to find the file in our list of already loaded files. */
264 if (info_loaded_files)
266 for (i = 0; file_buffer = info_loaded_files[i]; i++)
267 if ((strcmp (filename, file_buffer->filename) == 0) ||
268 (strcmp (filename, file_buffer->fullpath) == 0) ||
269 ((*filename != '/') &&
271 filename_non_directory (file_buffer->fullpath)) == 0))
273 struct stat new_info, *old_info;
275 /* This file is loaded. If the filename that we want is
276 specifically "dir", then simply return the file buffer. */
277 if (strcasecmp (filename_non_directory (filename), "dir") == 0)
278 return (file_buffer);
280 #if defined (HANDLE_MAN_PAGES)
281 /* Do the same for the magic MANPAGE file. */
282 if (file_buffer->flags & N_IsManPage)
283 return (file_buffer);
284 #endif /* HANDLE_MAN_PAGES */
286 /* The file appears to be already loaded, and it is not "dir".
287 Check to see if it has changed since the last time it was
289 if (stat (file_buffer->fullpath, &new_info) == -1)
291 filesys_error_number = errno;
292 return ((FILE_BUFFER *)NULL);
295 old_info = &file_buffer->finfo;
297 if ((new_info.st_size != old_info->st_size) ||
298 (new_info.st_mtime != old_info->st_mtime))
300 /* The file has changed. Forget that we ever had loaded it
301 in the first place. */
302 forget_info_file (filename);
307 /* The info file exists, and has not changed since the last
308 time it was loaded. If the caller requested a nodes list
309 for this file, and there isn't one here, build the nodes
310 for this file_buffer. In any case, return the file_buffer
312 if (get_tags && !file_buffer->tags)
313 build_tags_and_nodes (file_buffer);
315 return (file_buffer);
320 /* The file wasn't loaded. Try to load it now. */
321 #if defined (HANDLE_MAN_PAGES)
322 /* If the name of the file that we want is our special file buffer for
323 Unix manual pages, then create the file buffer, and return it now. */
324 if (strcasecmp (filename, MANPAGE_FILE_BUFFER_NAME) == 0)
325 file_buffer = create_manpage_file_buffer ();
327 #endif /* HANDLE_MAN_PAGES */
328 file_buffer = info_load_file_internal (filename, get_tags);
330 /* If the file was loaded, remember the name under which it was found. */
332 remember_info_file (file_buffer);
334 return (file_buffer);
337 /* The workhorse function for info_load_file (). Non-zero second argument
338 says to build a list of tags (or nodes) for this file. This is the
339 default behaviour when info_load_file () is called, but it is not
340 necessary when loading a subfile for which we already have tags. */
342 info_load_file_internal (filename, get_tags)
346 char *fullpath, *contents;
350 FILE_BUFFER *file_buffer = (FILE_BUFFER *)NULL;
352 /* Get the full pathname of this file, as known by the info system.
353 That is to say, search along INFOPATH and expand tildes, etc. */
354 fullpath = info_find_fullpath (filename);
356 /* Did we actually find the file? */
357 retcode = stat (fullpath, &finfo);
359 /* If the file referenced by the name returned from info_find_fullpath ()
360 doesn't exist, then try again with the last part of the filename
361 appearing in lowercase. */
367 lowered_name = strdup (filename);
368 basename = (char *) strrchr (lowered_name, '/');
373 basename = lowered_name;
377 if (isupper (*basename))
378 *basename = tolower (*basename);
383 fullpath = info_find_fullpath (lowered_name);
386 retcode = stat (fullpath, &finfo);
389 /* If the file wasn't found, give up, returning a NULL pointer. */
392 filesys_error_number = errno;
393 return ((FILE_BUFFER *)NULL);
396 /* Otherwise, try to load the file. */
397 contents = filesys_read_info_file (fullpath, &filesize, &finfo);
400 return ((FILE_BUFFER *)NULL);
402 /* The file was found, and can be read. Allocate FILE_BUFFER and fill
403 in the various members. */
404 file_buffer = make_file_buffer ();
405 file_buffer->filename = strdup (filename);
406 file_buffer->fullpath = strdup (fullpath);
407 file_buffer->finfo = finfo;
408 file_buffer->filesize = filesize;
409 file_buffer->contents = contents;
410 if (file_buffer->filesize != file_buffer->finfo.st_size)
411 file_buffer->flags |= N_IsCompressed;
413 /* If requested, build the tags and nodes for this file buffer. */
415 build_tags_and_nodes (file_buffer);
417 return (file_buffer);
420 /* Grovel FILE_BUFFER->contents finding tags and nodes, and filling in the
421 various slots. This can also be used to rebuild a tag or node table. */
423 build_tags_and_nodes (file_buffer)
424 FILE_BUFFER *file_buffer;
426 SEARCH_BINDING binding;
429 free_file_buffer_tags (file_buffer);
430 file_buffer->flags &= ~N_HasTagsTable;
432 /* See if there is a tags table in this info file. */
433 binding.buffer = file_buffer->contents;
434 binding.start = file_buffer->filesize;
435 binding.end = binding.start - 1000;
438 binding.flags = S_FoldCase;
440 position = search_backward (TAGS_TABLE_END_LABEL, &binding);
442 /* If there is a tag table, find the start of it, and grovel over it
443 extracting tag information. */
447 long tags_table_begin, tags_table_end;
449 binding.end = position;
450 binding.start = binding.end - 5 - strlen (TAGS_TABLE_END_LABEL);
451 if (binding.start < 0)
454 position = find_node_separator (&binding);
456 /* For this test, (and all others here) failure indicates a bogus
457 tags table. Grovel the file. */
461 /* Remember the end of the tags table. */
462 binding.start = position;
463 tags_table_end = binding.start;
466 /* Locate the start of the tags table. */
467 position = search_backward (TAGS_TABLE_BEG_LABEL, &binding);
472 binding.end = position;
473 binding.start = binding.end - 5 - strlen (TAGS_TABLE_BEG_LABEL);
474 position = find_node_separator (&binding);
479 /* The file contains a valid tags table. Fill the FILE_BUFFER's
481 file_buffer->flags |= N_HasTagsTable;
482 tags_table_begin = position;
484 /* If this isn't an indirect tags table, just remember the nodes
485 described locally in this tags table. Note that binding.end
486 is pointing to just after the beginning label. */
487 binding.start = binding.end;
488 binding.end = file_buffer->filesize;
490 if (!looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, &binding))
492 binding.start = tags_table_begin;
493 binding.end = tags_table_end;
494 get_nodes_of_tags_table (file_buffer, &binding);
499 /* This is an indirect tags table. Build TAGS member. */
500 SEARCH_BINDING indirect;
502 indirect.start = tags_table_begin;
504 indirect.buffer = binding.buffer;
505 indirect.flags = S_FoldCase;
507 position = search_backward (INDIRECT_TAGS_TABLE_LABEL, &indirect);
511 /* This file is malformed. Give up. */
515 indirect.start = position;
516 indirect.end = tags_table_begin;
517 binding.start = tags_table_begin;
518 binding.end = tags_table_end;
519 get_tags_of_indirect_tags_table (file_buffer, &indirect, &binding);
524 /* This file doesn't contain any kind of tags table. Grovel the
525 file and build node entries for it. */
526 get_nodes_of_info_file (file_buffer);
529 /* Search through FILE_BUFFER->contents building an array of TAG *,
530 one entry per each node present in the file. Store the tags in
531 FILE_BUFFER->tags, and the number of allocated slots in
532 FILE_BUFFER->tags_slots. */
534 get_nodes_of_info_file (file_buffer)
535 FILE_BUFFER *file_buffer;
539 SEARCH_BINDING binding;
541 binding.buffer = file_buffer->contents;
543 binding.end = file_buffer->filesize;
544 binding.flags = S_FoldCase;
546 while ((nodestart = find_node_separator (&binding)) != -1)
552 /* Skip past the characters just found. */
553 binding.start = nodestart;
554 binding.start += skip_node_separator (binding.buffer + binding.start);
556 /* Move to the start of the line defining the node. */
557 nodeline = binding.buffer + binding.start;
560 start = string_in_line (INFO_NODE_LABEL, nodeline);
562 /* If not there, this is not the start of a node. */
566 /* Find the start of the nodename. */
567 start += skip_whitespace (nodeline + start);
569 /* Find the end of the nodename. */
571 skip_node_characters (nodeline + start, DONT_SKIP_NEWLINES);
573 /* Okay, we have isolated the node name, and we know where the
574 node starts. Remember this information in a NODE structure. */
575 entry = (TAG *)xmalloc (sizeof (TAG));
576 entry->nodename = (char *)xmalloc (1 + (end - start));
577 strncpy (entry->nodename, nodeline + start, end - start);
578 entry->nodename[end - start] = '\0';
579 entry->nodestart = nodestart;
581 SEARCH_BINDING node_body;
583 node_body.buffer = binding.buffer + binding.start;
585 node_body.end = binding.end - binding.start;
586 node_body.flags = S_FoldCase;
587 entry->nodelen = get_node_length (&node_body);
590 entry->filename = file_buffer->fullpath;
592 /* Add this tag to the array of tag structures in this FILE_BUFFER. */
593 add_pointer_to_array (entry, tags_index, file_buffer->tags,
594 file_buffer->tags_slots, 100, TAG *);
598 /* Return the length of the node which starts at BINDING. */
600 get_node_length (binding)
601 SEARCH_BINDING *binding;
606 /* From the Info-RFC file:
607 [A node] ends with either a ^_, a ^L, or the end of file. */
608 for (i = binding->start, body = binding->buffer; i < binding->end; i++)
610 if (body[i] == INFO_FF || body[i] == INFO_COOKIE)
613 return ((long) i - binding->start);
616 /* Build and save the array of nodes in FILE_BUFFER by searching through the
617 contents of BUFFER_BINDING for a tags table, and groveling the contents. */
619 get_nodes_of_tags_table (file_buffer, buffer_binding)
620 FILE_BUFFER *file_buffer;
621 SEARCH_BINDING *buffer_binding;
623 int offset, tags_index = 0;
624 SEARCH_BINDING *search;
627 search = copy_binding (buffer_binding);
629 /* Find the start of the tags table. */
630 position = find_tags_table (search);
632 /* If none, we're all done. */
636 /* Move to one character before the start of the actual table. */
637 search->start = position;
638 search->start += skip_node_separator (search->buffer + search->start);
639 search->start += strlen (TAGS_TABLE_BEG_LABEL);
642 /* The tag table consists of lines containing node names and positions.
643 Do each line until we find one that doesn't contain a node name. */
644 while ((position = search_forward ("\n", search)) != -1)
649 /* Prepare to skip this line. */
650 search->start = position;
653 /* Skip past informative "(Indirect)" tags table line. */
654 if (!tags_index && looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, search))
657 /* Find the label preceding the node name. */
659 string_in_line (INFO_NODE_LABEL, search->buffer + search->start);
661 /* If not there, not a defining line, so we must be out of the
666 /* Point to the beginning of the node definition. */
667 search->start += offset;
668 nodedef = search->buffer + search->start;
669 nodedef += skip_whitespace (nodedef);
671 /* Move past the node's name. */
673 (nodedef[offset]) && (nodedef[offset] != INFO_TAGSEP);
676 if (nodedef[offset] != INFO_TAGSEP)
679 entry = (TAG *)xmalloc (sizeof (TAG));
680 entry->nodename = (char *)xmalloc (1 + offset);
681 strncpy (entry->nodename, nodedef, offset);
682 entry->nodename[offset] = '\0';
684 entry->nodestart = (long) atol (nodedef + offset);
686 /* We don't know the length of this node yet. */
689 /* The filename of this node is currently known as the same as the
690 name of this file. */
691 entry->filename = file_buffer->fullpath;
693 /* Add this node structure to the array of node structures in this
695 add_pointer_to_array (entry, tags_index, file_buffer->tags,
696 file_buffer->tags_slots, 100, TAG *);
701 /* A structure used only in get_tags_of_indirect_tags_table () to hold onto
702 an intermediate value. */
708 /* Remember in FILE_BUFFER the nodenames, subfilenames, and offsets within the
709 subfiles of every node which appears in TAGS_BINDING. The 2nd argument is
710 a binding surrounding the indirect files list. */
712 get_tags_of_indirect_tags_table (file_buffer, indirect_binding, tags_binding)
713 FILE_BUFFER *file_buffer;
714 SEARCH_BINDING *indirect_binding, *tags_binding;
717 SUBFILE **subfiles = (SUBFILE **)NULL;
718 int subfiles_index = 0, subfiles_slots = 0;
721 /* First get the list of tags from the tags table. Then lookup the
722 associated file in the indirect list for each tag, and update it. */
723 get_nodes_of_tags_table (file_buffer, tags_binding);
725 /* We have the list of tags in file_buffer->tags. Get the list of
726 subfiles from the indirect table. */
728 char *start, *end, *line;
731 start = indirect_binding->buffer + indirect_binding->start;
732 end = indirect_binding->buffer + indirect_binding->end;
739 colon = string_in_line (":", line);
744 subfile = (SUBFILE *)xmalloc (sizeof (SUBFILE));
745 subfile->filename = (char *)xmalloc (colon);
746 strncpy (subfile->filename, line, colon - 1);
747 subfile->filename[colon - 1] = '\0';
748 subfile->first_byte = (long) atol (line + colon);
751 (subfile, subfiles_index, subfiles, subfiles_slots, 10, SUBFILE *);
753 while (*line++ != '\n');
757 /* If we have successfully built the indirect files table, then
758 merge the information in the two tables. */
761 free_file_buffer_tags (file_buffer);
766 register int tags_index;
768 SEARCH_BINDING binding;
770 /* Find the length of the header of the file containing the indirect
771 tags table. This header appears at the start of every file. We
772 want the absolute position of each node within each subfile, so
773 we subtract the start of the containing subfile from the logical
774 position of the node, and then add the length of the header in. */
775 binding.buffer = file_buffer->contents;
777 binding.end = file_buffer->filesize;
778 binding.flags = S_FoldCase;
780 header_length = find_node_separator (&binding);
781 if (header_length == -1)
784 /* Build the file buffer's list of subfiles. */
786 char *containing_dir, *temp;
787 int len_containing_dir;
789 containing_dir = strdup (file_buffer->fullpath);
790 temp = (char *) strrchr (containing_dir, '/');
795 len_containing_dir = strlen (containing_dir);
797 for (i = 0; subfiles[i]; i++);
799 file_buffer->subfiles = (char **) xmalloc ((1 + i) * sizeof (char *));
801 for (i = 0; subfiles[i]; i++)
805 fullpath = (char *) xmalloc
806 (2 + strlen (subfiles[i]->filename) + len_containing_dir);
808 sprintf (fullpath, "%s/%s",
809 containing_dir, subfiles[i]->filename);
811 file_buffer->subfiles[i] = fullpath;
813 file_buffer->subfiles[i] = (char *)NULL;
814 free (containing_dir);
817 /* For each node in the file's tags table, remember the starting
820 entry = file_buffer->tags[tags_index];
824 subfiles[i] && entry->nodestart >= subfiles[i]->first_byte;
827 /* If the Info file containing the indirect tags table is
828 malformed, then give up. */
831 /* The Info file containing the indirect tags table is
832 malformed. Give up. */
833 for (i = 0; subfiles[i]; i++)
835 free (subfiles[i]->filename);
837 free (file_buffer->subfiles[i]);
839 file_buffer->subfiles = (char **)NULL;
840 free_file_buffer_tags (file_buffer);
844 /* SUBFILES[i] is the index of the first subfile whose logical
845 first byte is greater than the logical offset of this node's
846 starting position. This means that the subfile directly
847 preceding this one is the one containing the node. */
849 entry->filename = file_buffer->subfiles[i - 1];
850 entry->nodestart -= subfiles[i -1]->first_byte;
851 entry->nodestart += header_length;
855 /* We have successfully built the tags table. Remember that it
857 file_buffer->flags |= N_TagsIndirect;
860 /* Free the structures assigned to SUBFILES. Free the names as well
861 as the structures themselves, then finally, the array. */
862 for (i = 0; subfiles[i]; i++)
864 free (subfiles[i]->filename);
870 /* Return the node from FILE_BUFFER which matches NODENAME by searching
871 the tags table in FILE_BUFFER. If the node could not be found, return
874 info_node_of_file_buffer_tags (file_buffer, nodename)
875 FILE_BUFFER *file_buffer;
881 for (i = 0; tag = file_buffer->tags[i]; i++)
882 if (strcmp (nodename, tag->nodename) == 0)
884 FILE_BUFFER *subfile;
886 subfile = info_find_file_internal (tag->filename, INFO_NO_TAGS);
889 return ((NODE *)NULL);
891 if (!subfile->contents)
893 info_reload_file_buffer_contents (subfile);
895 if (!subfile->contents)
896 return ((NODE *)NULL);
899 /* If we were able to find this file and load it, then return
900 the node within it. */
904 node = (NODE *)xmalloc (sizeof (NODE));
905 node->filename = (subfile->fullpath);
906 node->nodename = tag->nodename;
907 node->contents = subfile->contents + tag->nodestart;
909 node->parent = (char *)NULL;
911 if (file_buffer->flags & N_HasTagsTable)
913 node->flags |= N_HasTagsTable;
915 if (file_buffer->flags & N_TagsIndirect)
917 node->flags |= N_TagsIndirect;
918 node->parent = file_buffer->fullpath;
922 if (subfile->flags & N_IsCompressed)
923 node->flags |= N_IsCompressed;
925 /* If TAG->nodelen hasn't been calculated yet, then we aren't
926 in a position to trust the entry pointer. Adjust things so
927 that ENTRY->nodestart gets the exact address of the start of
928 the node separator which starts this node, and NODE->contents
929 gets the address of the line defining this node. If we cannot
930 do that, the node isn't really here. */
931 if (tag->nodelen == -1)
935 SEARCH_BINDING node_body;
938 min = max = DEFAULT_INFO_FUDGE;
940 if (tag->nodestart < DEFAULT_INFO_FUDGE)
941 min = tag->nodestart;
943 if (DEFAULT_INFO_FUDGE >
944 (subfile->filesize - tag->nodestart))
945 max = subfile->filesize - tag->nodestart;
947 /* NODE_SEP gets the address of the separator which defines
948 this node, or (char *)NULL if the node wasn't found.
949 NODE->contents is side-effected to point to right after
951 node_sep = adjust_nodestart (node, min, max);
952 if (node_sep == (char *)NULL)
955 return ((NODE *)NULL);
957 /* Readjust tag->nodestart. */
958 tag->nodestart = node_sep - subfile->contents;
960 /* Calculate the length of the current node. */
961 buff_end = subfile->contents + subfile->filesize;
963 node_body.buffer = node->contents;
965 node_body.end = buff_end - node_body.buffer;
967 tag->nodelen = get_node_length (&node_body);
971 /* Since we know the length of this node, we have already
972 adjusted tag->nodestart to point to the exact start of
973 it. Simply skip the node separator. */
974 node->contents += skip_node_separator (node->contents);
977 node->nodelen = tag->nodelen;
982 /* There was a tag table for this file, and the node wasn't found.
983 Return NULL, since this file doesn't contain the desired node. */
984 return ((NODE *)NULL);
987 /* **************************************************************** */
989 /* Managing file_buffers, nodes, and tags. */
991 /* **************************************************************** */
993 /* Create a new, empty file buffer. */
997 FILE_BUFFER *file_buffer;
999 file_buffer = (FILE_BUFFER *)xmalloc (sizeof (FILE_BUFFER));
1000 file_buffer->filename = file_buffer->fullpath = (char *)NULL;
1001 file_buffer->contents = (char *)NULL;
1002 file_buffer->tags = (TAG **)NULL;
1003 file_buffer->subfiles = (char **)NULL;
1004 file_buffer->tags_slots = 0;
1005 file_buffer->flags = 0;
1007 return (file_buffer);
1010 /* Add FILE_BUFFER to our list of already loaded info files. */
1012 remember_info_file (file_buffer)
1013 FILE_BUFFER *file_buffer;
1017 for (i = 0; info_loaded_files && info_loaded_files[i]; i++)
1020 add_pointer_to_array (file_buffer, i, info_loaded_files,
1021 info_loaded_files_slots, 10, FILE_BUFFER *);
1024 /* Forget the contents, tags table, nodes list, and names of FILENAME. */
1026 forget_info_file (filename)
1030 FILE_BUFFER *file_buffer;
1032 if (!info_loaded_files)
1035 for (i = 0; file_buffer = info_loaded_files[i]; i++)
1036 if ((strcmp (filename, file_buffer->filename) == 0) ||
1037 (strcmp (filename, file_buffer->fullpath) == 0))
1039 free (file_buffer->filename);
1040 free (file_buffer->fullpath);
1042 if (file_buffer->contents)
1043 free (file_buffer->contents);
1045 /* Note that free_file_buffer_tags () also kills the subfiles
1046 list, since the subfiles list is only of use in conjunction
1048 free_file_buffer_tags (file_buffer);
1050 while (info_loaded_files[i] = info_loaded_files[++i])
1057 /* Free the tags (if any) associated with FILE_BUFFER. */
1059 free_file_buffer_tags (file_buffer)
1060 FILE_BUFFER *file_buffer;
1064 if (file_buffer->tags)
1068 for (i = 0; tag = file_buffer->tags[i]; i++)
1069 free_info_tag (tag);
1071 free (file_buffer->tags);
1072 file_buffer->tags = (TAG **)NULL;
1073 file_buffer->tags_slots = 0;
1076 if (file_buffer->subfiles)
1078 for (i = 0; file_buffer->subfiles[i]; i++)
1079 free (file_buffer->subfiles[i]);
1081 free (file_buffer->subfiles);
1082 file_buffer->subfiles = (char **)NULL;
1086 /* Free the data associated with TAG, as well as TAG itself. */
1091 free (tag->nodename);
1093 /* We don't free tag->filename, because that filename is part of the
1094 subfiles list for the containing FILE_BUFFER. free_info_tags ()
1095 will free the subfiles when it is appropriate. */
1100 /* Load the contents of FILE_BUFFER->contents. This function is called
1101 when a file buffer was loaded, and then in order to conserve memory, the
1102 file buffer's contents were freed and the pointer was zero'ed. Note that
1103 the file was already loaded at least once successfully, so the tags and/or
1104 nodes members are still correctly filled. */
1106 info_reload_file_buffer_contents (fb)
1110 #if defined (HANDLE_MAN_PAGES)
1111 /* If this is the magic manpage node, don't try to reload, just give up. */
1112 if (fb->flags & N_IsManPage)
1116 fb->flags &= ~N_IsCompressed;
1118 /* Let the filesystem do all the work for us. */
1120 filesys_read_info_file (fb->fullpath, &(fb->filesize), &(fb->finfo));
1121 if (fb->filesize != (long) (fb->finfo.st_size))
1122 fb->flags |= N_IsCompressed;
1125 /* Return the actual starting memory location of NODE, side-effecting
1126 NODE->contents. MIN and MAX are bounds for a search if one is necessary.
1127 Because of the way that tags are implemented, the physical nodestart may
1128 not actually be where the tag says it is. If that is the case, but the
1129 node was found anyway, set N_UpdateTags in NODE->flags. If the node is
1130 found, return non-zero. NODE->contents is returned positioned right after
1131 the node separator that precedes this node, while the return value is
1132 position directly on the separator that precedes this node. If the node
1133 could not be found, return a NULL pointer. */
1135 adjust_nodestart (node, min, max)
1140 SEARCH_BINDING node_body;
1142 /* Define the node body. */
1143 node_body.buffer = node->contents;
1144 node_body.start = 0;
1145 node_body.end = max;
1146 node_body.flags = 0;
1148 /* Try the optimal case first. Who knows? This file may actually be
1149 formatted (mostly) correctly. */
1150 if (node_body.buffer[0] != INFO_COOKIE && min > 2)
1151 node_body.buffer -= 3;
1153 position = find_node_separator (&node_body);
1155 /* If we found a node start, then check it out. */
1160 sep_len = skip_node_separator (node->contents);
1162 /* If we managed to skip a node separator, then check for this node
1163 being the right one. */
1166 char *nodedef, *nodestart;
1169 nodestart = node_body.buffer + position + sep_len;
1170 nodedef = nodestart;
1171 offset = string_in_line (INFO_NODE_LABEL, nodedef);
1176 nodedef += skip_whitespace (nodedef);
1177 offset = skip_node_characters (nodedef, DONT_SKIP_NEWLINES);
1178 if ((offset == strlen (node->nodename)) &&
1179 (strncmp (node->nodename, nodedef, offset) == 0))
1181 node->contents = nodestart;
1182 return (node_body.buffer + position);
1188 /* Oh well, I guess we have to try to find it in a larger area. */
1189 node_body.buffer = node->contents - min;
1190 node_body.start = 0;
1191 node_body.end = min + max;
1192 node_body.flags = 0;
1194 position = find_node_in_binding (node->nodename, &node_body);
1196 /* If the node couldn't be found, we lose big. */
1198 return ((char *)NULL);
1200 /* Otherwise, the node was found, but the tags table could need updating
1201 (if we used a tag to get here, that is). Set the flag in NODE->flags. */
1202 node->contents = node_body.buffer + position;
1203 node->contents += skip_node_separator (node->contents);
1204 if (node->flags & N_HasTagsTable)
1205 node->flags |= N_UpdateTags;
1206 return (node_body.buffer + position);