OSDN Git Service

8995c78195f66e8380dfdfeddaace226b76d8146
[pf3gnuchains/gcc-fork.git] / texinfo / info / nodes.c
1 /* nodes.c -- How to get an Info file and node. */
2
3 /* This file is part of GNU Info, a program for reading online documentation
4    stored in Info format.
5
6    Copyright (C) 1993 Free Software Foundation, Inc.
7
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.
12
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.
17
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.
21
22    Written by Brian Fox (bfox@ai.mit.edu). */
23
24 #include <stdio.h>
25 #include <ctype.h>
26 #include <sys/types.h>
27 #if defined (HAVE_SYS_FILE_H)
28 #include <sys/file.h>
29 #endif /* HAVE_SYS_FILE_H */
30 #include <sys/errno.h>
31 #include <sys/stat.h>
32 #if defined (HAVE_STRING_H)
33 #include <string.h>
34 #endif /* HAVE_STRING_H */
35 #include "nodes.h"
36 #include "search.h"
37 #include "filesys.h"
38 #include "info-utils.h"
39
40 #if defined (HANDLE_MAN_PAGES)
41 #  include "man.h"
42 #endif /* HANDLE_MAN_PAGES */
43
44 #if !defined (O_RDONLY)
45 #if defined (HAVE_SYS_FCNTL_H)
46 #include <sys/fcntl.h>
47 #else /* !HAVE_SYS_FCNTL_H */
48 #include <fcntl.h>
49 #endif /* !HAVE_SYS_FCNTL_H */
50 #endif /* !O_RDONLY */
51
52 #if !defined (errno)
53 extern int errno;
54 #endif /* !errno */
55
56 /* **************************************************************** */
57 /*                                                                  */
58 /*                   Functions Static to this File                  */
59 /*                                                                  */
60 /* **************************************************************** */
61
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 ();
70
71 static long get_node_length ();
72
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
75    4. */
76 #define DEFAULT_INFO_FUDGE 1000
77
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
82 \f
83 /* **************************************************************** */
84 /*                                                                  */
85 /*                       Global Variables                           */
86 /*                                                                  */
87 /* **************************************************************** */
88
89 /* When non-zero, this is a string describing the recent file error. */
90 char *info_recent_file_error = (char *)NULL;
91
92 /* The list of already loaded nodes. */
93 FILE_BUFFER **info_loaded_files = (FILE_BUFFER **)NULL;
94
95 /* The number of slots currently allocated to LOADED_FILES. */
96 int info_loaded_files_slots = 0;
97 \f
98 /* **************************************************************** */
99 /*                                                                  */
100 /*               Public Functions for Node Manipulation             */
101 /*                                                                  */
102 /* **************************************************************** */
103
104 /* Used to build "dir" menu from "localdir" files found in INFOPATH. */
105 extern void maybe_build_dir_node ();
106
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. */
111 NODE *
112 info_get_node (filename, nodename)
113      char *filename, *nodename;
114 {
115   FILE_BUFFER *file_buffer;
116   NODE *node;
117
118   file_buffer = (FILE_BUFFER *)NULL;
119   info_recent_file_error = (char *)NULL;
120
121   info_parse_node (nodename, DONT_SKIP_NEWLINES);
122   nodename = (char *)NULL;
123
124   if (info_parsed_filename)
125     filename = info_parsed_filename;
126
127   if (info_parsed_nodename)
128     nodename = info_parsed_nodename;
129
130   /* If FILENAME is not specified, it defaults to "dir". */
131   if (!filename)
132     filename = "dir";
133
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);
138
139   /* Find the correct info file. */
140   file_buffer = info_find_file (filename);
141
142   if (!file_buffer)
143     {
144       if (filesys_error_number)
145         info_recent_file_error =
146           filesys_error_string (filename, filesys_error_number);
147       return ((NODE *)NULL);
148     }
149
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))
154     {
155       node = info_get_node_of_file_buffer ("Top", file_buffer);
156       if (!node)
157         node = info_get_node_of_file_buffer ("top", file_buffer);
158       if (!node)
159         node = info_get_node_of_file_buffer ("TOP", file_buffer);
160     }
161   return (node);
162 }
163
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
167    NULL pointer. */
168 NODE *
169 info_get_node_of_file_buffer (nodename, file_buffer)
170      char *nodename;
171      FILE_BUFFER *file_buffer;
172 {
173   NODE *node = (NODE *)NULL;
174
175   /* If we are unable to find the file, we have to give up.  There isn't
176      anything else we can do. */
177   if (!file_buffer)
178     return ((NODE *)NULL);
179
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);
183
184   /* If NODENAME is not specified, it defaults to "Top". */
185   if (!nodename)
186     nodename = "Top";
187
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
190      a node. */
191   if (strcmp (nodename, "*") == 0)
192     {
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;
199       node->flags = 0;
200     }
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)
205     {
206         node = get_manpage_node (file_buffer, nodename);
207     }
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)
214     {
215       node = info_node_of_file_buffer_tags (file_buffer, nodename);
216     }
217
218   /* Return the results of our node search. */
219   return (node);
220 }
221
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 *. */
227 FILE_BUFFER *
228 info_find_file (filename)
229      char *filename;
230 {
231   return (info_find_file_internal (filename, INFO_GET_TAGS));
232 }
233
234 /* Load the info file FILENAME, remembering information about it in a
235    file buffer. */
236 FILE_BUFFER *
237 info_load_file (filename)
238      char *filename;
239 {
240   return (info_load_file_internal (filename, INFO_GET_TAGS));
241 }
242
243 \f
244 /* **************************************************************** */
245 /*                                                                  */
246 /*                  Private Functions Implementation                */
247 /*                                                                  */
248 /* **************************************************************** */
249
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. */
255 static FILE_BUFFER *
256 info_find_file_internal (filename, get_tags)
257      char *filename;
258      int get_tags;
259 {
260   register int i;
261   register FILE_BUFFER *file_buffer;
262
263   /* First try to find the file in our list of already loaded files. */
264   if (info_loaded_files)
265     {
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 != '/') &&
270              strcmp (filename,
271                      filename_non_directory (file_buffer->fullpath)) == 0))
272           {
273             struct stat new_info, *old_info;
274
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);
279
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 */
285
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
288                loaded. */
289             if (stat (file_buffer->fullpath, &new_info) == -1)
290               {
291                 filesys_error_number = errno;
292                 return ((FILE_BUFFER *)NULL);
293               }
294
295             old_info = &file_buffer->finfo;
296
297             if ((new_info.st_size != old_info->st_size) ||
298                 (new_info.st_mtime != old_info->st_mtime))
299               {
300                 /* The file has changed.  Forget that we ever had loaded it
301                    in the first place. */
302                 forget_info_file (filename);
303                 break;
304               }
305             else
306               {
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
311                    object. */
312                 if (get_tags && !file_buffer->tags)
313                   build_tags_and_nodes (file_buffer);
314
315                 return (file_buffer);
316               }
317           }
318     }
319
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 ();
326   else
327 #endif /* HANDLE_MAN_PAGES */
328     file_buffer = info_load_file_internal (filename, get_tags);
329
330   /* If the file was loaded, remember the name under which it was found. */
331   if (file_buffer)
332     remember_info_file (file_buffer);
333
334   return (file_buffer);
335 }
336
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. */
341 static FILE_BUFFER *
342 info_load_file_internal (filename, get_tags)
343      char *filename;
344      int get_tags;
345 {
346   char *fullpath, *contents;
347   long filesize;
348   struct stat finfo;
349   int retcode;
350   FILE_BUFFER *file_buffer = (FILE_BUFFER *)NULL;
351
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);
355
356   /* Did we actually find the file? */
357   retcode = stat (fullpath, &finfo);
358
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. */
362   if (retcode < 0)
363     {
364       char *lowered_name;
365       char *basename;
366
367       lowered_name = strdup (filename);
368       basename = (char *) strrchr (lowered_name, '/');
369
370       if (basename)
371         basename++;
372       else
373         basename = lowered_name;
374
375       while (*basename)
376         {
377           if (isupper (*basename))
378             *basename = tolower (*basename);
379
380           basename++;
381         }
382
383       fullpath = info_find_fullpath (lowered_name);
384       free (lowered_name);
385
386       retcode = stat (fullpath, &finfo);
387     }
388
389   /* If the file wasn't found, give up, returning a NULL pointer. */
390   if (retcode < 0)
391     {
392       filesys_error_number = errno;
393       return ((FILE_BUFFER *)NULL);
394     }
395
396   /* Otherwise, try to load the file. */
397   contents = filesys_read_info_file (fullpath, &filesize, &finfo);
398
399   if (!contents)
400     return ((FILE_BUFFER *)NULL);
401
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;
412
413   /* If requested, build the tags and nodes for this file buffer. */
414   if (get_tags)
415     build_tags_and_nodes (file_buffer);
416
417   return (file_buffer);
418 }
419
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. */
422 void
423 build_tags_and_nodes (file_buffer)
424      FILE_BUFFER *file_buffer;
425 {
426   SEARCH_BINDING binding;
427   long position;
428
429   free_file_buffer_tags (file_buffer);
430   file_buffer->flags &= ~N_HasTagsTable;
431
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;
436   if (binding.end < 0)
437     binding.end = 0;
438   binding.flags = S_FoldCase;
439
440   position = search_backward (TAGS_TABLE_END_LABEL, &binding);
441
442   /* If there is a tag table, find the start of it, and grovel over it
443      extracting tag information. */
444   if (position != -1)
445     while (1)
446       {
447         long tags_table_begin, tags_table_end;
448
449         binding.end = position;
450         binding.start = binding.end - 5 - strlen (TAGS_TABLE_END_LABEL);
451         if (binding.start < 0)
452           binding.start = 0;
453
454         position = find_node_separator (&binding);
455
456         /* For this test, (and all others here) failure indicates a bogus
457            tags table.  Grovel the file. */
458         if (position == -1)
459           break;
460
461         /* Remember the end of the tags table. */
462         binding.start = position;
463         tags_table_end = binding.start;
464         binding.end = 0;
465
466         /* Locate the start of the tags table. */
467         position = search_backward (TAGS_TABLE_BEG_LABEL, &binding);
468
469         if (position == -1)
470           break;
471
472         binding.end = position;
473         binding.start = binding.end - 5 - strlen (TAGS_TABLE_BEG_LABEL);
474         position = find_node_separator (&binding);
475
476         if (position == -1)
477           break;
478
479         /* The file contains a valid tags table.  Fill the FILE_BUFFER's
480            tags member. */
481         file_buffer->flags |= N_HasTagsTable;
482         tags_table_begin = position;
483
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;
489
490         if (!looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, &binding))
491           {
492             binding.start = tags_table_begin;
493             binding.end = tags_table_end;
494             get_nodes_of_tags_table (file_buffer, &binding);
495             return;
496           }
497         else
498           {
499             /* This is an indirect tags table.  Build TAGS member. */
500             SEARCH_BINDING indirect;
501
502             indirect.start = tags_table_begin;
503             indirect.end = 0;
504             indirect.buffer = binding.buffer;
505             indirect.flags = S_FoldCase;
506
507             position = search_backward (INDIRECT_TAGS_TABLE_LABEL, &indirect);
508
509             if (position == -1)
510               {
511                 /* This file is malformed.  Give up. */
512                 return;
513               }
514
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);
520             return;
521           }
522       }
523
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);
527 }
528
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. */
533 static void
534 get_nodes_of_info_file (file_buffer)
535      FILE_BUFFER *file_buffer;
536 {
537   long nodestart;
538   int tags_index = 0;
539   SEARCH_BINDING binding;
540
541   binding.buffer = file_buffer->contents;
542   binding.start = 0;
543   binding.end = file_buffer->filesize;
544   binding.flags = S_FoldCase;
545
546   while ((nodestart = find_node_separator (&binding)) != -1)
547     {
548       int start, end;
549       char *nodeline;
550       TAG *entry;
551
552       /* Skip past the characters just found. */
553       binding.start = nodestart;
554       binding.start += skip_node_separator (binding.buffer + binding.start);
555
556       /* Move to the start of the line defining the node. */
557       nodeline = binding.buffer + binding.start;
558
559       /* Find "Node:" */
560       start = string_in_line (INFO_NODE_LABEL, nodeline);
561
562       /* If not there, this is not the start of a node. */
563       if (start == -1)
564         continue;
565
566       /* Find the start of the nodename. */
567       start += skip_whitespace (nodeline + start);
568
569       /* Find the end of the nodename. */
570       end = start +
571         skip_node_characters (nodeline + start, DONT_SKIP_NEWLINES);
572
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;
580       {
581         SEARCH_BINDING node_body;
582
583         node_body.buffer = binding.buffer + binding.start;
584         node_body.start = 0;
585         node_body.end = binding.end - binding.start;
586         node_body.flags = S_FoldCase;
587         entry->nodelen = get_node_length (&node_body);
588       }
589
590       entry->filename = file_buffer->fullpath;
591
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 *);
595     }
596 }
597
598 /* Return the length of the node which starts at BINDING. */
599 static long
600 get_node_length (binding)
601      SEARCH_BINDING *binding;
602 {
603   register int i;
604   char *body;
605
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++)
609     {
610       if (body[i] == INFO_FF || body[i] == INFO_COOKIE)
611         break;
612     }
613   return ((long) i - binding->start);
614 }
615
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. */
618 static void
619 get_nodes_of_tags_table (file_buffer, buffer_binding)
620      FILE_BUFFER *file_buffer;
621      SEARCH_BINDING *buffer_binding;
622 {
623   int offset, tags_index = 0;
624   SEARCH_BINDING *search;
625   long position;
626
627   search = copy_binding (buffer_binding);
628
629   /* Find the start of the tags table. */
630   position = find_tags_table (search);
631
632   /* If none, we're all done. */
633   if (position == -1)
634     return;
635
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);
640   search->start--;
641
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)
645     {
646       TAG *entry;
647       char *nodedef;
648
649       /* Prepare to skip this line. */
650       search->start = position;
651       search->start++;
652
653       /* Skip past informative "(Indirect)" tags table line. */
654       if (!tags_index && looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, search))
655         continue;
656
657       /* Find the label preceding the node name. */
658       offset =
659         string_in_line (INFO_NODE_LABEL, search->buffer + search->start);
660
661       /* If not there, not a defining line, so we must be out of the
662          tags table. */
663       if (offset == -1)
664         break;
665
666       /* Point to the beginning of the node definition. */
667       search->start += offset;
668       nodedef = search->buffer + search->start;
669       nodedef += skip_whitespace (nodedef);
670
671       /* Move past the node's name. */
672       for (offset = 0;
673            (nodedef[offset]) && (nodedef[offset] != INFO_TAGSEP);
674            offset++);
675
676       if (nodedef[offset] != INFO_TAGSEP)
677         continue;
678
679       entry = (TAG *)xmalloc (sizeof (TAG));
680       entry->nodename = (char *)xmalloc (1 + offset);
681       strncpy (entry->nodename, nodedef, offset);
682       entry->nodename[offset] = '\0';
683       offset++;
684       entry->nodestart = (long) atol (nodedef + offset);
685
686       /* We don't know the length of this node yet. */
687       entry->nodelen = -1;
688
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;
692
693       /* Add this node structure to the array of node structures in this
694          FILE_BUFFER. */
695       add_pointer_to_array (entry, tags_index, file_buffer->tags,
696                             file_buffer->tags_slots, 100, TAG *);
697     }
698   free (search);
699 }
700
701 /* A structure used only in get_tags_of_indirect_tags_table () to hold onto
702    an intermediate value. */
703 typedef struct {
704   char *filename;
705   long first_byte;
706 } SUBFILE;
707
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. */
711 static void
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;
715 {
716   register int i;
717   SUBFILE **subfiles = (SUBFILE **)NULL;
718   int subfiles_index = 0, subfiles_slots = 0;
719   TAG *entry;
720
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);
724
725   /* We have the list of tags in file_buffer->tags.  Get the list of
726      subfiles from the indirect table. */
727   {
728     char *start, *end, *line;
729     SUBFILE *subfile;
730
731     start = indirect_binding->buffer + indirect_binding->start;
732     end = indirect_binding->buffer + indirect_binding->end;
733     line = start;
734
735     while (line < end)
736       {
737         int colon;
738
739         colon = string_in_line (":", line);
740
741         if (colon == -1)
742           break;
743
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);
749
750         add_pointer_to_array
751           (subfile, subfiles_index, subfiles, subfiles_slots, 10, SUBFILE *);
752
753         while (*line++ != '\n');
754       }
755   }
756
757   /* If we have successfully built the indirect files table, then
758      merge the information in the two tables. */
759   if (!subfiles)
760     {
761       free_file_buffer_tags (file_buffer);
762       return;
763     }
764   else
765     {
766       register int tags_index;
767       long header_length;
768       SEARCH_BINDING binding;
769
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;
776       binding.start = 0;
777       binding.end = file_buffer->filesize;
778       binding.flags = S_FoldCase;
779
780       header_length = find_node_separator (&binding);
781       if (header_length == -1)
782         header_length = 0;
783
784       /* Build the file buffer's list of subfiles. */
785       {
786         char *containing_dir, *temp;
787         int len_containing_dir;
788
789         containing_dir = strdup (file_buffer->fullpath);
790         temp = (char *) strrchr (containing_dir, '/');
791
792         if (temp)
793           *temp = '\0';
794
795         len_containing_dir = strlen (containing_dir);
796
797         for (i = 0; subfiles[i]; i++);
798
799         file_buffer->subfiles = (char **) xmalloc ((1 + i) * sizeof (char *));
800
801         for (i = 0; subfiles[i]; i++)
802           {
803             char *fullpath;
804
805             fullpath = (char *) xmalloc
806               (2 + strlen (subfiles[i]->filename) + len_containing_dir);
807
808             sprintf (fullpath, "%s/%s",
809                      containing_dir, subfiles[i]->filename);
810
811             file_buffer->subfiles[i] = fullpath;
812           }
813         file_buffer->subfiles[i] = (char *)NULL;
814         free (containing_dir);
815       }
816
817       /* For each node in the file's tags table, remember the starting
818          position. */
819       for (tags_index = 0;
820            entry = file_buffer->tags[tags_index];
821            tags_index++)
822         {
823           for (i = 0;
824                subfiles[i] && entry->nodestart >= subfiles[i]->first_byte;
825                i++);
826
827           /* If the Info file containing the indirect tags table is
828              malformed, then give up. */
829           if (!i)
830             {
831               /* The Info file containing the indirect tags table is
832                  malformed.  Give up. */
833               for (i = 0; subfiles[i]; i++)
834                 {
835                   free (subfiles[i]->filename);
836                   free (subfiles[i]);
837                   free (file_buffer->subfiles[i]);
838                 }
839               file_buffer->subfiles = (char **)NULL;
840               free_file_buffer_tags (file_buffer);
841               return;
842             }
843
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. */
848
849           entry->filename = file_buffer->subfiles[i - 1];
850           entry->nodestart -= subfiles[i -1]->first_byte;
851           entry->nodestart += header_length;
852           entry->nodelen = -1;
853         }
854
855       /* We have successfully built the tags table.  Remember that it
856          was indirect. */
857       file_buffer->flags |= N_TagsIndirect;
858     }
859
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++)
863     {
864       free (subfiles[i]->filename);
865       free (subfiles[i]);
866     }
867   free (subfiles);
868 }
869
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
872    a NULL pointer. */
873 static NODE *
874 info_node_of_file_buffer_tags (file_buffer, nodename)
875      FILE_BUFFER *file_buffer;
876      char *nodename;
877 {
878   register int i;
879   TAG *tag;
880
881   for (i = 0; tag = file_buffer->tags[i]; i++)
882     if (strcmp (nodename, tag->nodename) == 0)
883       {
884         FILE_BUFFER *subfile;
885
886         subfile = info_find_file_internal (tag->filename, INFO_NO_TAGS);
887
888         if (!subfile)
889           return ((NODE *)NULL);
890
891         if (!subfile->contents)
892           {
893             info_reload_file_buffer_contents (subfile);
894
895             if (!subfile->contents)
896               return ((NODE *)NULL);
897           }
898
899         /* If we were able to find this file and load it, then return
900            the node within it. */
901         {
902           NODE *node;
903
904           node = (NODE *)xmalloc (sizeof (NODE));
905           node->filename = (subfile->fullpath);
906           node->nodename = tag->nodename;
907           node->contents = subfile->contents + tag->nodestart;
908           node->flags    = 0;
909           node->parent   = (char *)NULL;
910
911           if (file_buffer->flags & N_HasTagsTable)
912             {
913               node->flags |= N_HasTagsTable;
914
915               if (file_buffer->flags & N_TagsIndirect)
916                 {
917                   node->flags |= N_TagsIndirect;
918                   node->parent = file_buffer->fullpath;
919                 }
920             }
921
922           if (subfile->flags & N_IsCompressed)
923             node->flags |= N_IsCompressed;
924
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)
932             {
933               int min, max;
934               char *node_sep;
935               SEARCH_BINDING node_body;
936               char *buff_end;
937
938               min = max = DEFAULT_INFO_FUDGE;
939
940               if (tag->nodestart < DEFAULT_INFO_FUDGE)
941                 min = tag->nodestart;
942
943               if (DEFAULT_INFO_FUDGE >
944                   (subfile->filesize - tag->nodestart))
945                 max = subfile->filesize - tag->nodestart;
946
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
950                  the separator. */
951               node_sep = adjust_nodestart (node, min, max);
952               if (node_sep == (char *)NULL)
953                 {
954                   free (node);
955                   return ((NODE *)NULL);
956                 }
957               /* Readjust tag->nodestart. */
958               tag->nodestart = node_sep - subfile->contents;
959
960               /* Calculate the length of the current node. */
961               buff_end = subfile->contents + subfile->filesize;
962
963               node_body.buffer = node->contents;
964               node_body.start = 0;
965               node_body.end = buff_end - node_body.buffer;
966               node_body.flags = 0;
967               tag->nodelen = get_node_length (&node_body);
968             }
969           else
970             {
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);
975             }
976
977           node->nodelen = tag->nodelen;
978           return (node);
979         }
980       }
981
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);
985 }
986 \f
987 /* **************************************************************** */
988 /*                                                                  */
989 /*              Managing file_buffers, nodes, and tags.             */
990 /*                                                                  */
991 /* **************************************************************** */
992
993 /* Create a new, empty file buffer. */
994 FILE_BUFFER *
995 make_file_buffer ()
996 {
997   FILE_BUFFER *file_buffer;
998
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;
1006
1007   return (file_buffer);
1008 }
1009
1010 /* Add FILE_BUFFER to our list of already loaded info files. */
1011 static void
1012 remember_info_file (file_buffer)
1013      FILE_BUFFER *file_buffer;
1014 {
1015   int i;
1016
1017   for (i = 0; info_loaded_files && info_loaded_files[i]; i++)
1018     ;
1019
1020   add_pointer_to_array (file_buffer, i, info_loaded_files,
1021                         info_loaded_files_slots, 10, FILE_BUFFER *);
1022 }
1023
1024 /* Forget the contents, tags table, nodes list, and names of FILENAME. */
1025 static void
1026 forget_info_file (filename)
1027      char *filename;
1028 {
1029   register int i;
1030   FILE_BUFFER *file_buffer;
1031
1032   if (!info_loaded_files)
1033     return;
1034
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))
1038       {
1039         free (file_buffer->filename);
1040         free (file_buffer->fullpath);
1041
1042         if (file_buffer->contents)
1043           free (file_buffer->contents);
1044         
1045         /* Note that free_file_buffer_tags () also kills the subfiles
1046            list, since the subfiles list is only of use in conjunction
1047            with tags. */
1048         free_file_buffer_tags (file_buffer);
1049
1050         while (info_loaded_files[i] = info_loaded_files[++i])
1051           ;
1052
1053         break;
1054       }
1055 }
1056
1057 /* Free the tags (if any) associated with FILE_BUFFER. */
1058 static void
1059 free_file_buffer_tags (file_buffer)
1060      FILE_BUFFER *file_buffer;
1061 {
1062   register int i;
1063
1064   if (file_buffer->tags)
1065     {
1066       register TAG *tag;
1067
1068       for (i = 0; tag = file_buffer->tags[i]; i++)
1069         free_info_tag (tag);
1070
1071       free (file_buffer->tags);
1072       file_buffer->tags = (TAG **)NULL;
1073       file_buffer->tags_slots = 0;
1074     }
1075
1076   if (file_buffer->subfiles)
1077     {
1078       for (i = 0; file_buffer->subfiles[i]; i++)
1079         free (file_buffer->subfiles[i]);
1080
1081       free (file_buffer->subfiles);
1082       file_buffer->subfiles = (char **)NULL;
1083     }
1084 }
1085
1086 /* Free the data associated with TAG, as well as TAG itself. */
1087 static void
1088 free_info_tag (tag)
1089      TAG *tag;
1090 {
1091   free (tag->nodename);
1092
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. */
1096
1097   free (tag);
1098 }
1099
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. */
1105 static void
1106 info_reload_file_buffer_contents (fb)
1107      FILE_BUFFER *fb;
1108 {
1109
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)
1113     return;
1114 #endif
1115
1116   fb->flags &= ~N_IsCompressed;
1117
1118   /* Let the filesystem do all the work for us. */
1119   fb->contents =
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;
1123 }
1124
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. */
1134 static char *
1135 adjust_nodestart (node, min, max)
1136      NODE *node;
1137      int min, max;
1138 {
1139   long position;
1140   SEARCH_BINDING node_body;
1141
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;
1147
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;
1152
1153   position = find_node_separator (&node_body);
1154
1155   /* If we found a node start, then check it out. */
1156   if (position != -1)
1157     {
1158       int sep_len;
1159
1160       sep_len = skip_node_separator (node->contents);
1161
1162       /* If we managed to skip a node separator, then check for this node
1163          being the right one. */
1164       if (sep_len != 0)
1165         {
1166           char *nodedef, *nodestart;
1167           int offset;
1168
1169           nodestart = node_body.buffer + position + sep_len;
1170           nodedef = nodestart;
1171           offset = string_in_line (INFO_NODE_LABEL, nodedef);
1172
1173           if (offset != -1)
1174             {
1175               nodedef += offset;
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))
1180                 {
1181                   node->contents = nodestart;
1182                   return (node_body.buffer + position);
1183                 }
1184             }
1185         }
1186     }
1187
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;
1193
1194   position = find_node_in_binding (node->nodename, &node_body);
1195
1196   /* If the node couldn't be found, we lose big. */
1197   if (position == -1)
1198     return ((char *)NULL);
1199
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);
1207 }