xref: /openbsd-src/gnu/usr.bin/texinfo/info/nodes.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /* nodes.c -- how to get an Info file and node.
2    $Id: nodes.c,v 1.1.1.3 2000/02/09 01:24:52 espie Exp $
3 
4    Copyright (C) 1993, 98, 99 Free Software Foundation, Inc.
5 
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)
9    any later version.
10 
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.
15 
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.
19 
20    Written by Brian Fox (bfox@ai.mit.edu). */
21 
22 #include "info.h"
23 
24 #include "nodes.h"
25 #include "search.h"
26 #include "filesys.h"
27 #include "info-utils.h"
28 
29 #if defined (HANDLE_MAN_PAGES)
30 #  include "man.h"
31 #endif /* HANDLE_MAN_PAGES */
32 
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 ();
41 
42 static long get_node_length ();
43 
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
47 
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
52 
53 /* Global variables.  */
54 
55 /* When non-zero, this is a string describing the recent file error. */
56 char *info_recent_file_error = NULL;
57 
58 /* The list of already loaded nodes. */
59 FILE_BUFFER **info_loaded_files = NULL;
60 
61 /* The number of slots currently allocated to LOADED_FILES. */
62 int info_loaded_files_slots = 0;
63 
64 /* Public functions for node manipulation.  */
65 
66 /* Used to build `dir' menu from `localdir' files found in INFOPATH. */
67 extern void maybe_build_dir_node ();
68 
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. */
73 NODE *
74 info_get_node (filename, nodename)
75      char *filename, *nodename;
76 {
77   NODE *node;
78   FILE_BUFFER *file_buffer = NULL;
79 
80   info_recent_file_error = NULL;
81   info_parse_node (nodename, DONT_SKIP_NEWLINES);
82   nodename = NULL;
83 
84   if (info_parsed_filename)
85     filename = info_parsed_filename;
86 
87   if (info_parsed_nodename)
88     nodename = info_parsed_nodename;
89 
90   /* If FILENAME is not specified, it defaults to "dir". */
91   if (!filename)
92     filename = "dir";
93 
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);
98 
99   /* Find the correct info file, or give up.  */
100   file_buffer = info_find_file (filename);
101   if (!file_buffer)
102     {
103       if (filesys_error_number)
104         info_recent_file_error =
105           filesys_error_string (filename, filesys_error_number);
106       return NULL;
107     }
108 
109   /* Look for the node.  */
110   node = info_get_node_of_file_buffer (nodename, file_buffer);
111 
112   /* If the node not found was "Top", try again with different case.  */
113   if (!node && (nodename == NULL || strcasecmp (nodename, "Top") == 0))
114     {
115       node = info_get_node_of_file_buffer ("Top", file_buffer);
116       if (!node)
117         node = info_get_node_of_file_buffer ("top", file_buffer);
118       if (!node)
119         node = info_get_node_of_file_buffer ("TOP", file_buffer);
120     }
121 
122   return node;
123 }
124 
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
128    NULL pointer. */
129 NODE *
130 info_get_node_of_file_buffer (nodename, file_buffer)
131      char *nodename;
132      FILE_BUFFER *file_buffer;
133 {
134   NODE *node = NULL;
135 
136   /* If we are unable to find the file, we have to give up.  There isn't
137      anything else we can do. */
138   if (!file_buffer)
139     return NULL;
140 
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);
144 
145   /* If NODENAME is not specified, it defaults to "Top". */
146   if (!nodename)
147     nodename = "Top";
148 
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
151      a node. */
152   if (strcmp (nodename, "*") == 0)
153     {
154       node = (NODE *)xmalloc (sizeof (NODE));
155       node->filename = file_buffer->fullpath;
156       node->parent   = NULL;
157       node->nodename = xstrdup ("*");
158       node->contents = file_buffer->contents;
159       node->nodelen = file_buffer->filesize;
160       node->flags = 0;
161       node->display_pos = 0;
162     }
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)
167     {
168         node = get_manpage_node (file_buffer, nodename);
169     }
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)
176     {
177       node = info_node_of_file_buffer_tags (file_buffer, nodename);
178     }
179 
180   /* Return the results of our node search. */
181   return node;
182 }
183 
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 *. */
189 FILE_BUFFER *
190 info_find_file (filename)
191      char *filename;
192 {
193   return info_find_file_internal (filename, INFO_GET_TAGS);
194 }
195 
196 /* Load the info file FILENAME, remembering information about it in a
197    file buffer. */
198 FILE_BUFFER *
199 info_load_file (filename)
200      char *filename;
201 {
202   return info_load_file_internal (filename, INFO_GET_TAGS);
203 }
204 
205 
206 /* Private functions implementation.  */
207 
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. */
213 static FILE_BUFFER *
214 info_find_file_internal (filename, get_tags)
215      char *filename;
216      int get_tags;
217 {
218   int i;
219   FILE_BUFFER *file_buffer;
220 
221   /* First try to find the file in our list of already loaded files. */
222   if (info_loaded_files)
223     {
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)) == 0))
230           {
231             struct stat new_info, *old_info;
232 
233             /* This file is loaded.  If the filename that we want is
234                specifically "dir", then simply return the file buffer. */
235             if (is_dir_name (filename_non_directory (filename)))
236               return file_buffer;
237 
238 #if defined (HANDLE_MAN_PAGES)
239             /* Do the same for the magic MANPAGE file. */
240             if (file_buffer->flags & N_IsManPage)
241               return file_buffer;
242 #endif /* HANDLE_MAN_PAGES */
243 
244             /* The file appears to be already loaded, and it is not "dir".
245                Check to see if it has changed since the last time it was
246                loaded. */
247             if (stat (file_buffer->fullpath, &new_info) == -1)
248               {
249                 filesys_error_number = errno;
250                 return NULL;
251               }
252 
253             old_info = &file_buffer->finfo;
254 
255             if ((new_info.st_size != old_info->st_size) ||
256                 (new_info.st_mtime != old_info->st_mtime))
257               {
258                 /* The file has changed.  Forget that we ever had loaded it
259                    in the first place. */
260                 forget_info_file (filename);
261                 break;
262               }
263             else
264               {
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
269                    object. */
270 		if (!file_buffer->contents)
271 		  {
272 		    /* The file's contents have been gc'ed.  Reload it.  */
273 		    info_reload_file_buffer_contents (file_buffer);
274 		    if (!file_buffer->contents)
275 		      return NULL;
276 		  }
277 
278                 if (get_tags && !file_buffer->tags)
279                   build_tags_and_nodes (file_buffer);
280 
281                 return file_buffer;
282               }
283           }
284     }
285 
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 ();
292   else
293 #endif /* HANDLE_MAN_PAGES */
294     file_buffer = info_load_file_internal (filename, get_tags);
295 
296   /* If the file was loaded, remember the name under which it was found. */
297   if (file_buffer)
298     remember_info_file (file_buffer);
299 
300   return file_buffer;
301 }
302 
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. */
307 static FILE_BUFFER *
308 info_load_file_internal (filename, get_tags)
309      char *filename;
310      int get_tags;
311 {
312   char *fullpath, *contents;
313   long filesize;
314   struct stat finfo;
315   int retcode, compressed;
316   FILE_BUFFER *file_buffer = NULL;
317 
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);
321 
322   /* Did we actually find the file? */
323   retcode = stat (fullpath, &finfo);
324 
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.  */
331   if (retcode < 0)
332     {
333       char *lowered_name;
334       char *basename;
335 
336       lowered_name = xstrdup (filename);
337       basename = filename_non_directory (lowered_name);
338 
339       while (*basename)
340         {
341           if (isupper (*basename))
342             *basename = tolower (*basename);
343 
344           basename++;
345         }
346 
347       fullpath = info_find_fullpath (lowered_name);
348       free (lowered_name);
349 
350       retcode = stat (fullpath, &finfo);
351     }
352 
353   /* If the file wasn't found, give up, returning a NULL pointer. */
354   if (retcode < 0)
355     {
356       filesys_error_number = errno;
357       return NULL;
358     }
359 
360   /* Otherwise, try to load the file. */
361   contents = filesys_read_info_file (fullpath, &filesize, &finfo, &compressed);
362 
363   if (!contents)
364     return NULL;
365 
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;
374   if (compressed)
375     file_buffer->flags |= N_IsCompressed;
376 
377   /* If requested, build the tags and nodes for this file buffer. */
378   if (get_tags)
379     build_tags_and_nodes (file_buffer);
380 
381   return file_buffer;
382 }
383 
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. */
386 void
387 build_tags_and_nodes (file_buffer)
388      FILE_BUFFER *file_buffer;
389 {
390   SEARCH_BINDING binding;
391   long position;
392 
393   free_file_buffer_tags (file_buffer);
394   file_buffer->flags &= ~N_HasTagsTable;
395 
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;
400   if (binding.end < 0)
401     binding.end = 0;
402   binding.flags = S_FoldCase;
403 
404   position = search_backward (TAGS_TABLE_END_LABEL, &binding);
405 
406   /* If there is a tag table, find the start of it, and grovel over it
407      extracting tag information. */
408   if (position != -1)
409     while (1)
410       {
411         long tags_table_begin, tags_table_end;
412 
413         binding.end = position;
414         binding.start = binding.end - 5 - strlen (TAGS_TABLE_END_LABEL);
415         if (binding.start < 0)
416           binding.start = 0;
417 
418         position = find_node_separator (&binding);
419 
420         /* For this test, (and all others here) failure indicates a bogus
421            tags table.  Grovel the file. */
422         if (position == -1)
423           break;
424 
425         /* Remember the end of the tags table. */
426         binding.start = position;
427         tags_table_end = binding.start;
428         binding.end = 0;
429 
430         /* Locate the start of the tags table. */
431         position = search_backward (TAGS_TABLE_BEG_LABEL, &binding);
432 
433         if (position == -1)
434           break;
435 
436         binding.end = position;
437         binding.start = binding.end - 5 - strlen (TAGS_TABLE_BEG_LABEL);
438         position = find_node_separator (&binding);
439 
440         if (position == -1)
441           break;
442 
443         /* The file contains a valid tags table.  Fill the FILE_BUFFER's
444            tags member. */
445         file_buffer->flags |= N_HasTagsTable;
446         tags_table_begin = position;
447 
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;
453 
454         if (!looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, &binding))
455           {
456             binding.start = tags_table_begin;
457             binding.end = tags_table_end;
458             get_nodes_of_tags_table (file_buffer, &binding);
459             return;
460           }
461         else
462           {
463             /* This is an indirect tags table.  Build TAGS member. */
464             SEARCH_BINDING indirect;
465 
466             indirect.start = tags_table_begin;
467             indirect.end = 0;
468             indirect.buffer = binding.buffer;
469             indirect.flags = S_FoldCase;
470 
471             position = search_backward (INDIRECT_TAGS_TABLE_LABEL, &indirect);
472 
473             if (position == -1)
474               {
475                 /* This file is malformed.  Give up. */
476                 return;
477               }
478 
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);
484             return;
485           }
486       }
487 
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);
491 }
492 
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. */
497 static void
498 get_nodes_of_info_file (file_buffer)
499      FILE_BUFFER *file_buffer;
500 {
501   long nodestart;
502   int tags_index = 0;
503   SEARCH_BINDING binding;
504 
505   binding.buffer = file_buffer->contents;
506   binding.start = 0;
507   binding.end = file_buffer->filesize;
508   binding.flags = S_FoldCase;
509 
510   while ((nodestart = find_node_separator (&binding)) != -1)
511     {
512       int start, end;
513       char *nodeline;
514       TAG *entry;
515       int anchor = 0;
516 
517       /* Skip past the characters just found. */
518       binding.start = nodestart;
519       binding.start += skip_node_separator (binding.buffer + binding.start);
520 
521       /* Move to the start of the line defining the node. */
522       nodeline = binding.buffer + binding.start;
523 
524       /* Find "Node:" */
525       start = string_in_line (INFO_NODE_LABEL, nodeline);
526       /* No Node:.  Maybe it's a Ref:.  */
527       if (start == -1)
528         {
529           start = string_in_line (INFO_REF_LABEL, nodeline);
530           if (start != -1)
531             anchor = 1;
532         }
533 
534       /* If not there, this is not the start of a node. */
535       if (start == -1)
536         continue;
537 
538       /* Find the start of the nodename. */
539       start += skip_whitespace (nodeline + start);
540 
541       /* Find the end of the nodename. */
542       end = start +
543         skip_node_characters (nodeline + start, DONT_SKIP_NEWLINES);
544 
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;
552       if (anchor)
553         entry->nodelen = 0;
554       else
555         {
556           SEARCH_BINDING node_body;
557 
558           node_body.buffer = binding.buffer + binding.start;
559           node_body.start = 0;
560           node_body.end = binding.end - binding.start;
561           node_body.flags = S_FoldCase;
562           entry->nodelen = get_node_length (&node_body);
563         }
564 
565       entry->filename = file_buffer->fullpath;
566 
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 *);
570     }
571 }
572 
573 /* Return the length of the node which starts at BINDING. */
574 static long
575 get_node_length (binding)
576      SEARCH_BINDING *binding;
577 {
578   int i;
579   char *body;
580 
581   /* [A node] ends with either a ^_, a ^L, or end of file.  */
582   for (i = binding->start, body = binding->buffer; i < binding->end; i++)
583     {
584       if (body[i] == INFO_FF || body[i] == INFO_COOKIE)
585         break;
586     }
587   return i - binding->start;
588 }
589 
590 /* Build and save the array of nodes in FILE_BUFFER by searching through the
591    contents of BUFFER_BINDING for a tags table, and groveling the contents. */
592 static void
593 get_nodes_of_tags_table (file_buffer, buffer_binding)
594      FILE_BUFFER *file_buffer;
595      SEARCH_BINDING *buffer_binding;
596 {
597   int name_offset;
598   SEARCH_BINDING *search;
599   long position;
600   int tags_index = 0;
601 
602   search = copy_binding (buffer_binding);
603 
604   /* Find the start of the tags table. */
605   position = find_tags_table (search);
606 
607   /* If none, we're all done. */
608   if (position == -1)
609     return;
610 
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);
615   search->start--;
616 
617   /* The tag table consists of lines containing node names and positions.
618      Do each line until we find one that doesn't contain a node name. */
619   while ((position = search_forward ("\n", search)) != -1)
620     {
621       TAG *entry;
622       char *nodedef;
623       unsigned p;
624       int anchor = 0;
625 
626       /* Prepare to skip this line. */
627       search->start = position;
628       search->start++;
629 
630       /* Skip past informative "(Indirect)" tags table line. */
631       if (!tags_index && looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, search))
632         continue;
633 
634       /* Find the label preceding the node name. */
635       name_offset =
636         string_in_line (INFO_NODE_LABEL, search->buffer + search->start);
637 
638       /* If no node label, maybe it's an anchor.  */
639       if (name_offset == -1)
640         {
641           name_offset = string_in_line (INFO_REF_LABEL,
642                                         search->buffer + search->start);
643           if (name_offset != -1)
644             anchor = 1;
645         }
646 
647       /* If not there, not a defining line, so we must be out of the
648          tags table.  */
649       if (name_offset == -1)
650         break;
651 
652       entry = xmalloc (sizeof (TAG));
653 
654       /* Find the beginning of the node definition. */
655       search->start += name_offset;
656       nodedef = search->buffer + search->start;
657       nodedef += skip_whitespace (nodedef);
658 
659       /* Move past the node's name in this tag to the TAGSEP character. */
660       for (p = 0; nodedef[p] && nodedef[p] != INFO_TAGSEP; p++)
661         ;
662       if (nodedef[p] != INFO_TAGSEP)
663         continue;
664 
665       entry->nodename = xmalloc (p + 1);
666       strncpy (entry->nodename, nodedef, p);
667       entry->nodename[p] = 0;
668       p++;
669       entry->nodestart = atol (nodedef + p);
670 
671       /* If a node, we don't know the length yet, but if it's an
672          anchor, the length is 0. */
673       entry->nodelen = anchor ? 0 : -1;
674 
675       /* The filename of this node is currently known as the same as the
676          name of this file. */
677       entry->filename = file_buffer->fullpath;
678 
679       /* Add this node structure to the array of node structures in this
680          FILE_BUFFER. */
681       add_pointer_to_array (entry, tags_index, file_buffer->tags,
682                             file_buffer->tags_slots, 100, TAG *);
683     }
684   free (search);
685 }
686 
687 /* A structure used only in `get_tags_of_indirect_tags_table' to hold onto
688    an intermediate value. */
689 typedef struct {
690   char *filename;
691   long first_byte;
692 } SUBFILE;
693 
694 /* Remember in FILE_BUFFER the nodenames, subfilenames, and offsets within the
695    subfiles of every node which appears in TAGS_BINDING.  The 2nd argument is
696    a binding surrounding the indirect files list. */
697 static void
698 get_tags_of_indirect_tags_table (file_buffer, indirect_binding, tags_binding)
699      FILE_BUFFER *file_buffer;
700      SEARCH_BINDING *indirect_binding, *tags_binding;
701 {
702   int i;
703   SUBFILE **subfiles = NULL;
704   int subfiles_index = 0, subfiles_slots = 0;
705   TAG *entry;
706 
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);
710 
711   /* We have the list of tags in file_buffer->tags.  Get the list of
712      subfiles from the indirect table. */
713   {
714     char *start, *end, *line;
715     SUBFILE *subfile;
716 
717     start = indirect_binding->buffer + indirect_binding->start;
718     end = indirect_binding->buffer + indirect_binding->end;
719     line = start;
720 
721     while (line < end)
722       {
723         int colon;
724 
725         colon = string_in_line (":", line);
726 
727         if (colon == -1)
728           break;
729 
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);
735 
736         add_pointer_to_array
737           (subfile, subfiles_index, subfiles, subfiles_slots, 10, SUBFILE *);
738 
739         while (*line++ != '\n');
740       }
741   }
742 
743   /* If we have successfully built the indirect files table, then
744      merge the information in the two tables. */
745   if (!subfiles)
746     {
747       free_file_buffer_tags (file_buffer);
748       return;
749     }
750   else
751     {
752       int tags_index;
753       long header_length;
754       SEARCH_BINDING binding;
755 
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;
762       binding.start = 0;
763       binding.end = file_buffer->filesize;
764       binding.flags = S_FoldCase;
765 
766       header_length = find_node_separator (&binding);
767       if (header_length == -1)
768         header_length = 0;
769 
770       /* Build the file buffer's list of subfiles. */
771       {
772         char *containing_dir = xstrdup (file_buffer->fullpath);
773 	char *temp = filename_non_directory (containing_dir);
774         int len_containing_dir;
775 
776 	if (temp > containing_dir)
777 	  {
778 	    if (HAVE_DRIVE (file_buffer->fullpath) &&
779 		temp == containing_dir + 2)
780 	      {
781 		/* Avoid converting "d:foo" into "d:/foo" below.  */
782 		*temp = '.';
783 		temp += 2;
784 	      }
785 	    temp[-1] = 0;
786 	  }
787 
788         len_containing_dir = strlen (containing_dir);
789 
790         for (i = 0; subfiles[i]; i++);
791 
792         file_buffer->subfiles = (char **) xmalloc ((1 + i) * sizeof (char *));
793 
794         for (i = 0; subfiles[i]; i++)
795           {
796             char *fullpath;
797 
798             fullpath = (char *) xmalloc
799               (2 + strlen (subfiles[i]->filename) + len_containing_dir);
800 
801             sprintf (fullpath, "%s/%s",
802                      containing_dir, subfiles[i]->filename);
803 
804             file_buffer->subfiles[i] = fullpath;
805           }
806         file_buffer->subfiles[i] = NULL;
807         free (containing_dir);
808       }
809 
810       /* For each node in the file's tags table, remember the starting
811          position. */
812       for (tags_index = 0; (entry = file_buffer->tags[tags_index]);
813            tags_index++)
814         {
815           for (i = 0;
816                subfiles[i] && entry->nodestart >= subfiles[i]->first_byte;
817                i++);
818 
819           /* If the Info file containing the indirect tags table is
820              malformed, then give up. */
821           if (!i)
822             {
823               /* The Info file containing the indirect tags table is
824                  malformed.  Give up. */
825               for (i = 0; subfiles[i]; i++)
826                 {
827                   free (subfiles[i]->filename);
828                   free (subfiles[i]);
829                   free (file_buffer->subfiles[i]);
830                 }
831               file_buffer->subfiles = NULL;
832               free_file_buffer_tags (file_buffer);
833               return;
834             }
835 
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. */
840 
841           entry->filename = file_buffer->subfiles[i - 1];
842           entry->nodestart -= subfiles[i - 1]->first_byte;
843           entry->nodestart += header_length;
844         }
845 
846       /* We have successfully built the tags table.  Remember that it
847          was indirect. */
848       file_buffer->flags |= N_TagsIndirect;
849     }
850 
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++)
854     {
855       free (subfiles[i]->filename);
856       free (subfiles[i]);
857     }
858   free (subfiles);
859 }
860 
861 
862 /* Return the node that contains TAG in FILE_BUFFER, else
863    (pathologically) NULL.  Called from info_node_of_file_buffer_tags.  */
864 static NODE *
865 find_node_of_anchor (file_buffer, tag)
866      FILE_BUFFER *file_buffer;
867      TAG *tag;
868 {
869   int anchor_pos, node_pos;
870   TAG *node_tag;
871   NODE *node;
872 
873   /* Look through the tag list for the anchor.  */
874   for (anchor_pos = 0; file_buffer->tags[anchor_pos]; anchor_pos++)
875     {
876       TAG *t = file_buffer->tags[anchor_pos];
877       if (t->nodestart == tag->nodestart)
878         break;
879     }
880 
881   /* Should not happen, because we should always find the anchor.  */
882   if (!file_buffer->tags[anchor_pos])
883     return NULL;
884 
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;
890        node_pos--)
891     ;
892 
893   /* An info file with an anchor before any nodes is pathological, but
894      it's possible, so don't crash.  */
895   if (node_pos < 0)
896     return NULL;
897 
898   /* We have the tag for the node that contained the anchor tag.  */
899   node_tag = file_buffer->tags[node_pos];
900 
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);
905 
906   /* Start displaying the node at the anchor position.  */
907   if (node)
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);
918 
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;
924 
925       /* Don't search in the node for the xref text, it's not there.  */
926       node->flags |= N_FromAnchor;
927     }
928 
929   return node;
930 }
931 
932 
933 /* Return the node from FILE_BUFFER which matches NODENAME by searching
934    the tags table in FILE_BUFFER, or NULL.  */
935 static NODE *
936 info_node_of_file_buffer_tags (file_buffer, nodename)
937      FILE_BUFFER *file_buffer;
938      char *nodename;
939 {
940   TAG *tag;
941   int i;
942 
943   for (i = 0; (tag = file_buffer->tags[i]); i++)
944     if (strcmp (nodename, tag->nodename) == 0)
945       {
946         FILE_BUFFER *subfile;
947 
948         subfile = info_find_file_internal (tag->filename, INFO_NO_TAGS);
949 
950         if (!subfile)
951           return NULL;
952 
953         if (!subfile->contents)
954           {
955             info_reload_file_buffer_contents (subfile);
956 
957             if (!subfile->contents)
958               return NULL;
959           }
960 
961         /* If we were able to find this file and load it, then return
962            the node within it. */
963         {
964           NODE *node = xmalloc (sizeof (NODE));
965           node->filename    = subfile->fullpath;
966           node->parent      = NULL;
967           node->nodename    = tag->nodename;
968           node->contents    = subfile->contents + tag->nodestart;
969           node->display_pos = 0;
970           node->flags       = 0;
971 
972           if (file_buffer->flags & N_HasTagsTable)
973             {
974               node->flags |= N_HasTagsTable;
975 
976               if (file_buffer->flags & N_TagsIndirect)
977                 {
978                   node->flags |= N_TagsIndirect;
979                   node->parent = file_buffer->fullpath;
980                 }
981             }
982 
983           if (subfile->flags & N_IsCompressed)
984             node->flags |= N_IsCompressed;
985 
986           /* If TAG->nodelen hasn't been calculated yet, then we aren't
987              in a position to trust the entry pointer.  Adjust things so
988              that ENTRY->nodestart gets the exact address of the start of
989              the node separator which starts this node, and NODE->contents
990              gets the address of the line defining this node.  If we cannot
991              do that, the node isn't really here. */
992           if (tag->nodelen == -1)
993             {
994               int min, max;
995               char *node_sep;
996               SEARCH_BINDING node_body;
997               char *buff_end;
998 
999               min = max = DEFAULT_INFO_FUDGE;
1000 
1001               if (tag->nodestart < DEFAULT_INFO_FUDGE)
1002                 min = tag->nodestart;
1003 
1004               if (DEFAULT_INFO_FUDGE >
1005                   (subfile->filesize - tag->nodestart))
1006                 max = subfile->filesize - tag->nodestart;
1007 
1008               /* NODE_SEP gets the address of the separator which defines
1009                  this node, or NULL if the node wasn't found.
1010                  NODE->contents is side-effected to point to right after
1011                  the separator. */
1012               node_sep = adjust_nodestart (node, min, max);
1013               if (node_sep == NULL)
1014                 {
1015                   free (node);
1016                   return NULL;
1017                 }
1018               /* Readjust tag->nodestart. */
1019               tag->nodestart = node_sep - subfile->contents;
1020 
1021               /* Calculate the length of the current node. */
1022               buff_end = subfile->contents + subfile->filesize;
1023 
1024               node_body.buffer = node->contents;
1025               node_body.start = 0;
1026               node_body.end = buff_end - node_body.buffer;
1027               node_body.flags = 0;
1028               tag->nodelen = get_node_length (&node_body);
1029               node->nodelen = tag->nodelen;
1030             }
1031 
1032           else if (tag->nodelen == 0) /* anchor, return containing node */
1033             {
1034               free (node);
1035               node = find_node_of_anchor (file_buffer, tag);
1036             }
1037 
1038           else
1039             {
1040               /* Since we know the length of this node, we have already
1041                  adjusted tag->nodestart to point to the exact start of
1042                  it.  Simply skip the node separator. */
1043               node->contents += skip_node_separator (node->contents);
1044               node->nodelen = tag->nodelen;
1045             }
1046 
1047           return node;
1048         }
1049       }
1050 
1051   /* There was a tag table for this file, and the node wasn't found.
1052      Return NULL, since this file doesn't contain the desired node. */
1053   return NULL;
1054 }
1055 
1056 /* Managing file_buffers, nodes, and tags.  */
1057 
1058 /* Create a new, empty file buffer. */
1059 FILE_BUFFER *
1060 make_file_buffer ()
1061 {
1062   FILE_BUFFER *file_buffer = xmalloc (sizeof (FILE_BUFFER));
1063 
1064   file_buffer->filename = file_buffer->fullpath = NULL;
1065   file_buffer->contents = NULL;
1066   file_buffer->tags = NULL;
1067   file_buffer->subfiles = NULL;
1068   file_buffer->tags_slots = 0;
1069   file_buffer->flags = 0;
1070 
1071   return file_buffer;
1072 }
1073 
1074 /* Add FILE_BUFFER to our list of already loaded info files. */
1075 static void
1076 remember_info_file (file_buffer)
1077      FILE_BUFFER *file_buffer;
1078 {
1079   int i;
1080 
1081   for (i = 0; info_loaded_files && info_loaded_files[i]; i++)
1082     ;
1083 
1084   add_pointer_to_array (file_buffer, i, info_loaded_files,
1085                         info_loaded_files_slots, 10, FILE_BUFFER *);
1086 }
1087 
1088 /* Forget the contents, tags table, nodes list, and names of FILENAME. */
1089 static void
1090 forget_info_file (filename)
1091      char *filename;
1092 {
1093   int i;
1094   FILE_BUFFER *file_buffer;
1095 
1096   if (!info_loaded_files)
1097     return;
1098 
1099   for (i = 0; file_buffer = info_loaded_files[i]; i++)
1100     if (FILENAME_CMP (filename, file_buffer->filename) == 0
1101         || FILENAME_CMP (filename, file_buffer->fullpath) == 0)
1102       {
1103         free (file_buffer->filename);
1104         free (file_buffer->fullpath);
1105 
1106         if (file_buffer->contents)
1107           free (file_buffer->contents);
1108 
1109         /* free_file_buffer_tags () also kills the subfiles list, since
1110            the subfiles list is only of use in conjunction with tags. */
1111         free_file_buffer_tags (file_buffer);
1112 
1113         /* Move rest of list down.  */
1114         while (info_loaded_files[i + 1])
1115           {
1116             info_loaded_files[i] = info_loaded_files[i + 1];
1117             i++;
1118           }
1119         info_loaded_files[i] = 0;
1120 
1121         break;
1122       }
1123 }
1124 
1125 /* Free the tags (if any) associated with FILE_BUFFER. */
1126 static void
1127 free_file_buffer_tags (file_buffer)
1128      FILE_BUFFER *file_buffer;
1129 {
1130   int i;
1131 
1132   if (file_buffer->tags)
1133     {
1134       TAG *tag;
1135 
1136       for (i = 0; (tag = file_buffer->tags[i]); i++)
1137         free_info_tag (tag);
1138 
1139       free (file_buffer->tags);
1140       file_buffer->tags = NULL;
1141       file_buffer->tags_slots = 0;
1142     }
1143 
1144   if (file_buffer->subfiles)
1145     {
1146       for (i = 0; file_buffer->subfiles[i]; i++)
1147         free (file_buffer->subfiles[i]);
1148 
1149       free (file_buffer->subfiles);
1150       file_buffer->subfiles = NULL;
1151     }
1152 }
1153 
1154 /* Free the data associated with TAG, as well as TAG itself. */
1155 static void
1156 free_info_tag (tag)
1157      TAG *tag;
1158 {
1159   free (tag->nodename);
1160 
1161   /* We don't free tag->filename, because that filename is part of the
1162      subfiles list for the containing FILE_BUFFER.  free_info_tags ()
1163      will free the subfiles when it is appropriate. */
1164 
1165   free (tag);
1166 }
1167 
1168 /* Load the contents of FILE_BUFFER->contents.  This function is called
1169    when a file buffer was loaded, and then in order to conserve memory, the
1170    file buffer's contents were freed and the pointer was zero'ed.  Note that
1171    the file was already loaded at least once successfully, so the tags and/or
1172    nodes members are still correctly filled. */
1173 static void
1174 info_reload_file_buffer_contents (fb)
1175      FILE_BUFFER *fb;
1176 {
1177   int is_compressed;
1178 
1179 #if defined (HANDLE_MAN_PAGES)
1180   /* If this is the magic manpage node, don't try to reload, just give up. */
1181   if (fb->flags & N_IsManPage)
1182     return;
1183 #endif
1184 
1185   fb->flags &= ~N_IsCompressed;
1186 
1187   /* Let the filesystem do all the work for us. */
1188   fb->contents =
1189     filesys_read_info_file (fb->fullpath, &(fb->filesize), &(fb->finfo),
1190 			    &is_compressed);
1191   if (is_compressed)
1192     fb->flags |= N_IsCompressed;
1193 }
1194 
1195 /* Return the actual starting memory location of NODE, side-effecting
1196    NODE->contents.  MIN and MAX are bounds for a search if one is necessary.
1197    Because of the way that tags are implemented, the physical nodestart may
1198    not actually be where the tag says it is.  If that is the case, but the
1199    node was found anyway, set N_UpdateTags in NODE->flags.  If the node is
1200    found, return non-zero.  NODE->contents is returned positioned right after
1201    the node separator that precedes this node, while the return value is
1202    position directly on the separator that precedes this node.  If the node
1203    could not be found, return a NULL pointer. */
1204 static char *
1205 adjust_nodestart (node, min, max)
1206      NODE *node;
1207      int min, max;
1208 {
1209   long position;
1210   SEARCH_BINDING node_body;
1211 
1212   /* Define the node body. */
1213   node_body.buffer = node->contents;
1214   node_body.start = 0;
1215   node_body.end = max;
1216   node_body.flags = 0;
1217 
1218   /* Try the optimal case first.  Who knows?  This file may actually be
1219      formatted (mostly) correctly. */
1220   if (node_body.buffer[0] != INFO_COOKIE && min > 2)
1221     node_body.buffer -= 3;
1222 
1223   position = find_node_separator (&node_body);
1224 
1225   /* If we found a node start, then check it out. */
1226   if (position != -1)
1227     {
1228       int sep_len;
1229 
1230       sep_len = skip_node_separator (node->contents);
1231 
1232       /* If we managed to skip a node separator, then check for this node
1233          being the right one. */
1234       if (sep_len != 0)
1235         {
1236           char *nodedef, *nodestart;
1237           int offset;
1238 
1239           nodestart = node_body.buffer + position + sep_len;
1240           nodedef = nodestart;
1241           offset = string_in_line (INFO_NODE_LABEL, nodedef);
1242 
1243           if (offset != -1)
1244             {
1245               nodedef += offset;
1246               nodedef += skip_whitespace (nodedef);
1247               offset = skip_node_characters (nodedef, DONT_SKIP_NEWLINES);
1248               if ((offset == strlen (node->nodename)) &&
1249                   (strncmp (node->nodename, nodedef, offset) == 0))
1250                 {
1251                   node->contents = nodestart;
1252                   return node_body.buffer + position;
1253                 }
1254             }
1255         }
1256     }
1257 
1258   /* Oh well, I guess we have to try to find it in a larger area. */
1259   node_body.buffer = node->contents - min;
1260   node_body.start = 0;
1261   node_body.end = min + max;
1262   node_body.flags = 0;
1263 
1264   position = find_node_in_binding (node->nodename, &node_body);
1265 
1266   /* If the node couldn't be found, we lose big. */
1267   if (position == -1)
1268     return NULL;
1269 
1270   /* Otherwise, the node was found, but the tags table could need updating
1271      (if we used a tag to get here, that is).  Set the flag in NODE->flags. */
1272   node->contents = node_body.buffer + position;
1273   node->contents += skip_node_separator (node->contents);
1274   if (node->flags & N_HasTagsTable)
1275     node->flags |= N_UpdateTags;
1276   return node_body.buffer + position;
1277 }
1278