Merge from vendor branch CVS:
[dragonfly.git] / contrib / texinfo / info / nodes.c
1 /* nodes.c -- how to get an Info file and node.
2    $Id: nodes.c,v 1.15 2000/11/11 00:40:37 karl Exp $
3
4    Copyright (C) 1993, 98, 99, 2000 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 \f
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 \f
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 \f
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))
230                     == 0))
231           {
232             struct stat new_info, *old_info;
233
234             /* This file is loaded.  If the filename that we want is
235                specifically "dir", then simply return the file buffer. */
236             if (is_dir_name (filename_non_directory (filename)))
237               return file_buffer;
238
239 #if defined (HANDLE_MAN_PAGES)
240             /* Do the same for the magic MANPAGE file. */
241             if (file_buffer->flags & N_IsManPage)
242               return file_buffer;
243 #endif /* HANDLE_MAN_PAGES */
244
245             /* The file appears to be already loaded, and is not "dir".  Check
246                to see if it's changed since the last time it was loaded.  */
247             if (stat (file_buffer->fullpath, &new_info) == -1)
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 \f
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   /* If no tags at all (possibly a misformatted info file), quit.  */
944   if (!file_buffer->tags) {
945     return NULL;
946   }
947   
948   for (i = 0; (tag = file_buffer->tags[i]); i++)    
949     if (strcmp (nodename, tag->nodename) == 0)
950       {
951         FILE_BUFFER *subfile = info_find_file_internal (tag->filename,
952                                                         INFO_NO_TAGS);
953         if (!subfile)
954           return NULL;
955
956         if (!subfile->contents)
957           {
958             info_reload_file_buffer_contents (subfile);
959             if (!subfile->contents)
960               return NULL;
961           }
962
963         /* If we were able to find this file and load it, then return
964            the node within it. */
965         {
966           NODE *node = xmalloc (sizeof (NODE));
967           node->filename    = subfile->fullpath;
968           node->parent      = NULL;
969           node->nodename    = tag->nodename;
970           node->contents    = subfile->contents + tag->nodestart;
971           node->display_pos = 0;
972           node->flags       = 0;
973
974           if (file_buffer->flags & N_HasTagsTable)
975             {
976               node->flags |= N_HasTagsTable;
977
978               if (file_buffer->flags & N_TagsIndirect)
979                 {
980                   node->flags |= N_TagsIndirect;
981                   node->parent = file_buffer->fullpath;
982                 }
983             }
984
985           if (subfile->flags & N_IsCompressed)
986             node->flags |= N_IsCompressed;
987
988           /* If TAG->nodelen hasn't been calculated yet, then we aren't
989              in a position to trust the entry pointer.  Adjust things so
990              that ENTRY->nodestart gets the exact address of the start of
991              the node separator which starts this node, and NODE->contents
992              gets the address of the line defining this node.  If we cannot
993              do that, the node isn't really here. */
994           if (tag->nodelen == -1)
995             {
996               int min, max;
997               char *node_sep;
998               SEARCH_BINDING node_body;
999               char *buff_end;
1000
1001               min = max = DEFAULT_INFO_FUDGE;
1002
1003               if (tag->nodestart < DEFAULT_INFO_FUDGE)
1004                 min = tag->nodestart;
1005
1006               if (DEFAULT_INFO_FUDGE >
1007                   (subfile->filesize - tag->nodestart))
1008                 max = subfile->filesize - tag->nodestart;
1009
1010               /* NODE_SEP gets the address of the separator which defines
1011                  this node, or NULL if the node wasn't found.
1012                  NODE->contents is side-effected to point to right after
1013                  the separator. */
1014               node_sep = adjust_nodestart (node, min, max);
1015               if (node_sep == NULL)
1016                 {
1017                   free (node);
1018                   return NULL;
1019                 }
1020               /* Readjust tag->nodestart. */
1021               tag->nodestart = node_sep - subfile->contents;
1022
1023               /* Calculate the length of the current node. */
1024               buff_end = subfile->contents + subfile->filesize;
1025
1026               node_body.buffer = node->contents;
1027               node_body.start = 0;
1028               node_body.end = buff_end - node_body.buffer;
1029               node_body.flags = 0;
1030               tag->nodelen = get_node_length (&node_body);
1031               node->nodelen = tag->nodelen;
1032             }
1033
1034           else if (tag->nodelen == 0) /* anchor, return containing node */
1035             {
1036               free (node);
1037               node = find_node_of_anchor (file_buffer, tag);
1038             }
1039             
1040           else
1041             {
1042               /* Since we know the length of this node, we have already
1043                  adjusted tag->nodestart to point to the exact start of
1044                  it.  Simply skip the node separator. */
1045               node->contents += skip_node_separator (node->contents);
1046               node->nodelen = tag->nodelen;
1047             }
1048
1049           return node;
1050         }
1051       }
1052
1053   /* There was a tag table for this file, and the node wasn't found.
1054      Return NULL, since this file doesn't contain the desired node. */
1055   return NULL;
1056 }
1057 \f
1058 /* Managing file_buffers, nodes, and tags.  */
1059
1060 /* Create a new, empty file buffer. */
1061 FILE_BUFFER *
1062 make_file_buffer ()
1063 {
1064   FILE_BUFFER *file_buffer = xmalloc (sizeof (FILE_BUFFER));
1065
1066   file_buffer->filename = file_buffer->fullpath = NULL;
1067   file_buffer->contents = NULL;
1068   file_buffer->tags = NULL;
1069   file_buffer->subfiles = NULL;
1070   file_buffer->tags_slots = 0;
1071   file_buffer->flags = 0;
1072
1073   return file_buffer;
1074 }
1075
1076 /* Add FILE_BUFFER to our list of already loaded info files. */
1077 static void
1078 remember_info_file (file_buffer)
1079      FILE_BUFFER *file_buffer;
1080 {
1081   int i;
1082
1083   for (i = 0; info_loaded_files && info_loaded_files[i]; i++)
1084     ;
1085
1086   add_pointer_to_array (file_buffer, i, info_loaded_files,
1087                         info_loaded_files_slots, 10, FILE_BUFFER *);
1088 }
1089
1090 /* Forget the contents, tags table, nodes list, and names of FILENAME. */
1091 static void
1092 forget_info_file (filename)
1093      char *filename;
1094 {
1095   int i;
1096   FILE_BUFFER *file_buffer;
1097
1098   if (!info_loaded_files)
1099     return;
1100
1101   for (i = 0; file_buffer = info_loaded_files[i]; i++)
1102     if (FILENAME_CMP (filename, file_buffer->filename) == 0
1103         || FILENAME_CMP (filename, file_buffer->fullpath) == 0)
1104       {
1105         free (file_buffer->filename);
1106         free (file_buffer->fullpath);
1107
1108         if (file_buffer->contents)
1109           free (file_buffer->contents);
1110         
1111         /* free_file_buffer_tags () also kills the subfiles list, since
1112            the subfiles list is only of use in conjunction with tags. */
1113         free_file_buffer_tags (file_buffer);
1114
1115         /* Move rest of list down.  */
1116         while (info_loaded_files[i + 1])
1117           {
1118             info_loaded_files[i] = info_loaded_files[i + 1];
1119             i++;
1120           }
1121         info_loaded_files[i] = 0;
1122
1123         break;
1124       }
1125 }
1126
1127 /* Free the tags (if any) associated with FILE_BUFFER. */
1128 static void
1129 free_file_buffer_tags (file_buffer)
1130      FILE_BUFFER *file_buffer;
1131 {
1132   int i;
1133
1134   if (file_buffer->tags)
1135     {
1136       TAG *tag;
1137
1138       for (i = 0; (tag = file_buffer->tags[i]); i++)
1139         free_info_tag (tag);
1140
1141       free (file_buffer->tags);
1142       file_buffer->tags = NULL;
1143       file_buffer->tags_slots = 0;
1144     }
1145
1146   if (file_buffer->subfiles)
1147     {
1148       for (i = 0; file_buffer->subfiles[i]; i++)
1149         free (file_buffer->subfiles[i]);
1150
1151       free (file_buffer->subfiles);
1152       file_buffer->subfiles = NULL;
1153     }
1154 }
1155
1156 /* Free the data associated with TAG, as well as TAG itself. */
1157 static void
1158 free_info_tag (tag)
1159      TAG *tag;
1160 {
1161   free (tag->nodename);
1162
1163   /* We don't free tag->filename, because that filename is part of the
1164      subfiles list for the containing FILE_BUFFER.  free_info_tags ()
1165      will free the subfiles when it is appropriate. */
1166
1167   free (tag);
1168 }
1169
1170 /* Load the contents of FILE_BUFFER->contents.  This function is called
1171    when a file buffer was loaded, and then in order to conserve memory, the
1172    file buffer's contents were freed and the pointer was zero'ed.  Note that
1173    the file was already loaded at least once successfully, so the tags and/or
1174    nodes members are still correctly filled. */
1175 static void
1176 info_reload_file_buffer_contents (fb)
1177      FILE_BUFFER *fb;
1178 {
1179   int is_compressed;
1180
1181 #if defined (HANDLE_MAN_PAGES)
1182   /* If this is the magic manpage node, don't try to reload, just give up. */
1183   if (fb->flags & N_IsManPage)
1184     return;
1185 #endif
1186
1187   fb->flags &= ~N_IsCompressed;
1188
1189   /* Let the filesystem do all the work for us. */
1190   fb->contents =
1191     filesys_read_info_file (fb->fullpath, &(fb->filesize), &(fb->finfo),
1192                             &is_compressed);
1193   if (is_compressed)
1194     fb->flags |= N_IsCompressed;
1195 }
1196
1197 /* Return the actual starting memory location of NODE, side-effecting
1198    NODE->contents.  MIN and MAX are bounds for a search if one is necessary.
1199    Because of the way that tags are implemented, the physical nodestart may
1200    not actually be where the tag says it is.  If that is the case, but the
1201    node was found anyway, set N_UpdateTags in NODE->flags.  If the node is
1202    found, return non-zero.  NODE->contents is returned positioned right after
1203    the node separator that precedes this node, while the return value is
1204    position directly on the separator that precedes this node.  If the node
1205    could not be found, return a NULL pointer. */
1206 static char *
1207 adjust_nodestart (node, min, max)
1208      NODE *node;
1209      int min, max;
1210 {
1211   long position;
1212   SEARCH_BINDING node_body;
1213
1214   /* Define the node body. */
1215   node_body.buffer = node->contents;
1216   node_body.start = 0;
1217   node_body.end = max;
1218   node_body.flags = 0;
1219
1220   /* Try the optimal case first.  Who knows?  This file may actually be
1221      formatted (mostly) correctly. */
1222   if (node_body.buffer[0] != INFO_COOKIE && min > 2)
1223     node_body.buffer -= 3;
1224
1225   position = find_node_separator (&node_body);
1226
1227   /* If we found a node start, then check it out. */
1228   if (position != -1)
1229     {
1230       int sep_len;
1231
1232       sep_len = skip_node_separator (node->contents);
1233
1234       /* If we managed to skip a node separator, then check for this node
1235          being the right one. */
1236       if (sep_len != 0)
1237         {
1238           char *nodedef, *nodestart;
1239           int offset;
1240
1241           nodestart = node_body.buffer + position + sep_len;
1242           nodedef = nodestart;
1243           offset = string_in_line (INFO_NODE_LABEL, nodedef);
1244
1245           if (offset != -1)
1246             {
1247               nodedef += offset;
1248               nodedef += skip_whitespace (nodedef);
1249               offset = skip_node_characters (nodedef, DONT_SKIP_NEWLINES);
1250               if ((offset == strlen (node->nodename)) &&
1251                   (strncmp (node->nodename, nodedef, offset) == 0))
1252                 {
1253                   node->contents = nodestart;
1254                   return node_body.buffer + position;
1255                 }
1256             }
1257         }
1258     }
1259
1260   /* Oh well, I guess we have to try to find it in a larger area. */
1261   node_body.buffer = node->contents - min;
1262   node_body.start = 0;
1263   node_body.end = min + max;
1264   node_body.flags = 0;
1265
1266   position = find_node_in_binding (node->nodename, &node_body);
1267
1268   /* If the node couldn't be found, we lose big. */
1269   if (position == -1)
1270     return NULL;
1271
1272   /* Otherwise, the node was found, but the tags table could need updating
1273      (if we used a tag to get here, that is).  Set the flag in NODE->flags. */
1274   node->contents = node_body.buffer + position;
1275   node->contents += skip_node_separator (node->contents);
1276   if (node->flags & N_HasTagsTable)
1277     node->flags |= N_UpdateTags;
1278   return node_body.buffer + position;
1279 }