* decl.c (pop_cp_function_context): Don't call free on a NULL
[official-gcc.git] / texinfo / info / nodes.c
blobf2737e7b354c38dfe86bf9ccb218f7487efa1ecf
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
4 stored in Info format.
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)
11 any later version.
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). */
24 #include "info.h"
26 #include "nodes.h"
27 #include "search.h"
28 #include "filesys.h"
29 #include "info-utils.h"
31 #if defined (HANDLE_MAN_PAGES)
32 # include "man.h"
33 #endif /* HANDLE_MAN_PAGES */
35 /* **************************************************************** */
36 /* */
37 /* Functions Static to this File */
38 /* */
39 /* **************************************************************** */
41 static void forget_info_file (), remember_info_file ();
42 static void free_file_buffer_tags (), free_info_tag ();
43 static void get_nodes_of_tags_table (), get_nodes_of_info_file ();
44 static void get_tags_of_indirect_tags_table ();
45 static void info_reload_file_buffer_contents ();
46 static char *adjust_nodestart ();
47 static FILE_BUFFER *info_load_file_internal (), *info_find_file_internal ();
48 static NODE *info_node_of_file_buffer_tags ();
50 static long get_node_length ();
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 on the order of
54 4. */
55 #define DEFAULT_INFO_FUDGE 1000
57 /* Passed to *_internal functions. INFO_GET_TAGS says to do what is
58 neccessary to fill in the nodes or tags arrays in FILE_BUFFER. */
59 #define INFO_NO_TAGS 0
60 #define INFO_GET_TAGS 1
62 /* **************************************************************** */
63 /* */
64 /* Global Variables */
65 /* */
66 /* **************************************************************** */
68 /* When non-zero, this is a string describing the recent file error. */
69 char *info_recent_file_error = (char *)NULL;
71 /* The list of already loaded nodes. */
72 FILE_BUFFER **info_loaded_files = (FILE_BUFFER **)NULL;
74 /* The number of slots currently allocated to LOADED_FILES. */
75 int info_loaded_files_slots = 0;
77 /* **************************************************************** */
78 /* */
79 /* Public Functions for Node Manipulation */
80 /* */
81 /* **************************************************************** */
83 /* Used to build "dir" menu from "localdir" files found in INFOPATH. */
84 extern void maybe_build_dir_node ();
86 /* Return a pointer to a NODE structure for the Info node (FILENAME)NODENAME.
87 FILENAME can be passed as NULL, in which case the filename of "dir" is used.
88 NODENAME can be passed as NULL, in which case the nodename of "Top" is used.
89 If the node cannot be found, return a NULL pointer. */
90 NODE *
91 info_get_node (filename, nodename)
92 char *filename, *nodename;
94 FILE_BUFFER *file_buffer;
95 NODE *node;
97 file_buffer = (FILE_BUFFER *)NULL;
98 info_recent_file_error = (char *)NULL;
100 info_parse_node (nodename, DONT_SKIP_NEWLINES);
101 nodename = (char *)NULL;
103 if (info_parsed_filename)
104 filename = info_parsed_filename;
106 if (info_parsed_nodename)
107 nodename = info_parsed_nodename;
109 /* If FILENAME is not specified, it defaults to "dir". */
110 if (!filename)
111 filename = "dir";
113 /* If the file to be looked up is "dir", build the contents from all of
114 the "dir"s and "localdir"s found in INFOPATH. */
115 if (strcasecmp (filename, "dir") == 0)
116 maybe_build_dir_node (filename);
118 /* Find the correct info file. */
119 file_buffer = info_find_file (filename);
121 if (!file_buffer)
123 if (filesys_error_number)
124 info_recent_file_error =
125 filesys_error_string (filename, filesys_error_number);
126 return ((NODE *)NULL);
129 node = info_get_node_of_file_buffer (nodename, file_buffer);
130 /* If the node looked for was "Top", try again looking for the node under
131 a slightly different name. */
132 if (!node && (nodename == NULL || strcasecmp (nodename, "Top") == 0))
134 node = info_get_node_of_file_buffer ("Top", file_buffer);
135 if (!node)
136 node = info_get_node_of_file_buffer ("top", file_buffer);
137 if (!node)
138 node = info_get_node_of_file_buffer ("TOP", file_buffer);
140 return (node);
143 /* Return a pointer to a NODE structure for the Info node NODENAME in
144 FILE_BUFFER. NODENAME can be passed as NULL, in which case the
145 nodename of "Top" is used. If the node cannot be found, return a
146 NULL pointer. */
147 NODE *
148 info_get_node_of_file_buffer (nodename, file_buffer)
149 char *nodename;
150 FILE_BUFFER *file_buffer;
152 NODE *node = (NODE *)NULL;
154 /* If we are unable to find the file, we have to give up. There isn't
155 anything else we can do. */
156 if (!file_buffer)
157 return ((NODE *)NULL);
159 /* If the file buffer was gc'ed, reload the contents now. */
160 if (!file_buffer->contents)
161 info_reload_file_buffer_contents (file_buffer);
163 /* If NODENAME is not specified, it defaults to "Top". */
164 if (!nodename)
165 nodename = "Top";
167 /* If the name of the node that we wish to find is exactly "*", then the
168 node body is the contents of the entire file. Create and return such
169 a node. */
170 if (strcmp (nodename, "*") == 0)
172 node = (NODE *)xmalloc (sizeof (NODE));
173 node->filename = file_buffer->fullpath;
174 node->parent = (char *)NULL;
175 node->nodename = xstrdup ("*");
176 node->contents = file_buffer->contents;
177 node->nodelen = file_buffer->filesize;
178 node->flags = 0;
180 #if defined (HANDLE_MAN_PAGES)
181 /* If the file buffer is the magic one associated with manpages, call
182 the manpage node finding function instead. */
183 else if (file_buffer->flags & N_IsManPage)
185 node = get_manpage_node (file_buffer, nodename);
187 #endif /* HANDLE_MAN_PAGES */
188 /* If this is the "main" info file, it might contain a tags table. Search
189 the tags table for an entry which matches the node that we want. If
190 there is a tags table, get the file which contains this node, but don't
191 bother building a node list for it. */
192 else if (file_buffer->tags)
194 node = info_node_of_file_buffer_tags (file_buffer, nodename);
197 /* Return the results of our node search. */
198 return (node);
201 /* Locate the file named by FILENAME, and return the information structure
202 describing this file. The file may appear in our list of loaded files
203 already, or it may not. If it does not already appear, find the file,
204 and add it to the list of loaded files. If the file cannot be found,
205 return a NULL FILE_BUFFER *. */
206 FILE_BUFFER *
207 info_find_file (filename)
208 char *filename;
210 return (info_find_file_internal (filename, INFO_GET_TAGS));
213 /* Load the info file FILENAME, remembering information about it in a
214 file buffer. */
215 FILE_BUFFER *
216 info_load_file (filename)
217 char *filename;
219 return (info_load_file_internal (filename, INFO_GET_TAGS));
223 /* **************************************************************** */
224 /* */
225 /* Private Functions Implementation */
226 /* */
227 /* **************************************************************** */
229 /* The workhorse for info_find_file (). Non-zero 2nd argument says to
230 try to build a tags table (or otherwise glean the nodes) for this
231 file once found. By default, we build the tags table, but when this
232 function is called by info_get_node () when we already have a valid
233 tags table describing the nodes, it is unnecessary. */
234 static FILE_BUFFER *
235 info_find_file_internal (filename, get_tags)
236 char *filename;
237 int get_tags;
239 register int i;
240 register FILE_BUFFER *file_buffer;
242 /* First try to find the file in our list of already loaded files. */
243 if (info_loaded_files)
245 for (i = 0; (file_buffer = info_loaded_files[i]); i++)
246 if ((strcmp (filename, file_buffer->filename) == 0) ||
247 (strcmp (filename, file_buffer->fullpath) == 0) ||
248 ((*filename != '/') &&
249 strcmp (filename,
250 filename_non_directory (file_buffer->fullpath)) == 0))
252 struct stat new_info, *old_info;
254 /* This file is loaded. If the filename that we want is
255 specifically "dir", then simply return the file buffer. */
256 if (strcasecmp (filename_non_directory (filename), "dir") == 0)
257 return (file_buffer);
259 #if defined (HANDLE_MAN_PAGES)
260 /* Do the same for the magic MANPAGE file. */
261 if (file_buffer->flags & N_IsManPage)
262 return (file_buffer);
263 #endif /* HANDLE_MAN_PAGES */
265 /* The file appears to be already loaded, and it is not "dir".
266 Check to see if it has changed since the last time it was
267 loaded. */
268 if (stat (file_buffer->fullpath, &new_info) == -1)
270 filesys_error_number = errno;
271 return ((FILE_BUFFER *)NULL);
274 old_info = &file_buffer->finfo;
276 if ((new_info.st_size != old_info->st_size) ||
277 (new_info.st_mtime != old_info->st_mtime))
279 /* The file has changed. Forget that we ever had loaded it
280 in the first place. */
281 forget_info_file (filename);
282 break;
284 else
286 /* The info file exists, and has not changed since the last
287 time it was loaded. If the caller requested a nodes list
288 for this file, and there isn't one here, build the nodes
289 for this file_buffer. In any case, return the file_buffer
290 object. */
291 if (get_tags && !file_buffer->tags)
292 build_tags_and_nodes (file_buffer);
294 return (file_buffer);
299 /* The file wasn't loaded. Try to load it now. */
300 #if defined (HANDLE_MAN_PAGES)
301 /* If the name of the file that we want is our special file buffer for
302 Unix manual pages, then create the file buffer, and return it now. */
303 if (strcasecmp (filename, MANPAGE_FILE_BUFFER_NAME) == 0)
304 file_buffer = create_manpage_file_buffer ();
305 else
306 #endif /* HANDLE_MAN_PAGES */
307 file_buffer = info_load_file_internal (filename, get_tags);
309 /* If the file was loaded, remember the name under which it was found. */
310 if (file_buffer)
311 remember_info_file (file_buffer);
313 return (file_buffer);
316 /* The workhorse function for info_load_file (). Non-zero second argument
317 says to build a list of tags (or nodes) for this file. This is the
318 default behaviour when info_load_file () is called, but it is not
319 necessary when loading a subfile for which we already have tags. */
320 static FILE_BUFFER *
321 info_load_file_internal (filename, get_tags)
322 char *filename;
323 int get_tags;
325 char *fullpath, *contents;
326 long filesize;
327 struct stat finfo;
328 int retcode;
329 FILE_BUFFER *file_buffer = (FILE_BUFFER *)NULL;
331 /* Get the full pathname of this file, as known by the info system.
332 That is to say, search along INFOPATH and expand tildes, etc. */
333 fullpath = info_find_fullpath (filename);
335 /* Did we actually find the file? */
336 retcode = stat (fullpath, &finfo);
338 /* If the file referenced by the name returned from info_find_fullpath ()
339 doesn't exist, then try again with the last part of the filename
340 appearing in lowercase. */
341 if (retcode < 0)
343 char *lowered_name;
344 char *basename;
346 lowered_name = xstrdup (filename);
347 basename = (char *) strrchr (lowered_name, '/');
349 if (basename)
350 basename++;
351 else
352 basename = lowered_name;
354 while (*basename)
356 if (isupper (*basename))
357 *basename = tolower (*basename);
359 basename++;
362 fullpath = info_find_fullpath (lowered_name);
363 free (lowered_name);
365 retcode = stat (fullpath, &finfo);
368 /* If the file wasn't found, give up, returning a NULL pointer. */
369 if (retcode < 0)
371 filesys_error_number = errno;
372 return ((FILE_BUFFER *)NULL);
375 /* Otherwise, try to load the file. */
376 contents = filesys_read_info_file (fullpath, &filesize, &finfo);
378 if (!contents)
379 return ((FILE_BUFFER *)NULL);
381 /* The file was found, and can be read. Allocate FILE_BUFFER and fill
382 in the various members. */
383 file_buffer = make_file_buffer ();
384 file_buffer->filename = xstrdup (filename);
385 file_buffer->fullpath = xstrdup (fullpath);
386 file_buffer->finfo = finfo;
387 file_buffer->filesize = filesize;
388 file_buffer->contents = contents;
389 if (file_buffer->filesize != file_buffer->finfo.st_size)
390 file_buffer->flags |= N_IsCompressed;
392 /* If requested, build the tags and nodes for this file buffer. */
393 if (get_tags)
394 build_tags_and_nodes (file_buffer);
396 return (file_buffer);
399 /* Grovel FILE_BUFFER->contents finding tags and nodes, and filling in the
400 various slots. This can also be used to rebuild a tag or node table. */
401 void
402 build_tags_and_nodes (file_buffer)
403 FILE_BUFFER *file_buffer;
405 SEARCH_BINDING binding;
406 long position;
408 free_file_buffer_tags (file_buffer);
409 file_buffer->flags &= ~N_HasTagsTable;
411 /* See if there is a tags table in this info file. */
412 binding.buffer = file_buffer->contents;
413 binding.start = file_buffer->filesize;
414 binding.end = binding.start - 1000;
415 if (binding.end < 0)
416 binding.end = 0;
417 binding.flags = S_FoldCase;
419 position = search_backward (TAGS_TABLE_END_LABEL, &binding);
421 /* If there is a tag table, find the start of it, and grovel over it
422 extracting tag information. */
423 if (position != -1)
424 while (1)
426 long tags_table_begin, tags_table_end;
428 binding.end = position;
429 binding.start = binding.end - 5 - strlen (TAGS_TABLE_END_LABEL);
430 if (binding.start < 0)
431 binding.start = 0;
433 position = find_node_separator (&binding);
435 /* For this test, (and all others here) failure indicates a bogus
436 tags table. Grovel the file. */
437 if (position == -1)
438 break;
440 /* Remember the end of the tags table. */
441 binding.start = position;
442 tags_table_end = binding.start;
443 binding.end = 0;
445 /* Locate the start of the tags table. */
446 position = search_backward (TAGS_TABLE_BEG_LABEL, &binding);
448 if (position == -1)
449 break;
451 binding.end = position;
452 binding.start = binding.end - 5 - strlen (TAGS_TABLE_BEG_LABEL);
453 position = find_node_separator (&binding);
455 if (position == -1)
456 break;
458 /* The file contains a valid tags table. Fill the FILE_BUFFER's
459 tags member. */
460 file_buffer->flags |= N_HasTagsTable;
461 tags_table_begin = position;
463 /* If this isn't an indirect tags table, just remember the nodes
464 described locally in this tags table. Note that binding.end
465 is pointing to just after the beginning label. */
466 binding.start = binding.end;
467 binding.end = file_buffer->filesize;
469 if (!looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, &binding))
471 binding.start = tags_table_begin;
472 binding.end = tags_table_end;
473 get_nodes_of_tags_table (file_buffer, &binding);
474 return;
476 else
478 /* This is an indirect tags table. Build TAGS member. */
479 SEARCH_BINDING indirect;
481 indirect.start = tags_table_begin;
482 indirect.end = 0;
483 indirect.buffer = binding.buffer;
484 indirect.flags = S_FoldCase;
486 position = search_backward (INDIRECT_TAGS_TABLE_LABEL, &indirect);
488 if (position == -1)
490 /* This file is malformed. Give up. */
491 return;
494 indirect.start = position;
495 indirect.end = tags_table_begin;
496 binding.start = tags_table_begin;
497 binding.end = tags_table_end;
498 get_tags_of_indirect_tags_table (file_buffer, &indirect, &binding);
499 return;
503 /* This file doesn't contain any kind of tags table. Grovel the
504 file and build node entries for it. */
505 get_nodes_of_info_file (file_buffer);
508 /* Search through FILE_BUFFER->contents building an array of TAG *,
509 one entry per each node present in the file. Store the tags in
510 FILE_BUFFER->tags, and the number of allocated slots in
511 FILE_BUFFER->tags_slots. */
512 static void
513 get_nodes_of_info_file (file_buffer)
514 FILE_BUFFER *file_buffer;
516 long nodestart;
517 int tags_index = 0;
518 SEARCH_BINDING binding;
520 binding.buffer = file_buffer->contents;
521 binding.start = 0;
522 binding.end = file_buffer->filesize;
523 binding.flags = S_FoldCase;
525 while ((nodestart = find_node_separator (&binding)) != -1)
527 int start, end;
528 char *nodeline;
529 TAG *entry;
531 /* Skip past the characters just found. */
532 binding.start = nodestart;
533 binding.start += skip_node_separator (binding.buffer + binding.start);
535 /* Move to the start of the line defining the node. */
536 nodeline = binding.buffer + binding.start;
538 /* Find "Node:" */
539 start = string_in_line (INFO_NODE_LABEL, nodeline);
541 /* If not there, this is not the start of a node. */
542 if (start == -1)
543 continue;
545 /* Find the start of the nodename. */
546 start += skip_whitespace (nodeline + start);
548 /* Find the end of the nodename. */
549 end = start +
550 skip_node_characters (nodeline + start, DONT_SKIP_NEWLINES);
552 /* Okay, we have isolated the node name, and we know where the
553 node starts. Remember this information in a NODE structure. */
554 entry = (TAG *)xmalloc (sizeof (TAG));
555 entry->nodename = (char *)xmalloc (1 + (end - start));
556 strncpy (entry->nodename, nodeline + start, end - start);
557 entry->nodename[end - start] = '\0';
558 entry->nodestart = nodestart;
560 SEARCH_BINDING node_body;
562 node_body.buffer = binding.buffer + binding.start;
563 node_body.start = 0;
564 node_body.end = binding.end - binding.start;
565 node_body.flags = S_FoldCase;
566 entry->nodelen = get_node_length (&node_body);
569 entry->filename = file_buffer->fullpath;
571 /* Add this tag to the array of tag structures in this FILE_BUFFER. */
572 add_pointer_to_array (entry, tags_index, file_buffer->tags,
573 file_buffer->tags_slots, 100, TAG *);
577 /* Return the length of the node which starts at BINDING. */
578 static long
579 get_node_length (binding)
580 SEARCH_BINDING *binding;
582 register int i;
583 char *body;
585 /* From the Info-RFC file:
586 [A node] ends with either a ^_, a ^L, or the end of file. */
587 for (i = binding->start, body = binding->buffer; i < binding->end; i++)
589 if (body[i] == INFO_FF || body[i] == INFO_COOKIE)
590 break;
592 return ((long) i - binding->start);
595 /* Build and save the array of nodes in FILE_BUFFER by searching through the
596 contents of BUFFER_BINDING for a tags table, and groveling the contents. */
597 static void
598 get_nodes_of_tags_table (file_buffer, buffer_binding)
599 FILE_BUFFER *file_buffer;
600 SEARCH_BINDING *buffer_binding;
602 int offset, tags_index = 0;
603 SEARCH_BINDING *search;
604 long position;
606 search = copy_binding (buffer_binding);
608 /* Find the start of the tags table. */
609 position = find_tags_table (search);
611 /* If none, we're all done. */
612 if (position == -1)
613 return;
615 /* Move to one character before the start of the actual table. */
616 search->start = position;
617 search->start += skip_node_separator (search->buffer + search->start);
618 search->start += strlen (TAGS_TABLE_BEG_LABEL);
619 search->start--;
621 /* The tag table consists of lines containing node names and positions.
622 Do each line until we find one that doesn't contain a node name. */
623 while ((position = search_forward ("\n", search)) != -1)
625 TAG *entry;
626 char *nodedef;
628 /* Prepare to skip this line. */
629 search->start = position;
630 search->start++;
632 /* Skip past informative "(Indirect)" tags table line. */
633 if (!tags_index && looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, search))
634 continue;
636 /* Find the label preceding the node name. */
637 offset =
638 string_in_line (INFO_NODE_LABEL, search->buffer + search->start);
640 /* If not there, not a defining line, so we must be out of the
641 tags table. */
642 if (offset == -1)
643 break;
645 /* Point to the beginning of the node definition. */
646 search->start += offset;
647 nodedef = search->buffer + search->start;
648 nodedef += skip_whitespace (nodedef);
650 /* Move past the node's name. */
651 for (offset = 0;
652 (nodedef[offset]) && (nodedef[offset] != INFO_TAGSEP);
653 offset++);
655 if (nodedef[offset] != INFO_TAGSEP)
656 continue;
658 entry = (TAG *)xmalloc (sizeof (TAG));
659 entry->nodename = (char *)xmalloc (1 + offset);
660 strncpy (entry->nodename, nodedef, offset);
661 entry->nodename[offset] = '\0';
662 offset++;
663 entry->nodestart = (long) atol (nodedef + offset);
665 /* We don't know the length of this node yet. */
666 entry->nodelen = -1;
668 /* The filename of this node is currently known as the same as the
669 name of this file. */
670 entry->filename = file_buffer->fullpath;
672 /* Add this node structure to the array of node structures in this
673 FILE_BUFFER. */
674 add_pointer_to_array (entry, tags_index, file_buffer->tags,
675 file_buffer->tags_slots, 100, TAG *);
677 free (search);
680 /* A structure used only in get_tags_of_indirect_tags_table () to hold onto
681 an intermediate value. */
682 typedef struct {
683 char *filename;
684 long first_byte;
685 } SUBFILE;
687 /* Remember in FILE_BUFFER the nodenames, subfilenames, and offsets within the
688 subfiles of every node which appears in TAGS_BINDING. The 2nd argument is
689 a binding surrounding the indirect files list. */
690 static void
691 get_tags_of_indirect_tags_table (file_buffer, indirect_binding, tags_binding)
692 FILE_BUFFER *file_buffer;
693 SEARCH_BINDING *indirect_binding, *tags_binding;
695 register int i;
696 SUBFILE **subfiles = (SUBFILE **)NULL;
697 int subfiles_index = 0, subfiles_slots = 0;
698 TAG *entry;
700 /* First get the list of tags from the tags table. Then lookup the
701 associated file in the indirect list for each tag, and update it. */
702 get_nodes_of_tags_table (file_buffer, tags_binding);
704 /* We have the list of tags in file_buffer->tags. Get the list of
705 subfiles from the indirect table. */
707 char *start, *end, *line;
708 SUBFILE *subfile;
710 start = indirect_binding->buffer + indirect_binding->start;
711 end = indirect_binding->buffer + indirect_binding->end;
712 line = start;
714 while (line < end)
716 int colon;
718 colon = string_in_line (":", line);
720 if (colon == -1)
721 break;
723 subfile = (SUBFILE *)xmalloc (sizeof (SUBFILE));
724 subfile->filename = (char *)xmalloc (colon);
725 strncpy (subfile->filename, line, colon - 1);
726 subfile->filename[colon - 1] = '\0';
727 subfile->first_byte = (long) atol (line + colon);
729 add_pointer_to_array
730 (subfile, subfiles_index, subfiles, subfiles_slots, 10, SUBFILE *);
732 while (*line++ != '\n');
736 /* If we have successfully built the indirect files table, then
737 merge the information in the two tables. */
738 if (!subfiles)
740 free_file_buffer_tags (file_buffer);
741 return;
743 else
745 register int tags_index;
746 long header_length;
747 SEARCH_BINDING binding;
749 /* Find the length of the header of the file containing the indirect
750 tags table. This header appears at the start of every file. We
751 want the absolute position of each node within each subfile, so
752 we subtract the start of the containing subfile from the logical
753 position of the node, and then add the length of the header in. */
754 binding.buffer = file_buffer->contents;
755 binding.start = 0;
756 binding.end = file_buffer->filesize;
757 binding.flags = S_FoldCase;
759 header_length = find_node_separator (&binding);
760 if (header_length == -1)
761 header_length = 0;
763 /* Build the file buffer's list of subfiles. */
765 char *containing_dir, *temp;
766 int len_containing_dir;
768 containing_dir = xstrdup (file_buffer->fullpath);
769 temp = (char *) strrchr (containing_dir, '/');
771 if (temp)
772 *temp = '\0';
774 len_containing_dir = strlen (containing_dir);
776 for (i = 0; subfiles[i]; i++);
778 file_buffer->subfiles = (char **) xmalloc ((1 + i) * sizeof (char *));
780 for (i = 0; subfiles[i]; i++)
782 char *fullpath;
784 fullpath = (char *) xmalloc
785 (2 + strlen (subfiles[i]->filename) + len_containing_dir);
787 sprintf (fullpath, "%s/%s",
788 containing_dir, subfiles[i]->filename);
790 file_buffer->subfiles[i] = fullpath;
792 file_buffer->subfiles[i] = (char *)NULL;
793 free (containing_dir);
796 /* For each node in the file's tags table, remember the starting
797 position. */
798 for (tags_index = 0; (entry = file_buffer->tags[tags_index]);
799 tags_index++)
801 for (i = 0;
802 subfiles[i] && entry->nodestart >= subfiles[i]->first_byte;
803 i++);
805 /* If the Info file containing the indirect tags table is
806 malformed, then give up. */
807 if (!i)
809 /* The Info file containing the indirect tags table is
810 malformed. Give up. */
811 for (i = 0; subfiles[i]; i++)
813 free (subfiles[i]->filename);
814 free (subfiles[i]);
815 free (file_buffer->subfiles[i]);
817 file_buffer->subfiles = (char **)NULL;
818 free_file_buffer_tags (file_buffer);
819 return;
822 /* SUBFILES[i] is the index of the first subfile whose logical
823 first byte is greater than the logical offset of this node's
824 starting position. This means that the subfile directly
825 preceding this one is the one containing the node. */
827 entry->filename = file_buffer->subfiles[i - 1];
828 entry->nodestart -= subfiles[i -1]->first_byte;
829 entry->nodestart += header_length;
830 entry->nodelen = -1;
833 /* We have successfully built the tags table. Remember that it
834 was indirect. */
835 file_buffer->flags |= N_TagsIndirect;
838 /* Free the structures assigned to SUBFILES. Free the names as well
839 as the structures themselves, then finally, the array. */
840 for (i = 0; subfiles[i]; i++)
842 free (subfiles[i]->filename);
843 free (subfiles[i]);
845 free (subfiles);
848 /* Return the node from FILE_BUFFER which matches NODENAME by searching
849 the tags table in FILE_BUFFER. If the node could not be found, return
850 a NULL pointer. */
851 static NODE *
852 info_node_of_file_buffer_tags (file_buffer, nodename)
853 FILE_BUFFER *file_buffer;
854 char *nodename;
856 register int i;
857 TAG *tag;
859 for (i = 0; (tag = file_buffer->tags[i]); i++)
860 if (strcmp (nodename, tag->nodename) == 0)
862 FILE_BUFFER *subfile;
864 subfile = info_find_file_internal (tag->filename, INFO_NO_TAGS);
866 if (!subfile)
867 return ((NODE *)NULL);
869 if (!subfile->contents)
871 info_reload_file_buffer_contents (subfile);
873 if (!subfile->contents)
874 return ((NODE *)NULL);
877 /* If we were able to find this file and load it, then return
878 the node within it. */
880 NODE *node;
882 node = (NODE *)xmalloc (sizeof (NODE));
883 node->filename = (subfile->fullpath);
884 node->nodename = tag->nodename;
885 node->contents = subfile->contents + tag->nodestart;
886 node->flags = 0;
887 node->parent = (char *)NULL;
889 if (file_buffer->flags & N_HasTagsTable)
891 node->flags |= N_HasTagsTable;
893 if (file_buffer->flags & N_TagsIndirect)
895 node->flags |= N_TagsIndirect;
896 node->parent = file_buffer->fullpath;
900 if (subfile->flags & N_IsCompressed)
901 node->flags |= N_IsCompressed;
903 /* If TAG->nodelen hasn't been calculated yet, then we aren't
904 in a position to trust the entry pointer. Adjust things so
905 that ENTRY->nodestart gets the exact address of the start of
906 the node separator which starts this node, and NODE->contents
907 gets the address of the line defining this node. If we cannot
908 do that, the node isn't really here. */
909 if (tag->nodelen == -1)
911 int min, max;
912 char *node_sep;
913 SEARCH_BINDING node_body;
914 char *buff_end;
916 min = max = DEFAULT_INFO_FUDGE;
918 if (tag->nodestart < DEFAULT_INFO_FUDGE)
919 min = tag->nodestart;
921 if (DEFAULT_INFO_FUDGE >
922 (subfile->filesize - tag->nodestart))
923 max = subfile->filesize - tag->nodestart;
925 /* NODE_SEP gets the address of the separator which defines
926 this node, or (char *)NULL if the node wasn't found.
927 NODE->contents is side-effected to point to right after
928 the separator. */
929 node_sep = adjust_nodestart (node, min, max);
930 if (node_sep == (char *)NULL)
932 free (node);
933 return ((NODE *)NULL);
935 /* Readjust tag->nodestart. */
936 tag->nodestart = node_sep - subfile->contents;
938 /* Calculate the length of the current node. */
939 buff_end = subfile->contents + subfile->filesize;
941 node_body.buffer = node->contents;
942 node_body.start = 0;
943 node_body.end = buff_end - node_body.buffer;
944 node_body.flags = 0;
945 tag->nodelen = get_node_length (&node_body);
947 else
949 /* Since we know the length of this node, we have already
950 adjusted tag->nodestart to point to the exact start of
951 it. Simply skip the node separator. */
952 node->contents += skip_node_separator (node->contents);
955 node->nodelen = tag->nodelen;
956 return (node);
960 /* There was a tag table for this file, and the node wasn't found.
961 Return NULL, since this file doesn't contain the desired node. */
962 return ((NODE *)NULL);
965 /* **************************************************************** */
966 /* */
967 /* Managing file_buffers, nodes, and tags. */
968 /* */
969 /* **************************************************************** */
971 /* Create a new, empty file buffer. */
972 FILE_BUFFER *
973 make_file_buffer ()
975 FILE_BUFFER *file_buffer;
977 file_buffer = (FILE_BUFFER *)xmalloc (sizeof (FILE_BUFFER));
978 file_buffer->filename = file_buffer->fullpath = (char *)NULL;
979 file_buffer->contents = (char *)NULL;
980 file_buffer->tags = (TAG **)NULL;
981 file_buffer->subfiles = (char **)NULL;
982 file_buffer->tags_slots = 0;
983 file_buffer->flags = 0;
985 return (file_buffer);
988 /* Add FILE_BUFFER to our list of already loaded info files. */
989 static void
990 remember_info_file (file_buffer)
991 FILE_BUFFER *file_buffer;
993 int i;
995 for (i = 0; info_loaded_files && info_loaded_files[i]; i++)
998 add_pointer_to_array (file_buffer, i, info_loaded_files,
999 info_loaded_files_slots, 10, FILE_BUFFER *);
1002 /* Forget the contents, tags table, nodes list, and names of FILENAME. */
1003 static void
1004 forget_info_file (filename)
1005 char *filename;
1007 register int i;
1008 FILE_BUFFER *file_buffer;
1010 if (!info_loaded_files)
1011 return;
1013 for (i = 0; (file_buffer = info_loaded_files[i]); i++)
1014 if ((strcmp (filename, file_buffer->filename) == 0) ||
1015 (strcmp (filename, file_buffer->fullpath) == 0))
1017 free (file_buffer->filename);
1018 free (file_buffer->fullpath);
1020 if (file_buffer->contents)
1021 free (file_buffer->contents);
1023 /* Note that free_file_buffer_tags () also kills the subfiles
1024 list, since the subfiles list is only of use in conjunction
1025 with tags. */
1026 free_file_buffer_tags (file_buffer);
1028 while ((info_loaded_files[i] = info_loaded_files[++i]))
1031 break;
1035 /* Free the tags (if any) associated with FILE_BUFFER. */
1036 static void
1037 free_file_buffer_tags (file_buffer)
1038 FILE_BUFFER *file_buffer;
1040 register int i;
1042 if (file_buffer->tags)
1044 register TAG *tag;
1046 for (i = 0; (tag = file_buffer->tags[i]); i++)
1047 free_info_tag (tag);
1049 free (file_buffer->tags);
1050 file_buffer->tags = (TAG **)NULL;
1051 file_buffer->tags_slots = 0;
1054 if (file_buffer->subfiles)
1056 for (i = 0; file_buffer->subfiles[i]; i++)
1057 free (file_buffer->subfiles[i]);
1059 free (file_buffer->subfiles);
1060 file_buffer->subfiles = (char **)NULL;
1064 /* Free the data associated with TAG, as well as TAG itself. */
1065 static void
1066 free_info_tag (tag)
1067 TAG *tag;
1069 free (tag->nodename);
1071 /* We don't free tag->filename, because that filename is part of the
1072 subfiles list for the containing FILE_BUFFER. free_info_tags ()
1073 will free the subfiles when it is appropriate. */
1075 free (tag);
1078 /* Load the contents of FILE_BUFFER->contents. This function is called
1079 when a file buffer was loaded, and then in order to conserve memory, the
1080 file buffer's contents were freed and the pointer was zero'ed. Note that
1081 the file was already loaded at least once successfully, so the tags and/or
1082 nodes members are still correctly filled. */
1083 static void
1084 info_reload_file_buffer_contents (fb)
1085 FILE_BUFFER *fb;
1088 #if defined (HANDLE_MAN_PAGES)
1089 /* If this is the magic manpage node, don't try to reload, just give up. */
1090 if (fb->flags & N_IsManPage)
1091 return;
1092 #endif
1094 fb->flags &= ~N_IsCompressed;
1096 /* Let the filesystem do all the work for us. */
1097 fb->contents =
1098 filesys_read_info_file (fb->fullpath, &(fb->filesize), &(fb->finfo));
1099 if (fb->filesize != (long) (fb->finfo.st_size))
1100 fb->flags |= N_IsCompressed;
1103 /* Return the actual starting memory location of NODE, side-effecting
1104 NODE->contents. MIN and MAX are bounds for a search if one is necessary.
1105 Because of the way that tags are implemented, the physical nodestart may
1106 not actually be where the tag says it is. If that is the case, but the
1107 node was found anyway, set N_UpdateTags in NODE->flags. If the node is
1108 found, return non-zero. NODE->contents is returned positioned right after
1109 the node separator that precedes this node, while the return value is
1110 position directly on the separator that precedes this node. If the node
1111 could not be found, return a NULL pointer. */
1112 static char *
1113 adjust_nodestart (node, min, max)
1114 NODE *node;
1115 int min, max;
1117 long position;
1118 SEARCH_BINDING node_body;
1120 /* Define the node body. */
1121 node_body.buffer = node->contents;
1122 node_body.start = 0;
1123 node_body.end = max;
1124 node_body.flags = 0;
1126 /* Try the optimal case first. Who knows? This file may actually be
1127 formatted (mostly) correctly. */
1128 if (node_body.buffer[0] != INFO_COOKIE && min > 2)
1129 node_body.buffer -= 3;
1131 position = find_node_separator (&node_body);
1133 /* If we found a node start, then check it out. */
1134 if (position != -1)
1136 int sep_len;
1138 sep_len = skip_node_separator (node->contents);
1140 /* If we managed to skip a node separator, then check for this node
1141 being the right one. */
1142 if (sep_len != 0)
1144 char *nodedef, *nodestart;
1145 int offset;
1147 nodestart = node_body.buffer + position + sep_len;
1148 nodedef = nodestart;
1149 offset = string_in_line (INFO_NODE_LABEL, nodedef);
1151 if (offset != -1)
1153 nodedef += offset;
1154 nodedef += skip_whitespace (nodedef);
1155 offset = skip_node_characters (nodedef, DONT_SKIP_NEWLINES);
1156 if ((offset == strlen (node->nodename)) &&
1157 (strncmp (node->nodename, nodedef, offset) == 0))
1159 node->contents = nodestart;
1160 return (node_body.buffer + position);
1166 /* Oh well, I guess we have to try to find it in a larger area. */
1167 node_body.buffer = node->contents - min;
1168 node_body.start = 0;
1169 node_body.end = min + max;
1170 node_body.flags = 0;
1172 position = find_node_in_binding (node->nodename, &node_body);
1174 /* If the node couldn't be found, we lose big. */
1175 if (position == -1)
1176 return ((char *)NULL);
1178 /* Otherwise, the node was found, but the tags table could need updating
1179 (if we used a tag to get here, that is). Set the flag in NODE->flags. */
1180 node->contents = node_body.buffer + position;
1181 node->contents += skip_node_separator (node->contents);
1182 if (node->flags & N_HasTagsTable)
1183 node->flags |= N_UpdateTags;
1184 return (node_body.buffer + position);