1 /* nodes.c -- how to get an Info file and node.
2 $Id: nodes.c,v 1.15 2000/11/11 00:40:37 karl Exp $
4 Copyright (C) 1993, 98, 99, 2000 Free Software Foundation, Inc.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 Written by Brian Fox (bfox@ai.mit.edu). */
27 #include "info-utils.h"
29 #if defined (HANDLE_MAN_PAGES)
31 #endif /* HANDLE_MAN_PAGES */
33 static void forget_info_file (), remember_info_file ();
34 static void free_file_buffer_tags (), free_info_tag ();
35 static void get_nodes_of_tags_table (), get_nodes_of_info_file ();
36 static void get_tags_of_indirect_tags_table ();
37 static void info_reload_file_buffer_contents ();
38 static char *adjust_nodestart ();
39 static FILE_BUFFER *info_load_file_internal (), *info_find_file_internal ();
40 static NODE *info_node_of_file_buffer_tags ();
42 static long get_node_length ();
44 /* Magic number that RMS used to decide how much a tags table pointer could
45 be off by. I feel that it should be much smaller, like 4. */
46 #define DEFAULT_INFO_FUDGE 1000
48 /* Passed to *_internal functions. INFO_GET_TAGS says to do what is
49 neccessary to fill in the nodes or tags arrays in FILE_BUFFER. */
50 #define INFO_NO_TAGS 0
51 #define INFO_GET_TAGS 1
53 /* Global variables. */
55 /* When non-zero, this is a string describing the recent file error. */
56 char *info_recent_file_error = NULL;
58 /* The list of already loaded nodes. */
59 FILE_BUFFER **info_loaded_files = NULL;
61 /* The number of slots currently allocated to LOADED_FILES. */
62 int info_loaded_files_slots = 0;
64 /* Public functions for node manipulation. */
66 /* Used to build `dir' menu from `localdir' files found in INFOPATH. */
67 extern void maybe_build_dir_node ();
69 /* Return a pointer to a NODE structure for the Info node (FILENAME)NODENAME.
70 If FILENAME is NULL, `dir' is used.
71 IF NODENAME is NULL, `Top' is used.
72 If the node cannot be found, return NULL. */
74 info_get_node (filename, nodename)
75 char *filename, *nodename;
78 FILE_BUFFER *file_buffer = NULL;
80 info_recent_file_error = NULL;
81 info_parse_node (nodename, DONT_SKIP_NEWLINES);
84 if (info_parsed_filename)
85 filename = info_parsed_filename;
87 if (info_parsed_nodename)
88 nodename = info_parsed_nodename;
90 /* If FILENAME is not specified, it defaults to "dir". */
94 /* If the file to be looked up is "dir", build the contents from all of
95 the "dir"s and "localdir"s found in INFOPATH. */
96 if (is_dir_name (filename))
97 maybe_build_dir_node (filename);
99 /* Find the correct info file, or give up. */
100 file_buffer = info_find_file (filename);
103 if (filesys_error_number)
104 info_recent_file_error =
105 filesys_error_string (filename, filesys_error_number);
109 /* Look for the node. */
110 node = info_get_node_of_file_buffer (nodename, file_buffer);
112 /* If the node not found was "Top", try again with different case. */
113 if (!node && (nodename == NULL || strcasecmp (nodename, "Top") == 0))
115 node = info_get_node_of_file_buffer ("Top", file_buffer);
117 node = info_get_node_of_file_buffer ("top", file_buffer);
119 node = info_get_node_of_file_buffer ("TOP", file_buffer);
125 /* Return a pointer to a NODE structure for the Info node NODENAME in
126 FILE_BUFFER. NODENAME can be passed as NULL, in which case the
127 nodename of "Top" is used. If the node cannot be found, return a
130 info_get_node_of_file_buffer (nodename, file_buffer)
132 FILE_BUFFER *file_buffer;
136 /* If we are unable to find the file, we have to give up. There isn't
137 anything else we can do. */
141 /* If the file buffer was gc'ed, reload the contents now. */
142 if (!file_buffer->contents)
143 info_reload_file_buffer_contents (file_buffer);
145 /* If NODENAME is not specified, it defaults to "Top". */
149 /* If the name of the node that we wish to find is exactly "*", then the
150 node body is the contents of the entire file. Create and return such
152 if (strcmp (nodename, "*") == 0)
154 node = (NODE *)xmalloc (sizeof (NODE));
155 node->filename = file_buffer->fullpath;
157 node->nodename = xstrdup ("*");
158 node->contents = file_buffer->contents;
159 node->nodelen = file_buffer->filesize;
161 node->display_pos = 0;
163 #if defined (HANDLE_MAN_PAGES)
164 /* If the file buffer is the magic one associated with manpages, call
165 the manpage node finding function instead. */
166 else if (file_buffer->flags & N_IsManPage)
168 node = get_manpage_node (file_buffer, nodename);
170 #endif /* HANDLE_MAN_PAGES */
171 /* If this is the "main" info file, it might contain a tags table. Search
172 the tags table for an entry which matches the node that we want. If
173 there is a tags table, get the file which contains this node, but don't
174 bother building a node list for it. */
175 else if (file_buffer->tags)
177 node = info_node_of_file_buffer_tags (file_buffer, nodename);
180 /* Return the results of our node search. */
184 /* Locate the file named by FILENAME, and return the information structure
185 describing this file. The file may appear in our list of loaded files
186 already, or it may not. If it does not already appear, find the file,
187 and add it to the list of loaded files. If the file cannot be found,
188 return a NULL FILE_BUFFER *. */
190 info_find_file (filename)
193 return info_find_file_internal (filename, INFO_GET_TAGS);
196 /* Load the info file FILENAME, remembering information about it in a
199 info_load_file (filename)
202 return info_load_file_internal (filename, INFO_GET_TAGS);
206 /* Private functions implementation. */
208 /* The workhorse for info_find_file (). Non-zero 2nd argument says to
209 try to build a tags table (or otherwise glean the nodes) for this
210 file once found. By default, we build the tags table, but when this
211 function is called by info_get_node () when we already have a valid
212 tags table describing the nodes, it is unnecessary. */
214 info_find_file_internal (filename, get_tags)
219 FILE_BUFFER *file_buffer;
221 /* First try to find the file in our list of already loaded files. */
222 if (info_loaded_files)
224 for (i = 0; (file_buffer = info_loaded_files[i]); i++)
225 if ((FILENAME_CMP (filename, file_buffer->filename) == 0)
226 || (FILENAME_CMP (filename, file_buffer->fullpath) == 0)
227 || (!IS_ABSOLUTE (filename)
228 && FILENAME_CMP (filename,
229 filename_non_directory (file_buffer->fullpath))
232 struct stat new_info, *old_info;
234 /* This file is loaded. If the filename that we want is
235 specifically "dir", then simply return the file buffer. */
236 if (is_dir_name (filename_non_directory (filename)))
239 #if defined (HANDLE_MAN_PAGES)
240 /* Do the same for the magic MANPAGE file. */
241 if (file_buffer->flags & N_IsManPage)
243 #endif /* HANDLE_MAN_PAGES */
245 /* The file appears to be already loaded, and is not "dir". Check
246 to see if it's changed since the last time it was loaded. */
247 if (stat (file_buffer->fullpath, &new_info) == -1)
249 filesys_error_number = errno;
253 old_info = &file_buffer->finfo;
255 if (new_info.st_size != old_info->st_size
256 || new_info.st_mtime != old_info->st_mtime)
258 /* The file has changed. Forget that we ever had loaded it
259 in the first place. */
260 forget_info_file (filename);
265 /* The info file exists, and has not changed since the last
266 time it was loaded. If the caller requested a nodes list
267 for this file, and there isn't one here, build the nodes
268 for this file_buffer. In any case, return the file_buffer
270 if (!file_buffer->contents)
272 /* The file's contents have been gc'ed. Reload it. */
273 info_reload_file_buffer_contents (file_buffer);
274 if (!file_buffer->contents)
278 if (get_tags && !file_buffer->tags)
279 build_tags_and_nodes (file_buffer);
286 /* The file wasn't loaded. Try to load it now. */
287 #if defined (HANDLE_MAN_PAGES)
288 /* If the name of the file that we want is our special file buffer for
289 Unix manual pages, then create the file buffer, and return it now. */
290 if (strcasecmp (filename, MANPAGE_FILE_BUFFER_NAME) == 0)
291 file_buffer = create_manpage_file_buffer ();
293 #endif /* HANDLE_MAN_PAGES */
294 file_buffer = info_load_file_internal (filename, get_tags);
296 /* If the file was loaded, remember the name under which it was found. */
298 remember_info_file (file_buffer);
303 /* The workhorse function for info_load_file (). Non-zero second argument
304 says to build a list of tags (or nodes) for this file. This is the
305 default behaviour when info_load_file () is called, but it is not
306 necessary when loading a subfile for which we already have tags. */
308 info_load_file_internal (filename, get_tags)
312 char *fullpath, *contents;
315 int retcode, compressed;
316 FILE_BUFFER *file_buffer = NULL;
318 /* Get the full pathname of this file, as known by the info system.
319 That is to say, search along INFOPATH and expand tildes, etc. */
320 fullpath = info_find_fullpath (filename);
322 /* Did we actually find the file? */
323 retcode = stat (fullpath, &finfo);
325 /* If the file referenced by the name returned from info_find_fullpath ()
326 doesn't exist, then try again with the last part of the filename
327 appearing in lowercase. */
328 /* This is probably not needed at all on those systems which define
329 FILENAME_CMP to be strcasecmp. But let's do it anyway, lest some
330 network redirector supports case sensitivity. */
336 lowered_name = xstrdup (filename);
337 basename = filename_non_directory (lowered_name);
341 if (isupper (*basename))
342 *basename = tolower (*basename);
347 fullpath = info_find_fullpath (lowered_name);
350 retcode = stat (fullpath, &finfo);
353 /* If the file wasn't found, give up, returning a NULL pointer. */
356 filesys_error_number = errno;
360 /* Otherwise, try to load the file. */
361 contents = filesys_read_info_file (fullpath, &filesize, &finfo, &compressed);
366 /* The file was found, and can be read. Allocate FILE_BUFFER and fill
367 in the various members. */
368 file_buffer = make_file_buffer ();
369 file_buffer->filename = xstrdup (filename);
370 file_buffer->fullpath = xstrdup (fullpath);
371 file_buffer->finfo = finfo;
372 file_buffer->filesize = filesize;
373 file_buffer->contents = contents;
375 file_buffer->flags |= N_IsCompressed;
377 /* If requested, build the tags and nodes for this file buffer. */
379 build_tags_and_nodes (file_buffer);
384 /* Grovel FILE_BUFFER->contents finding tags and nodes, and filling in the
385 various slots. This can also be used to rebuild a tag or node table. */
387 build_tags_and_nodes (file_buffer)
388 FILE_BUFFER *file_buffer;
390 SEARCH_BINDING binding;
393 free_file_buffer_tags (file_buffer);
394 file_buffer->flags &= ~N_HasTagsTable;
396 /* See if there is a tags table in this info file. */
397 binding.buffer = file_buffer->contents;
398 binding.start = file_buffer->filesize;
399 binding.end = binding.start - 1000;
402 binding.flags = S_FoldCase;
404 position = search_backward (TAGS_TABLE_END_LABEL, &binding);
406 /* If there is a tag table, find the start of it, and grovel over it
407 extracting tag information. */
411 long tags_table_begin, tags_table_end;
413 binding.end = position;
414 binding.start = binding.end - 5 - strlen (TAGS_TABLE_END_LABEL);
415 if (binding.start < 0)
418 position = find_node_separator (&binding);
420 /* For this test, (and all others here) failure indicates a bogus
421 tags table. Grovel the file. */
425 /* Remember the end of the tags table. */
426 binding.start = position;
427 tags_table_end = binding.start;
430 /* Locate the start of the tags table. */
431 position = search_backward (TAGS_TABLE_BEG_LABEL, &binding);
436 binding.end = position;
437 binding.start = binding.end - 5 - strlen (TAGS_TABLE_BEG_LABEL);
438 position = find_node_separator (&binding);
443 /* The file contains a valid tags table. Fill the FILE_BUFFER's
445 file_buffer->flags |= N_HasTagsTable;
446 tags_table_begin = position;
448 /* If this isn't an indirect tags table, just remember the nodes
449 described locally in this tags table. Note that binding.end
450 is pointing to just after the beginning label. */
451 binding.start = binding.end;
452 binding.end = file_buffer->filesize;
454 if (!looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, &binding))
456 binding.start = tags_table_begin;
457 binding.end = tags_table_end;
458 get_nodes_of_tags_table (file_buffer, &binding);
463 /* This is an indirect tags table. Build TAGS member. */
464 SEARCH_BINDING indirect;
466 indirect.start = tags_table_begin;
468 indirect.buffer = binding.buffer;
469 indirect.flags = S_FoldCase;
471 position = search_backward (INDIRECT_TAGS_TABLE_LABEL, &indirect);
475 /* This file is malformed. Give up. */
479 indirect.start = position;
480 indirect.end = tags_table_begin;
481 binding.start = tags_table_begin;
482 binding.end = tags_table_end;
483 get_tags_of_indirect_tags_table (file_buffer, &indirect, &binding);
488 /* This file doesn't contain any kind of tags table. Grovel the
489 file and build node entries for it. */
490 get_nodes_of_info_file (file_buffer);
493 /* Search through FILE_BUFFER->contents building an array of TAG *,
494 one entry per each node present in the file. Store the tags in
495 FILE_BUFFER->tags, and the number of allocated slots in
496 FILE_BUFFER->tags_slots. */
498 get_nodes_of_info_file (file_buffer)
499 FILE_BUFFER *file_buffer;
503 SEARCH_BINDING binding;
505 binding.buffer = file_buffer->contents;
507 binding.end = file_buffer->filesize;
508 binding.flags = S_FoldCase;
510 while ((nodestart = find_node_separator (&binding)) != -1)
517 /* Skip past the characters just found. */
518 binding.start = nodestart;
519 binding.start += skip_node_separator (binding.buffer + binding.start);
521 /* Move to the start of the line defining the node. */
522 nodeline = binding.buffer + binding.start;
525 start = string_in_line (INFO_NODE_LABEL, nodeline);
526 /* No Node:. Maybe it's a Ref:. */
529 start = string_in_line (INFO_REF_LABEL, nodeline);
534 /* If not there, this is not the start of a node. */
538 /* Find the start of the nodename. */
539 start += skip_whitespace (nodeline + start);
541 /* Find the end of the nodename. */
543 skip_node_characters (nodeline + start, DONT_SKIP_NEWLINES);
545 /* Okay, we have isolated the node name, and we know where the
546 node starts. Remember this information. */
547 entry = xmalloc (sizeof (TAG));
548 entry->nodename = xmalloc (1 + (end - start));
549 strncpy (entry->nodename, nodeline + start, end - start);
550 entry->nodename[end - start] = 0;
551 entry->nodestart = nodestart;
556 SEARCH_BINDING node_body;
558 node_body.buffer = binding.buffer + binding.start;
560 node_body.end = binding.end - binding.start;
561 node_body.flags = S_FoldCase;
562 entry->nodelen = get_node_length (&node_body);
565 entry->filename = file_buffer->fullpath;
567 /* Add this tag to the array of tag structures in this FILE_BUFFER. */
568 add_pointer_to_array (entry, tags_index, file_buffer->tags,
569 file_buffer->tags_slots, 100, TAG *);
573 /* Return the length of the node which starts at BINDING. */
575 get_node_length (binding)
576 SEARCH_BINDING *binding;
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++)
584 if (body[i] == INFO_FF || body[i] == INFO_COOKIE)
587 return i - binding->start;
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. */
593 get_nodes_of_tags_table (file_buffer, buffer_binding)
594 FILE_BUFFER *file_buffer;
595 SEARCH_BINDING *buffer_binding;
598 SEARCH_BINDING *search;
602 search = copy_binding (buffer_binding);
604 /* Find the start of the tags table. */
605 position = find_tags_table (search);
607 /* If none, we're all done. */
611 /* Move to one character before the start of the actual table. */
612 search->start = position;
613 search->start += skip_node_separator (search->buffer + search->start);
614 search->start += strlen (TAGS_TABLE_BEG_LABEL);
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", search)) != -1)
626 /* Prepare to skip this line. */
627 search->start = position;
630 /* Skip past informative "(Indirect)" tags table line. */
631 if (!tags_index && looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, search))
634 /* Find the label preceding the node name. */
636 string_in_line (INFO_NODE_LABEL, search->buffer + search->start);
638 /* If no node label, maybe it's an anchor. */
639 if (name_offset == -1)
641 name_offset = string_in_line (INFO_REF_LABEL,
642 search->buffer + search->start);
643 if (name_offset != -1)
647 /* If not there, not a defining line, so we must be out of the
649 if (name_offset == -1)
652 entry = xmalloc (sizeof (TAG));
654 /* Find the beginning of the node definition. */
655 search->start += name_offset;
656 nodedef = search->buffer + search->start;
657 nodedef += skip_whitespace (nodedef);
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++)
662 if (nodedef[p] != INFO_TAGSEP)
665 entry->nodename = xmalloc (p + 1);
666 strncpy (entry->nodename, nodedef, p);
667 entry->nodename[p] = 0;
669 entry->nodestart = atol (nodedef + p);
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;
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;
679 /* Add this node structure to the array of node structures in this
681 add_pointer_to_array (entry, tags_index, file_buffer->tags,
682 file_buffer->tags_slots, 100, TAG *);
687 /* A structure used only in `get_tags_of_indirect_tags_table' to hold onto
688 an intermediate value. */
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. */
698 get_tags_of_indirect_tags_table (file_buffer, indirect_binding, tags_binding)
699 FILE_BUFFER *file_buffer;
700 SEARCH_BINDING *indirect_binding, *tags_binding;
703 SUBFILE **subfiles = NULL;
704 int subfiles_index = 0, subfiles_slots = 0;
707 /* First get the list of tags from the tags table. Then lookup the
708 associated file in the indirect list for each tag, and update it. */
709 get_nodes_of_tags_table (file_buffer, tags_binding);
711 /* We have the list of tags in file_buffer->tags. Get the list of
712 subfiles from the indirect table. */
714 char *start, *end, *line;
717 start = indirect_binding->buffer + indirect_binding->start;
718 end = indirect_binding->buffer + indirect_binding->end;
725 colon = string_in_line (":", line);
730 subfile = (SUBFILE *)xmalloc (sizeof (SUBFILE));
731 subfile->filename = (char *)xmalloc (colon);
732 strncpy (subfile->filename, line, colon - 1);
733 subfile->filename[colon - 1] = 0;
734 subfile->first_byte = (long) atol (line + colon);
737 (subfile, subfiles_index, subfiles, subfiles_slots, 10, SUBFILE *);
739 while (*line++ != '\n');
743 /* If we have successfully built the indirect files table, then
744 merge the information in the two tables. */
747 free_file_buffer_tags (file_buffer);
754 SEARCH_BINDING binding;
756 /* Find the length of the header of the file containing the indirect
757 tags table. This header appears at the start of every file. We
758 want the absolute position of each node within each subfile, so
759 we subtract the start of the containing subfile from the logical
760 position of the node, and then add the length of the header in. */
761 binding.buffer = file_buffer->contents;
763 binding.end = file_buffer->filesize;
764 binding.flags = S_FoldCase;
766 header_length = find_node_separator (&binding);
767 if (header_length == -1)
770 /* Build the file buffer's list of subfiles. */
772 char *containing_dir = xstrdup (file_buffer->fullpath);
773 char *temp = filename_non_directory (containing_dir);
774 int len_containing_dir;
776 if (temp > containing_dir)
778 if (HAVE_DRIVE (file_buffer->fullpath) &&
779 temp == containing_dir + 2)
781 /* Avoid converting "d:foo" into "d:/foo" below. */
788 len_containing_dir = strlen (containing_dir);
790 for (i = 0; subfiles[i]; i++);
792 file_buffer->subfiles = (char **) xmalloc ((1 + i) * sizeof (char *));
794 for (i = 0; subfiles[i]; i++)
798 fullpath = (char *) xmalloc
799 (2 + strlen (subfiles[i]->filename) + len_containing_dir);
801 sprintf (fullpath, "%s/%s",
802 containing_dir, subfiles[i]->filename);
804 file_buffer->subfiles[i] = fullpath;
806 file_buffer->subfiles[i] = NULL;
807 free (containing_dir);
810 /* For each node in the file's tags table, remember the starting
812 for (tags_index = 0; (entry = file_buffer->tags[tags_index]);
816 subfiles[i] && entry->nodestart >= subfiles[i]->first_byte;
819 /* If the Info file containing the indirect tags table is
820 malformed, then give up. */
823 /* The Info file containing the indirect tags table is
824 malformed. Give up. */
825 for (i = 0; subfiles[i]; i++)
827 free (subfiles[i]->filename);
829 free (file_buffer->subfiles[i]);
831 file_buffer->subfiles = NULL;
832 free_file_buffer_tags (file_buffer);
836 /* SUBFILES[i] is the index of the first subfile whose logical
837 first byte is greater than the logical offset of this node's
838 starting position. This means that the subfile directly
839 preceding this one is the one containing the node. */
841 entry->filename = file_buffer->subfiles[i - 1];
842 entry->nodestart -= subfiles[i - 1]->first_byte;
843 entry->nodestart += header_length;
846 /* We have successfully built the tags table. Remember that it
848 file_buffer->flags |= N_TagsIndirect;
851 /* Free the structures assigned to SUBFILES. Free the names as well
852 as the structures themselves, then finally, the array. */
853 for (i = 0; subfiles[i]; i++)
855 free (subfiles[i]->filename);
862 /* Return the node that contains TAG in FILE_BUFFER, else
863 (pathologically) NULL. Called from info_node_of_file_buffer_tags. */
865 find_node_of_anchor (file_buffer, tag)
866 FILE_BUFFER *file_buffer;
869 int anchor_pos, node_pos;
873 /* Look through the tag list for the anchor. */
874 for (anchor_pos = 0; file_buffer->tags[anchor_pos]; anchor_pos++)
876 TAG *t = file_buffer->tags[anchor_pos];
877 if (t->nodestart == tag->nodestart)
881 /* Should not happen, because we should always find the anchor. */
882 if (!file_buffer->tags[anchor_pos])
885 /* We've found the anchor. Look backwards in the tag table for the
886 preceding node (we're assuming the tags are given in order),
887 skipping over any preceding anchors. */
888 for (node_pos = anchor_pos - 1;
889 node_pos >= 0 && file_buffer->tags[node_pos]->nodelen == 0;
893 /* An info file with an anchor before any nodes is pathological, but
894 it's possible, so don't crash. */
898 /* We have the tag for the node that contained the anchor tag. */
899 node_tag = file_buffer->tags[node_pos];
901 /* Look up the node name in the tag table to get the actual node.
902 This is a recursive call, but it can't recurse again, because we
903 call it with a real node. */
904 node = info_node_of_file_buffer_tags (file_buffer, node_tag->nodename);
906 /* Start displaying the node at the anchor position. */
908 { /* The nodestart for real nodes is three characters before the `F'
909 in the `File:' line (a newline, the CTRL-_, and another
910 newline). The nodestart for anchors is the actual position.
911 But we offset by only 2, rather than 3, because if an anchor is
912 at the beginning of a paragraph, it's nicer for it to end up on
913 the beginning of the first line of the paragraph rather than
914 the blank line before it. (makeinfo has no way of knowing that
915 a paragraph is going to start, so we can't fix it there.) */
916 node->display_pos = file_buffer->tags[anchor_pos]->nodestart
917 - (node_tag->nodestart + 2);
919 /* Otherwise an anchor at the end of a node ends up displaying at
920 the end of the last line of the node (way over on the right of
921 the screen), which looks wrong. */
922 if (node->display_pos >= node->nodelen)
923 node->display_pos = node->nodelen - 1;
925 /* Don't search in the node for the xref text, it's not there. */
926 node->flags |= N_FromAnchor;
933 /* Return the node from FILE_BUFFER which matches NODENAME by searching
934 the tags table in FILE_BUFFER, or NULL. */
936 info_node_of_file_buffer_tags (file_buffer, nodename)
937 FILE_BUFFER *file_buffer;
943 /* If no tags at all (possibly a misformatted info file), quit. */
944 if (!file_buffer->tags) {
948 for (i = 0; (tag = file_buffer->tags[i]); i++)
949 if (strcmp (nodename, tag->nodename) == 0)
951 FILE_BUFFER *subfile = info_find_file_internal (tag->filename,
956 if (!subfile->contents)
958 info_reload_file_buffer_contents (subfile);
959 if (!subfile->contents)
963 /* If we were able to find this file and load it, then return
964 the node within it. */
966 NODE *node = xmalloc (sizeof (NODE));
967 node->filename = subfile->fullpath;
969 node->nodename = tag->nodename;
970 node->contents = subfile->contents + tag->nodestart;
971 node->display_pos = 0;
974 if (file_buffer->flags & N_HasTagsTable)
976 node->flags |= N_HasTagsTable;
978 if (file_buffer->flags & N_TagsIndirect)
980 node->flags |= N_TagsIndirect;
981 node->parent = file_buffer->fullpath;
985 if (subfile->flags & N_IsCompressed)
986 node->flags |= N_IsCompressed;
988 /* If TAG->nodelen hasn't been calculated yet, then we aren't
989 in a position to trust the entry pointer. Adjust things so
990 that ENTRY->nodestart gets the exact address of the start of
991 the node separator which starts this node, and NODE->contents
992 gets the address of the line defining this node. If we cannot
993 do that, the node isn't really here. */
994 if (tag->nodelen == -1)
998 SEARCH_BINDING node_body;
1001 min = max = DEFAULT_INFO_FUDGE;
1003 if (tag->nodestart < DEFAULT_INFO_FUDGE)
1004 min = tag->nodestart;
1006 if (DEFAULT_INFO_FUDGE >
1007 (subfile->filesize - tag->nodestart))
1008 max = subfile->filesize - tag->nodestart;
1010 /* NODE_SEP gets the address of the separator which defines
1011 this node, or NULL if the node wasn't found.
1012 NODE->contents is side-effected to point to right after
1014 node_sep = adjust_nodestart (node, min, max);
1015 if (node_sep == NULL)
1020 /* Readjust tag->nodestart. */
1021 tag->nodestart = node_sep - subfile->contents;
1023 /* Calculate the length of the current node. */
1024 buff_end = subfile->contents + subfile->filesize;
1026 node_body.buffer = node->contents;
1027 node_body.start = 0;
1028 node_body.end = buff_end - node_body.buffer;
1029 node_body.flags = 0;
1030 tag->nodelen = get_node_length (&node_body);
1031 node->nodelen = tag->nodelen;
1034 else if (tag->nodelen == 0) /* anchor, return containing node */
1037 node = find_node_of_anchor (file_buffer, tag);
1042 /* Since we know the length of this node, we have already
1043 adjusted tag->nodestart to point to the exact start of
1044 it. Simply skip the node separator. */
1045 node->contents += skip_node_separator (node->contents);
1046 node->nodelen = tag->nodelen;
1053 /* There was a tag table for this file, and the node wasn't found.
1054 Return NULL, since this file doesn't contain the desired node. */
1058 /* Managing file_buffers, nodes, and tags. */
1060 /* Create a new, empty file buffer. */
1064 FILE_BUFFER *file_buffer = xmalloc (sizeof (FILE_BUFFER));
1066 file_buffer->filename = file_buffer->fullpath = NULL;
1067 file_buffer->contents = NULL;
1068 file_buffer->tags = NULL;
1069 file_buffer->subfiles = NULL;
1070 file_buffer->tags_slots = 0;
1071 file_buffer->flags = 0;
1076 /* Add FILE_BUFFER to our list of already loaded info files. */
1078 remember_info_file (file_buffer)
1079 FILE_BUFFER *file_buffer;
1083 for (i = 0; info_loaded_files && info_loaded_files[i]; i++)
1086 add_pointer_to_array (file_buffer, i, info_loaded_files,
1087 info_loaded_files_slots, 10, FILE_BUFFER *);
1090 /* Forget the contents, tags table, nodes list, and names of FILENAME. */
1092 forget_info_file (filename)
1096 FILE_BUFFER *file_buffer;
1098 if (!info_loaded_files)
1101 for (i = 0; file_buffer = info_loaded_files[i]; i++)
1102 if (FILENAME_CMP (filename, file_buffer->filename) == 0
1103 || FILENAME_CMP (filename, file_buffer->fullpath) == 0)
1105 free (file_buffer->filename);
1106 free (file_buffer->fullpath);
1108 if (file_buffer->contents)
1109 free (file_buffer->contents);
1111 /* free_file_buffer_tags () also kills the subfiles list, since
1112 the subfiles list is only of use in conjunction with tags. */
1113 free_file_buffer_tags (file_buffer);
1115 /* Move rest of list down. */
1116 while (info_loaded_files[i + 1])
1118 info_loaded_files[i] = info_loaded_files[i + 1];
1121 info_loaded_files[i] = 0;
1127 /* Free the tags (if any) associated with FILE_BUFFER. */
1129 free_file_buffer_tags (file_buffer)
1130 FILE_BUFFER *file_buffer;
1134 if (file_buffer->tags)
1138 for (i = 0; (tag = file_buffer->tags[i]); i++)
1139 free_info_tag (tag);
1141 free (file_buffer->tags);
1142 file_buffer->tags = NULL;
1143 file_buffer->tags_slots = 0;
1146 if (file_buffer->subfiles)
1148 for (i = 0; file_buffer->subfiles[i]; i++)
1149 free (file_buffer->subfiles[i]);
1151 free (file_buffer->subfiles);
1152 file_buffer->subfiles = NULL;
1156 /* Free the data associated with TAG, as well as TAG itself. */
1161 free (tag->nodename);
1163 /* We don't free tag->filename, because that filename is part of the
1164 subfiles list for the containing FILE_BUFFER. free_info_tags ()
1165 will free the subfiles when it is appropriate. */
1170 /* Load the contents of FILE_BUFFER->contents. This function is called
1171 when a file buffer was loaded, and then in order to conserve memory, the
1172 file buffer's contents were freed and the pointer was zero'ed. Note that
1173 the file was already loaded at least once successfully, so the tags and/or
1174 nodes members are still correctly filled. */
1176 info_reload_file_buffer_contents (fb)
1181 #if defined (HANDLE_MAN_PAGES)
1182 /* If this is the magic manpage node, don't try to reload, just give up. */
1183 if (fb->flags & N_IsManPage)
1187 fb->flags &= ~N_IsCompressed;
1189 /* Let the filesystem do all the work for us. */
1191 filesys_read_info_file (fb->fullpath, &(fb->filesize), &(fb->finfo),
1194 fb->flags |= N_IsCompressed;
1197 /* Return the actual starting memory location of NODE, side-effecting
1198 NODE->contents. MIN and MAX are bounds for a search if one is necessary.
1199 Because of the way that tags are implemented, the physical nodestart may
1200 not actually be where the tag says it is. If that is the case, but the
1201 node was found anyway, set N_UpdateTags in NODE->flags. If the node is
1202 found, return non-zero. NODE->contents is returned positioned right after
1203 the node separator that precedes this node, while the return value is
1204 position directly on the separator that precedes this node. If the node
1205 could not be found, return a NULL pointer. */
1207 adjust_nodestart (node, min, max)
1212 SEARCH_BINDING node_body;
1214 /* Define the node body. */
1215 node_body.buffer = node->contents;
1216 node_body.start = 0;
1217 node_body.end = max;
1218 node_body.flags = 0;
1220 /* Try the optimal case first. Who knows? This file may actually be
1221 formatted (mostly) correctly. */
1222 if (node_body.buffer[0] != INFO_COOKIE && min > 2)
1223 node_body.buffer -= 3;
1225 position = find_node_separator (&node_body);
1227 /* If we found a node start, then check it out. */
1232 sep_len = skip_node_separator (node->contents);
1234 /* If we managed to skip a node separator, then check for this node
1235 being the right one. */
1238 char *nodedef, *nodestart;
1241 nodestart = node_body.buffer + position + sep_len;
1242 nodedef = nodestart;
1243 offset = string_in_line (INFO_NODE_LABEL, nodedef);
1248 nodedef += skip_whitespace (nodedef);
1249 offset = skip_node_characters (nodedef, DONT_SKIP_NEWLINES);
1250 if ((offset == strlen (node->nodename)) &&
1251 (strncmp (node->nodename, nodedef, offset) == 0))
1253 node->contents = nodestart;
1254 return node_body.buffer + position;
1260 /* Oh well, I guess we have to try to find it in a larger area. */
1261 node_body.buffer = node->contents - min;
1262 node_body.start = 0;
1263 node_body.end = min + max;
1264 node_body.flags = 0;
1266 position = find_node_in_binding (node->nodename, &node_body);
1268 /* If the node couldn't be found, we lose big. */
1272 /* Otherwise, the node was found, but the tags table could need updating
1273 (if we used a tag to get here, that is). Set the flag in NODE->flags. */
1274 node->contents = node_body.buffer + position;
1275 node->contents += skip_node_separator (node->contents);
1276 if (node->flags & N_HasTagsTable)
1277 node->flags |= N_UpdateTags;
1278 return node_body.buffer + position;