Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / contrib / texinfo / info / nodes.c
1 /* nodes.c -- how to get an Info file and node.
2    $Id: nodes.c,v 1.11 2008/06/11 09:55:42 gray Exp $
3
4    Copyright (C) 1993, 1998, 1999, 2000, 2002, 2003, 2004, 2006, 2007,
5    2008 Free Software Foundation, Inc.
6
7    This program is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20    Originally 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 (char *filename);
34 static void remember_info_file (FILE_BUFFER *file_buffer);
35 static void free_file_buffer_tags (FILE_BUFFER *file_buffer);
36 static void free_info_tag (TAG *tag);
37 static void get_nodes_of_tags_table (FILE_BUFFER *file_buffer,
38     SEARCH_BINDING *buffer_binding);
39 static void get_nodes_of_info_file (FILE_BUFFER *file_buffer);
40 static void get_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer,
41     SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding);
42 static void info_reload_file_buffer_contents (FILE_BUFFER *fb);
43 static char *adjust_nodestart (NODE *node, int min, int max);
44 static FILE_BUFFER *info_load_file_internal (char *filename, int get_tags);
45 static FILE_BUFFER *info_find_file_internal (char *filename, int get_tags);
46 static NODE *info_node_of_file_buffer_tags (FILE_BUFFER *file_buffer,
47     char *nodename);
48
49 static long get_node_length (SEARCH_BINDING *binding);
50
51 /* Magic number that RMS used to decide how much a tags table pointer could
52    be off by.  I feel that it should be much smaller, like 4.  */
53 #define DEFAULT_INFO_FUDGE 1000
54
55 /* Passed to *_internal functions.  INFO_GET_TAGS says to do what is
56    neccessary to fill in the nodes or tags arrays in FILE_BUFFER. */
57 #define INFO_NO_TAGS  0
58 #define INFO_GET_TAGS 1
59 \f
60 /* Global variables.  */
61
62 /* When non-zero, this is a string describing the recent file error. */
63 char *info_recent_file_error = NULL;
64
65 /* The list of already loaded nodes. */
66 FILE_BUFFER **info_loaded_files = NULL;
67
68 /* The number of slots currently allocated to LOADED_FILES. */
69 int info_loaded_files_slots = 0;
70 \f
71 /* Public functions for node manipulation.  */
72
73 /* Used to build `dir' menu from `localdir' files found in INFOPATH. */
74 extern void maybe_build_dir_node (char *dirname);
75
76 /* Return a pointer to a NODE structure for the Info node (FILENAME)NODENAME.
77    If FILENAME is NULL, `dir' is used.
78    IF NODENAME is NULL, `Top' is used.
79    If the node cannot be found, return NULL. */
80 NODE *
81 info_get_node (char *filename, char *nodename)
82 {
83   NODE *node;
84   FILE_BUFFER *file_buffer = NULL;
85
86   info_recent_file_error = NULL;
87   info_parse_node (nodename, DONT_SKIP_NEWLINES);
88   nodename = NULL;
89
90   if (info_parsed_filename)
91     filename = info_parsed_filename;
92
93   if (info_parsed_nodename)
94     nodename = info_parsed_nodename;
95
96   /* If FILENAME is not specified, it defaults to "dir". */
97   if (!filename)
98     filename = "dir";
99
100   /* If the file to be looked up is "dir", build the contents from all of
101      the "dir"s and "localdir"s found in INFOPATH. */
102   if (is_dir_name (filename))
103     maybe_build_dir_node (filename);
104
105   /* Find the correct info file, or give up.  */
106   file_buffer = info_find_file (filename);
107   if (!file_buffer)
108     {
109       if (filesys_error_number)
110         info_recent_file_error =
111           filesys_error_string (filename, filesys_error_number);
112       return NULL;
113     }
114
115   /* Look for the node.  */
116   node = info_get_node_of_file_buffer (nodename, file_buffer);
117
118   /* If the node not found was "Top", try again with different case,
119      unless this was a man page.  */
120   if (!node
121       && mbscasecmp (filename, MANPAGE_FILE_BUFFER_NAME) != 0
122       && (nodename == NULL || mbscasecmp (nodename, "Top") == 0))
123     {
124       node = info_get_node_of_file_buffer ("Top", file_buffer);
125       if (!node)
126         node = info_get_node_of_file_buffer ("top", file_buffer);
127       if (!node)
128         node = info_get_node_of_file_buffer ("TOP", file_buffer);
129     }
130
131   return node;
132 }
133
134 /* Return a pointer to a NODE structure for the Info node NODENAME in
135    FILE_BUFFER.  NODENAME can be passed as NULL, in which case the
136    nodename of "Top" is used.  If the node cannot be found, return a
137    NULL pointer. */
138 NODE *
139 info_get_node_of_file_buffer (char *nodename, FILE_BUFFER *file_buffer)
140 {
141   NODE *node = NULL;
142   int implicit_nodename = 0;
143
144   /* If we are unable to find the file, we have to give up.  There isn't
145      anything else we can do. */
146   if (!file_buffer)
147     return NULL;
148
149   /* If the file buffer was gc'ed, reload the contents now. */
150   if (!file_buffer->contents)
151     info_reload_file_buffer_contents (file_buffer);
152
153   /* If NODENAME is not specified, it defaults to "Top". */
154   if (!nodename)
155     {
156       nodename = "Top";
157       implicit_nodename = 1;  /* don't return man page for top */
158     }
159
160   /* If the name of the node that we wish to find is exactly "*", then the
161      node body is the contents of the entire file.  Create and return such
162      a node. */
163   if (strcmp (nodename, "*") == 0)
164     {
165       node = xmalloc (sizeof (NODE));
166       node->filename = file_buffer->fullpath;
167       node->parent   = NULL;
168       node->nodename = xstrdup ("*");
169       node->contents = file_buffer->contents;
170       node->nodelen = file_buffer->filesize;
171       node->flags = 0;
172       node->display_pos = 0;
173     }
174 #if defined (HANDLE_MAN_PAGES)
175   /* If the file buffer is the magic one associated with manpages, call
176      the manpage node finding function instead. */
177   else if (!implicit_nodename && file_buffer->flags & N_IsManPage)
178     {
179       node = get_manpage_node (file_buffer, nodename);
180     }
181 #endif /* HANDLE_MAN_PAGES */
182   /* If this is the "main" info file, it might contain a tags table.  Search
183      the tags table for an entry which matches the node that we want.  If
184      there is a tags table, get the file which contains this node, but don't
185      bother building a node list for it. */
186   else if (file_buffer->tags)
187     {
188       node = info_node_of_file_buffer_tags (file_buffer, nodename);
189     }
190
191   /* Return the results of our node search. */
192   return node;
193 }
194
195 /* Locate the file named by FILENAME, and return the information structure
196    describing this file.  The file may appear in our list of loaded files
197    already, or it may not.  If it does not already appear, find the file,
198    and add it to the list of loaded files.  If the file cannot be found,
199    return a NULL FILE_BUFFER *. */
200 FILE_BUFFER *
201 info_find_file (char *filename)
202 {
203   return info_find_file_internal (filename, INFO_GET_TAGS);
204 }
205
206 /* Load the info file FILENAME, remembering information about it in a
207    file buffer. */
208 FILE_BUFFER *
209 info_load_file (char *filename)
210 {
211   return info_load_file_internal (filename, INFO_GET_TAGS);
212 }
213
214 \f
215 /* Private functions implementation.  */
216
217 /* The workhorse for info_find_file ().  Non-zero 2nd argument says to
218    try to build a tags table (or otherwise glean the nodes) for this
219    file once found.  By default, we build the tags table, but when this
220    function is called by info_get_node () when we already have a valid
221    tags table describing the nodes, it is unnecessary. */
222 static FILE_BUFFER *
223 info_find_file_internal (char *filename, int get_tags)
224 {
225   int i;
226   FILE_BUFFER *file_buffer;
227
228   /* First try to find the file in our list of already loaded files. */
229   if (info_loaded_files)
230     {
231       for (i = 0; (file_buffer = info_loaded_files[i]); i++)
232         if ((FILENAME_CMP (filename, file_buffer->filename) == 0)
233             || (FILENAME_CMP (filename, file_buffer->fullpath) == 0)
234             || (!IS_ABSOLUTE (filename)
235                 && FILENAME_CMP (filename,
236                                 filename_non_directory (file_buffer->fullpath))
237                     == 0))
238           {
239             struct stat new_info, *old_info;
240
241             /* This file is loaded.  If the filename that we want is
242                specifically "dir", then simply return the file buffer. */
243             if (is_dir_name (filename_non_directory (filename)))
244               return file_buffer;
245
246 #if defined (HANDLE_MAN_PAGES)
247             /* Do the same for the magic MANPAGE file. */
248             if (file_buffer->flags & N_IsManPage)
249               return file_buffer;
250 #endif /* HANDLE_MAN_PAGES */
251
252             /* The file appears to be already loaded, and is not "dir".  Check
253                to see if it's changed since the last time it was loaded.  */
254             if (stat (file_buffer->fullpath, &new_info) == -1)
255               {
256                 filesys_error_number = errno;
257                 return NULL;
258               }
259
260             old_info = &file_buffer->finfo;
261
262             if (new_info.st_size != old_info->st_size
263                 || new_info.st_mtime != old_info->st_mtime)
264               {
265                 /* The file has changed.  Forget that we ever had loaded it
266                    in the first place. */
267                 forget_info_file (filename);
268                 break;
269               }
270             else
271               {
272                 /* The info file exists, and has not changed since the last
273                    time it was loaded.  If the caller requested a nodes list
274                    for this file, and there isn't one here, build the nodes
275                    for this file_buffer.  In any case, return the file_buffer
276                    object. */
277                 if (!file_buffer->contents)
278                   {
279                     /* The file's contents have been gc'ed.  Reload it.  */
280                     info_reload_file_buffer_contents (file_buffer);
281                     if (!file_buffer->contents)
282                       return NULL;
283                   }
284
285                 if (get_tags && !file_buffer->tags)
286                   build_tags_and_nodes (file_buffer);
287
288                 return file_buffer;
289               }
290           }
291     }
292
293   /* The file wasn't loaded.  Try to load it now. */
294 #if defined (HANDLE_MAN_PAGES)
295   /* If the name of the file that we want is our special file buffer for
296      Unix manual pages, then create the file buffer, and return it now. */
297   if (mbscasecmp (filename, MANPAGE_FILE_BUFFER_NAME) == 0)
298     file_buffer = create_manpage_file_buffer ();
299   else
300 #endif /* HANDLE_MAN_PAGES */
301     file_buffer = info_load_file_internal (filename, get_tags);
302
303   /* If the file was loaded, remember the name under which it was found. */
304   if (file_buffer)
305     remember_info_file (file_buffer);
306
307   return file_buffer;
308 }
309
310 /* The workhorse function for info_load_file ().  Non-zero second argument
311    says to build a list of tags (or nodes) for this file.  This is the
312    default behaviour when info_load_file () is called, but it is not
313    necessary when loading a subfile for which we already have tags. */
314 static FILE_BUFFER *
315 info_load_file_internal (char *filename, int get_tags)
316 {
317   char *fullpath, *contents;
318   long filesize;
319   struct stat finfo;
320   int retcode, compressed;
321   FILE_BUFFER *file_buffer = NULL;
322
323   /* Get the full pathname of this file, as known by the info system.
324      That is to say, search along INFOPATH and expand tildes, etc. */
325   fullpath = info_find_fullpath (filename);
326
327   /* Did we actually find the file? */
328   retcode = stat (fullpath, &finfo);
329
330   /* If the file referenced by the name returned from info_find_fullpath ()
331      doesn't exist, then try again with the last part of the filename
332      appearing in lowercase. */
333   /* This is probably not needed at all on those systems which define
334      FILENAME_CMP to be mbscasecmp.  But let's do it anyway, lest some
335      network redirector supports case sensitivity.  */
336   if (retcode < 0)
337     {
338       char *lowered_name;
339       char *tmp_basename;
340
341       lowered_name = xstrdup (filename);
342       tmp_basename = filename_non_directory (lowered_name);
343
344       while (*tmp_basename)
345         {
346           if (isupper (*tmp_basename))
347             *tmp_basename = tolower (*tmp_basename);
348
349           tmp_basename++;
350         }
351
352       fullpath = info_find_fullpath (lowered_name);
353
354       retcode = stat (fullpath, &finfo);
355       free (lowered_name);
356     }
357
358   /* If the file wasn't found, give up, returning a NULL pointer. */
359   if (retcode < 0)
360     {
361       filesys_error_number = errno;
362       return NULL;
363     }
364
365   /* Otherwise, try to load the file. */
366   contents = filesys_read_info_file (fullpath, &filesize, &finfo, &compressed);
367
368   if (!contents)
369     return NULL;
370
371   /* The file was found, and can be read.  Allocate FILE_BUFFER and fill
372      in the various members. */
373   file_buffer = make_file_buffer ();
374   file_buffer->filename = xstrdup (filename);
375   file_buffer->fullpath = xstrdup (fullpath);
376   file_buffer->finfo = finfo;
377   file_buffer->filesize = filesize;
378   file_buffer->contents = contents;
379   if (compressed)
380     file_buffer->flags |= N_IsCompressed;
381
382   /* If requested, build the tags and nodes for this file buffer. */
383   if (get_tags)
384     build_tags_and_nodes (file_buffer);
385
386   return file_buffer;
387 }
388 \f
389 /* Grovel FILE_BUFFER->contents finding tags and nodes, and filling in the
390    various slots.  This can also be used to rebuild a tag or node table. */
391 void
392 build_tags_and_nodes (FILE_BUFFER *file_buffer)
393 {
394   SEARCH_BINDING binding;
395   long position;
396
397   free_file_buffer_tags (file_buffer);
398   file_buffer->flags &= ~N_HasTagsTable;
399
400   /* See if there is a tags table in this info file. */
401   binding.buffer = file_buffer->contents;
402   binding.start = file_buffer->filesize;
403   binding.end = binding.start - 1000;
404   if (binding.end < 0)
405     binding.end = 0;
406   binding.flags = S_FoldCase;
407
408   position = search_backward (TAGS_TABLE_END_LABEL, &binding);
409
410   /* If there is a tag table, find the start of it, and grovel over it
411      extracting tag information. */
412   if (position != -1)
413     while (1)
414       {
415         long tags_table_begin, tags_table_end;
416
417         binding.end = position;
418         binding.start = binding.end - 5 - strlen (TAGS_TABLE_END_LABEL);
419         if (binding.start < 0)
420           binding.start = 0;
421
422         position = find_node_separator (&binding);
423
424         /* For this test, (and all others here) failure indicates a bogus
425            tags table.  Grovel the file. */
426         if (position == -1)
427           break;
428
429         /* Remember the end of the tags table. */
430         binding.start = position;
431         tags_table_end = binding.start;
432         binding.end = 0;
433
434         /* Locate the start of the tags table. */
435         position = search_backward (TAGS_TABLE_BEG_LABEL, &binding);
436
437         if (position == -1)
438           break;
439
440         binding.end = position;
441         binding.start = binding.end - 5 - strlen (TAGS_TABLE_BEG_LABEL);
442         position = find_node_separator (&binding);
443
444         if (position == -1)
445           break;
446
447         /* The file contains a valid tags table.  Fill the FILE_BUFFER's
448            tags member. */
449         file_buffer->flags |= N_HasTagsTable;
450         tags_table_begin = position;
451
452         /* If this isn't an indirect tags table, just remember the nodes
453            described locally in this tags table.  Note that binding.end
454            is pointing to just after the beginning label. */
455         binding.start = binding.end;
456         binding.end = file_buffer->filesize;
457
458         if (!looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, &binding))
459           {
460             binding.start = tags_table_begin;
461             binding.end = tags_table_end;
462             get_nodes_of_tags_table (file_buffer, &binding);
463             return;
464           }
465         else
466           {
467             /* This is an indirect tags table.  Build TAGS member. */
468             SEARCH_BINDING indirect;
469
470             indirect.start = tags_table_begin;
471             indirect.end = 0;
472             indirect.buffer = binding.buffer;
473             indirect.flags = S_FoldCase;
474
475             position = search_backward (INDIRECT_TAGS_TABLE_LABEL, &indirect);
476
477             if (position == -1)
478               {
479                 /* This file is malformed.  Give up. */
480                 return;
481               }
482
483             indirect.start = position;
484             indirect.end = tags_table_begin;
485             binding.start = tags_table_begin;
486             binding.end = tags_table_end;
487             get_tags_of_indirect_tags_table (file_buffer, &indirect, &binding);
488             return;
489           }
490       }
491
492   /* This file doesn't contain any kind of tags table.  Grovel the
493      file and build node entries for it. */
494   get_nodes_of_info_file (file_buffer);
495 }
496
497 /* Search through FILE_BUFFER->contents building an array of TAG *,
498    one entry per each node present in the file.  Store the tags in
499    FILE_BUFFER->tags, and the number of allocated slots in
500    FILE_BUFFER->tags_slots. */
501 static void
502 get_nodes_of_info_file (FILE_BUFFER *file_buffer)
503 {
504   long nodestart;
505   int tags_index = 0;
506   SEARCH_BINDING binding;
507
508   binding.buffer = file_buffer->contents;
509   binding.start = 0;
510   binding.end = file_buffer->filesize;
511   binding.flags = S_FoldCase;
512
513   while ((nodestart = find_node_separator (&binding)) != -1)
514     {
515       int start, end;
516       char *nodeline;
517       TAG *entry;
518       int anchor = 0;
519
520       /* Skip past the characters just found. */
521       binding.start = nodestart;
522       binding.start += skip_node_separator (binding.buffer + binding.start);
523
524       /* Move to the start of the line defining the node. */
525       nodeline = binding.buffer + binding.start;
526
527       /* Find "Node:" */
528       start = string_in_line (INFO_NODE_LABEL, nodeline);
529       /* No Node:.  Maybe it's a Ref:.  */
530       if (start == -1)
531         {
532           start = string_in_line (INFO_REF_LABEL, nodeline);
533           if (start != -1)
534             anchor = 1;
535         }
536
537       /* If not there, this is not the start of a node. */
538       if (start == -1)
539         continue;
540
541       /* Find the start of the nodename. */
542       start += skip_whitespace (nodeline + start);
543
544       /* Find the end of the nodename. */
545       end = start +
546         skip_node_characters (nodeline + start, DONT_SKIP_NEWLINES);
547
548       /* Okay, we have isolated the node name, and we know where the
549          node starts.  Remember this information. */
550       entry = xmalloc (sizeof (TAG));
551       entry->nodename = xmalloc (1 + (end - start));
552       strncpy (entry->nodename, nodeline + start, end - start);
553       entry->nodename[end - start] = 0;
554       entry->nodestart = nodestart;
555       if (anchor)
556         entry->nodelen = 0;
557       else
558         {
559           SEARCH_BINDING node_body;
560
561           node_body.buffer = binding.buffer + binding.start;
562           node_body.start = 0;
563           node_body.end = binding.end - binding.start;
564           node_body.flags = S_FoldCase;
565           entry->nodelen = get_node_length (&node_body);
566         }
567
568       entry->filename = file_buffer->fullpath;
569
570       /* Add this tag to the array of tag structures in this FILE_BUFFER. */
571       add_pointer_to_array (entry, tags_index, file_buffer->tags,
572                             file_buffer->tags_slots, 100, TAG *);
573     }
574 }
575
576 /* Return the length of the node which starts at BINDING. */
577 static long
578 get_node_length (SEARCH_BINDING *binding)
579 {
580   int i;
581   char *body;
582
583   /* [A node] ends with either a ^_, a ^L, or end of file.  */
584   for (i = binding->start, body = binding->buffer; i < binding->end; i++)
585     {
586       if (body[i] == INFO_FF || body[i] == INFO_COOKIE)
587         break;
588     }
589   return i - binding->start;
590 }
591
592 /* Build and save the array of nodes in FILE_BUFFER by searching through the
593    contents of BUFFER_BINDING for a tags table, and groveling the contents. */
594 static void
595 get_nodes_of_tags_table (FILE_BUFFER *file_buffer,
596     SEARCH_BINDING *buffer_binding)
597 {
598   int name_offset;
599   SEARCH_BINDING *tmp_search;
600   long position;
601   int tags_index = 0;
602
603   tmp_search = copy_binding (buffer_binding);
604
605   /* Find the start of the tags table. */
606   position = find_tags_table (tmp_search);
607
608   /* If none, we're all done. */
609   if (position == -1)
610     return;
611
612   /* Move to one character before the start of the actual table. */
613   tmp_search->start = position;
614   tmp_search->start += skip_node_separator
615     (tmp_search->buffer + tmp_search->start);
616   tmp_search->start += strlen (TAGS_TABLE_BEG_LABEL);
617   tmp_search->start--;
618
619   /* The tag table consists of lines containing node names and positions.
620      Do each line until we find one that doesn't contain a node name. */
621   while ((position = search_forward ("\n", tmp_search)) != -1)
622     {
623       TAG *entry;
624       char *nodedef;
625       unsigned p;
626       int anchor = 0;
627
628       /* Prepare to skip this line. */
629       tmp_search->start = position;
630       tmp_search->start++;
631
632       /* Skip past informative "(Indirect)" tags table line. */
633       if (!tags_index && looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, tmp_search))
634         continue;
635
636       /* Find the label preceding the node name. */
637       name_offset =
638         string_in_line (INFO_NODE_LABEL, tmp_search->buffer + tmp_search->start);
639
640       /* If no node label, maybe it's an anchor.  */
641       if (name_offset == -1)
642         {
643           name_offset = string_in_line (INFO_REF_LABEL,
644               tmp_search->buffer + tmp_search->start);
645           if (name_offset != -1)
646             anchor = 1;
647         }
648
649       /* If not there, not a defining line, so we must be out of the
650          tags table.  */
651       if (name_offset == -1)
652         break;
653
654       entry = xmalloc (sizeof (TAG));
655
656       /* Find the beginning of the node definition. */
657       tmp_search->start += name_offset;
658       nodedef = tmp_search->buffer + tmp_search->start;
659       nodedef += skip_whitespace (nodedef);
660
661       /* Move past the node's name in this tag to the TAGSEP character. */
662       for (p = 0; nodedef[p] && nodedef[p] != INFO_TAGSEP; p++)
663         ;
664       if (nodedef[p] != INFO_TAGSEP)
665         continue;
666
667       entry->nodename = xmalloc (p + 1);
668       strncpy (entry->nodename, nodedef, p);
669       entry->nodename[p] = 0;
670       p++;
671       entry->nodestart = atol (nodedef + p);
672
673       /* If a node, we don't know the length yet, but if it's an
674          anchor, the length is 0. */
675       entry->nodelen = anchor ? 0 : -1;
676
677       /* The filename of this node is currently known as the same as the
678          name of this file. */
679       entry->filename = file_buffer->fullpath;
680
681       /* Add this node structure to the array of node structures in this
682          FILE_BUFFER. */
683       add_pointer_to_array (entry, tags_index, file_buffer->tags,
684                             file_buffer->tags_slots, 100, TAG *);
685     }
686   free (tmp_search);
687 }
688
689 /* A structure used only in `get_tags_of_indirect_tags_table' to hold onto
690    an intermediate value. */
691 typedef struct {
692   char *filename;
693   long first_byte;
694 } SUBFILE;
695
696 /* Remember in FILE_BUFFER the nodenames, subfilenames, and offsets within the
697    subfiles of every node which appears in TAGS_BINDING.  The 2nd argument is
698    a binding surrounding the indirect files list. */
699 static void
700 get_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer,
701     SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding)
702 {
703   int i;
704   SUBFILE **subfiles = NULL;
705   int subfiles_index = 0, subfiles_slots = 0;
706   TAG *entry;
707
708   /* First get the list of tags from the tags table.  Then lookup the
709      associated file in the indirect list for each tag, and update it. */
710   get_nodes_of_tags_table (file_buffer, tags_binding);
711
712   /* We have the list of tags in file_buffer->tags.  Get the list of
713      subfiles from the indirect table. */
714   {
715     char *start, *end, *line;
716     SUBFILE *subfile;
717
718     start = indirect_binding->buffer + indirect_binding->start;
719     end = indirect_binding->buffer + indirect_binding->end;
720     line = start;
721
722     while (line < end)
723       {
724         int colon;
725
726         colon = string_in_line (":", line);
727
728         if (colon == -1)
729           break;
730
731         subfile = xmalloc (sizeof (SUBFILE));
732         subfile->filename = xmalloc (colon);
733         strncpy (subfile->filename, line, colon - 1);
734         subfile->filename[colon - 1] = 0;
735         subfile->first_byte = (long) atol (line + colon);
736
737         add_pointer_to_array
738           (subfile, subfiles_index, subfiles, subfiles_slots, 10, SUBFILE *);
739
740         while (*line++ != '\n');
741       }
742   }
743
744   /* If we have successfully built the indirect files table, then
745      merge the information in the two tables. */
746   if (!subfiles)
747     {
748       free_file_buffer_tags (file_buffer);
749       return;
750     }
751   else
752     {
753       int tags_index;
754       long header_length;
755       SEARCH_BINDING binding;
756
757       /* Find the length of the header of the file containing the indirect
758          tags table.  This header appears at the start of every file.  We
759          want the absolute position of each node within each subfile, so
760          we subtract the start of the containing subfile from the logical
761          position of the node, and then add the length of the header in. */
762       binding.buffer = file_buffer->contents;
763       binding.start = 0;
764       binding.end = file_buffer->filesize;
765       binding.flags = S_FoldCase;
766
767       header_length = find_node_separator (&binding);
768       if (header_length == -1)
769         header_length = 0;
770
771       /* Build the file buffer's list of subfiles. */
772       {
773         char *containing_dir = xstrdup (file_buffer->fullpath);
774         char *temp = filename_non_directory (containing_dir);
775         int len_containing_dir;
776
777         if (temp > containing_dir)
778           {
779             if (HAVE_DRIVE (file_buffer->fullpath) &&
780                 temp == containing_dir + 2)
781               {
782                 /* Avoid converting "d:foo" into "d:/foo" below.  */
783                 *temp = '.';
784                 temp += 2;
785               }
786             temp[-1] = 0;
787           }
788
789         len_containing_dir = strlen (containing_dir);
790
791         for (i = 0; subfiles[i]; i++);
792
793         file_buffer->subfiles = xmalloc ((1 + i) * sizeof (char *));
794
795         for (i = 0; subfiles[i]; i++)
796           {
797             char *fullpath;
798
799             fullpath = xmalloc
800               (2 + strlen (subfiles[i]->filename) + len_containing_dir);
801
802             sprintf (fullpath, "%s/%s",
803                      containing_dir, subfiles[i]->filename);
804
805             file_buffer->subfiles[i] = fullpath;
806           }
807         file_buffer->subfiles[i] = NULL;
808         free (containing_dir);
809       }
810
811       /* For each node in the file's tags table, remember the starting
812          position. */
813       for (tags_index = 0; (entry = file_buffer->tags[tags_index]);
814            tags_index++)
815         {
816           for (i = 0;
817                subfiles[i] && entry->nodestart >= subfiles[i]->first_byte;
818                i++);
819
820           /* If the Info file containing the indirect tags table is
821              malformed, then give up. */
822           if (!i)
823             {
824               /* The Info file containing the indirect tags table is
825                  malformed.  Give up. */
826               for (i = 0; subfiles[i]; i++)
827                 {
828                   free (subfiles[i]->filename);
829                   free (subfiles[i]);
830                   free (file_buffer->subfiles[i]);
831                 }
832               file_buffer->subfiles = NULL;
833               free_file_buffer_tags (file_buffer);
834               return;
835             }
836
837           /* SUBFILES[i] is the index of the first subfile whose logical
838              first byte is greater than the logical offset of this node's
839              starting position.  This means that the subfile directly
840              preceding this one is the one containing the node. */
841
842           entry->filename = file_buffer->subfiles[i - 1];
843           entry->nodestart -= subfiles[i - 1]->first_byte;
844           entry->nodestart += header_length;
845         }
846
847       /* We have successfully built the tags table.  Remember that it
848          was indirect. */
849       file_buffer->flags |= N_TagsIndirect;
850     }
851
852   /* Free the structures assigned to SUBFILES.  Free the names as well
853      as the structures themselves, then finally, the array. */
854   for (i = 0; subfiles[i]; i++)
855     {
856       free (subfiles[i]->filename);
857       free (subfiles[i]);
858     }
859   free (subfiles);
860 }
861
862
863 /* Return the node that contains TAG in FILE_BUFFER, else
864    (pathologically) NULL.  Called from info_node_of_file_buffer_tags.  */
865 static NODE *
866 find_node_of_anchor (FILE_BUFFER *file_buffer, TAG *tag)
867 {
868   int anchor_pos, node_pos;
869   TAG *node_tag;
870   NODE *node;
871
872   /* Look through the tag list for the anchor.  */
873   for (anchor_pos = 0; file_buffer->tags[anchor_pos]; anchor_pos++)
874     {
875       TAG *t = file_buffer->tags[anchor_pos];
876       if (t->nodestart == tag->nodestart)
877         break;
878     }
879
880   /* Should not happen, because we should always find the anchor.  */
881   if (!file_buffer->tags[anchor_pos])
882     return NULL;
883
884   /* We've found the anchor.  Look backwards in the tag table for the
885      preceding node (we're assuming the tags are given in order),
886      skipping over any preceding anchors.  */
887   for (node_pos = anchor_pos - 1;
888        node_pos >= 0 && file_buffer->tags[node_pos]->nodelen == 0;
889        node_pos--)
890     ;
891
892   /* An info file with an anchor before any nodes is pathological, but
893      it's possible, so don't crash.  */
894   if (node_pos < 0)
895     return NULL;
896
897   /* We have the tag for the node that contained the anchor tag.  */
898   node_tag = file_buffer->tags[node_pos];
899
900   /* Look up the node name in the tag table to get the actual node.
901      This is a recursive call, but it can't recurse again, because we
902      call it with a real node.  */
903   node = info_node_of_file_buffer_tags (file_buffer, node_tag->nodename);
904
905   /* Start displaying the node at the anchor position.  */
906   if (node)
907     { /* The nodestart for real nodes is three characters before the `F'
908          in the `File:' line (a newline, the CTRL-_, and another
909          newline).  The nodestart for anchors is the actual position.
910          But we offset by only 2, rather than 3, because if an anchor is
911          at the beginning of a paragraph, it's nicer for it to end up on
912          the beginning of the first line of the paragraph rather than
913          the blank line before it.  (makeinfo has no way of knowing that
914          a paragraph is going to start, so we can't fix it there.)  */
915       node->display_pos = file_buffer->tags[anchor_pos]->nodestart
916                           - (node_tag->nodestart + 2);
917
918       /* Otherwise an anchor at the end of a node ends up displaying at
919          the end of the last line of the node (way over on the right of
920          the screen), which looks wrong.  */
921       if (node->display_pos >= (unsigned long) node->nodelen)
922         node->display_pos = node->nodelen - 1;
923
924       /* Don't search in the node for the xref text, it's not there.  */
925       node->flags |= N_FromAnchor;
926     }
927
928   return node;
929 }
930
931
932 /* Return the node from FILE_BUFFER which matches NODENAME by searching
933    the tags table in FILE_BUFFER, or NULL.  */
934 static NODE *
935 info_node_of_file_buffer_tags (FILE_BUFFER *file_buffer, char *nodename)
936 {
937   TAG *tag;
938   int i;
939
940   /* If no tags at all (possibly a misformatted info file), quit.  */
941   if (!file_buffer->tags) {
942     return NULL;
943   }
944
945   for (i = 0; (tag = file_buffer->tags[i]); i++)
946     if (strcmp (nodename, tag->nodename) == 0)
947       {
948         FILE_BUFFER *subfile = info_find_file_internal (tag->filename,
949                                                         INFO_NO_TAGS);
950         if (!subfile)
951           return NULL;
952
953         if (!subfile->contents)
954           {
955             info_reload_file_buffer_contents (subfile);
956             if (!subfile->contents)
957               return NULL;
958           }
959
960         /* If we were able to find this file and load it, then return
961            the node within it. */
962         {
963           NODE *node = xmalloc (sizeof (NODE));
964           node->filename    = subfile->fullpath;
965           node->parent      = NULL;
966           node->nodename    = tag->nodename;
967           node->contents    = subfile->contents + tag->nodestart;
968           node->display_pos = 0;
969           node->flags       = 0;
970
971           if (file_buffer->flags & N_HasTagsTable)
972             {
973               node->flags |= N_HasTagsTable;
974
975               if (file_buffer->flags & N_TagsIndirect)
976                 {
977                   node->flags |= N_TagsIndirect;
978                   node->parent = file_buffer->fullpath;
979                 }
980             }
981
982           if (subfile->flags & N_IsCompressed)
983             node->flags |= N_IsCompressed;
984
985           /* If TAG->nodelen hasn't been calculated yet, then we aren't
986              in a position to trust the entry pointer.  Adjust things so
987              that ENTRY->nodestart gets the exact address of the start of
988              the node separator which starts this node, and NODE->contents
989              gets the address of the line defining this node.  If we cannot
990              do that, the node isn't really here. */
991           if (tag->nodelen == -1)
992             {
993               int min, max;
994               char *node_sep;
995               SEARCH_BINDING node_body;
996               char *buff_end;
997
998               min = max = DEFAULT_INFO_FUDGE;
999
1000               if (tag->nodestart < DEFAULT_INFO_FUDGE)
1001                 min = tag->nodestart;
1002
1003               if (DEFAULT_INFO_FUDGE >
1004                   (subfile->filesize - tag->nodestart))
1005                 max = subfile->filesize - tag->nodestart;
1006
1007               /* NODE_SEP gets the address of the separator which defines
1008                  this node, or NULL if the node wasn't found.
1009                  NODE->contents is side-effected to point to right after
1010                  the separator. */
1011               node_sep = adjust_nodestart (node, min, max);
1012               if (node_sep == NULL)
1013                 {
1014                   free (node);
1015                   return NULL;
1016                 }
1017               /* Readjust tag->nodestart. */
1018               tag->nodestart = node_sep - subfile->contents;
1019
1020               /* Calculate the length of the current node. */
1021               buff_end = subfile->contents + subfile->filesize;
1022
1023               node_body.buffer = node->contents;
1024               node_body.start = 0;
1025               node_body.end = buff_end - node_body.buffer;
1026               node_body.flags = 0;
1027               tag->nodelen = get_node_length (&node_body);
1028               node->nodelen = tag->nodelen;
1029             }
1030
1031           else if (tag->nodelen == 0) /* anchor, return containing node */
1032             {
1033               free (node);
1034               node = find_node_of_anchor (file_buffer, tag);
1035             }
1036
1037           else
1038             {
1039               /* Since we know the length of this node, we have already
1040                  adjusted tag->nodestart to point to the exact start of
1041                  it.  Simply skip the node separator. */
1042               node->contents += skip_node_separator (node->contents);
1043               node->nodelen = tag->nodelen;
1044             }
1045
1046           return node;
1047         }
1048       }
1049
1050   /* There was a tag table for this file, and the node wasn't found.
1051      Return NULL, since this file doesn't contain the desired node. */
1052   return NULL;
1053 }
1054 \f
1055 /* Managing file_buffers, nodes, and tags.  */
1056
1057 /* Create a new, empty file buffer. */
1058 FILE_BUFFER *
1059 make_file_buffer (void)
1060 {
1061   FILE_BUFFER *file_buffer = xmalloc (sizeof (FILE_BUFFER));
1062
1063   file_buffer->filename = file_buffer->fullpath = NULL;
1064   file_buffer->contents = NULL;
1065   file_buffer->tags = NULL;
1066   file_buffer->subfiles = NULL;
1067   file_buffer->tags_slots = 0;
1068   file_buffer->flags = 0;
1069
1070   return file_buffer;
1071 }
1072
1073 /* Add FILE_BUFFER to our list of already loaded info files. */
1074 static void
1075 remember_info_file (FILE_BUFFER *file_buffer)
1076 {
1077   int i;
1078
1079   for (i = 0; info_loaded_files && info_loaded_files[i]; i++)
1080     ;
1081
1082   add_pointer_to_array (file_buffer, i, info_loaded_files,
1083                         info_loaded_files_slots, 10, FILE_BUFFER *);
1084 }
1085
1086 /* Forget the contents, tags table, nodes list, and names of FILENAME. */
1087 static void
1088 forget_info_file (char *filename)
1089 {
1090   int i;
1091   FILE_BUFFER *file_buffer;
1092
1093   if (!info_loaded_files)
1094     return;
1095
1096   for (i = 0; (file_buffer = info_loaded_files[i]); i++)
1097     if (FILENAME_CMP (filename, file_buffer->filename) == 0
1098         || FILENAME_CMP (filename, file_buffer->fullpath) == 0)
1099       {
1100         free (file_buffer->filename);
1101         free (file_buffer->fullpath);
1102
1103         if (file_buffer->contents)
1104           free (file_buffer->contents);
1105
1106         /* free_file_buffer_tags () also kills the subfiles list, since
1107            the subfiles list is only of use in conjunction with tags. */
1108         free_file_buffer_tags (file_buffer);
1109
1110         /* Move rest of list down.  */
1111         while (info_loaded_files[i + 1])
1112           {
1113             info_loaded_files[i] = info_loaded_files[i + 1];
1114             i++;
1115           }
1116         info_loaded_files[i] = 0;
1117
1118         break;
1119       }
1120 }
1121
1122 /* Free the tags (if any) associated with FILE_BUFFER. */
1123 static void
1124 free_file_buffer_tags (FILE_BUFFER *file_buffer)
1125 {
1126   int i;
1127
1128   if (file_buffer->tags)
1129     {
1130       TAG *tag;
1131
1132       for (i = 0; (tag = file_buffer->tags[i]); i++)
1133         free_info_tag (tag);
1134
1135       free (file_buffer->tags);
1136       file_buffer->tags = NULL;
1137       file_buffer->tags_slots = 0;
1138     }
1139
1140   if (file_buffer->subfiles)
1141     {
1142       for (i = 0; file_buffer->subfiles[i]; i++)
1143         free (file_buffer->subfiles[i]);
1144
1145       free (file_buffer->subfiles);
1146       file_buffer->subfiles = NULL;
1147     }
1148 }
1149
1150 /* Free the data associated with TAG, as well as TAG itself. */
1151 static void
1152 free_info_tag (TAG *tag)
1153 {
1154   free (tag->nodename);
1155
1156   /* We don't free tag->filename, because that filename is part of the
1157      subfiles list for the containing FILE_BUFFER.  free_info_tags ()
1158      will free the subfiles when it is appropriate. */
1159
1160   free (tag);
1161 }
1162
1163 /* Load the contents of FILE_BUFFER->contents.  This function is called
1164    when a file buffer was loaded, and then in order to conserve memory, the
1165    file buffer's contents were freed and the pointer was zero'ed.  Note that
1166    the file was already loaded at least once successfully, so the tags and/or
1167    nodes members are still correctly filled. */
1168 static void
1169 info_reload_file_buffer_contents (FILE_BUFFER *fb)
1170 {
1171   int is_compressed;
1172
1173 #if defined (HANDLE_MAN_PAGES)
1174   /* If this is the magic manpage node, don't try to reload, just give up. */
1175   if (fb->flags & N_IsManPage)
1176     return;
1177 #endif
1178
1179   fb->flags &= ~N_IsCompressed;
1180
1181   /* Let the filesystem do all the work for us. */
1182   fb->contents =
1183     filesys_read_info_file (fb->fullpath, &(fb->filesize), &(fb->finfo),
1184                             &is_compressed);
1185   if (is_compressed)
1186     fb->flags |= N_IsCompressed;
1187 }
1188
1189 /* Return the actual starting memory location of NODE, side-effecting
1190    NODE->contents.  MIN and MAX are bounds for a search if one is necessary.
1191    Because of the way that tags are implemented, the physical nodestart may
1192    not actually be where the tag says it is.  If that is the case, but the
1193    node was found anyway, set N_UpdateTags in NODE->flags.  If the node is
1194    found, return non-zero.  NODE->contents is returned positioned right after
1195    the node separator that precedes this node, while the return value is
1196    position directly on the separator that precedes this node.  If the node
1197    could not be found, return a NULL pointer. */
1198 static char *
1199 adjust_nodestart (NODE *node, int min, int max)
1200 {
1201   long position;
1202   SEARCH_BINDING node_body;
1203
1204   /* Define the node body. */
1205   node_body.buffer = node->contents;
1206   node_body.start = 0;
1207   node_body.end = max;
1208   node_body.flags = 0;
1209
1210   /* Try the optimal case first.  Who knows?  This file may actually be
1211      formatted (mostly) correctly. */
1212   if (node_body.buffer[0] != INFO_COOKIE && min > 2)
1213     node_body.buffer -= 3;
1214
1215   position = find_node_separator (&node_body);
1216
1217   /* If we found a node start, then check it out. */
1218   if (position != -1)
1219     {
1220       int sep_len;
1221
1222       sep_len = skip_node_separator (node->contents);
1223
1224       /* If we managed to skip a node separator, then check for this node
1225          being the right one. */
1226       if (sep_len != 0)
1227         {
1228           char *nodedef, *nodestart;
1229           int offset;
1230
1231           nodestart = node_body.buffer + position + sep_len;
1232           nodedef = nodestart;
1233           offset = string_in_line (INFO_NODE_LABEL, nodedef);
1234
1235           if (offset != -1)
1236             {
1237               nodedef += offset;
1238               nodedef += skip_whitespace (nodedef);
1239               offset = skip_node_characters (nodedef, DONT_SKIP_NEWLINES);
1240               if (((unsigned int) offset == strlen (node->nodename)) &&
1241                   (strncmp (node->nodename, nodedef, offset) == 0))
1242                 {
1243                   node->contents = nodestart;
1244                   return node_body.buffer + position;
1245                 }
1246             }
1247         }
1248     }
1249
1250   /* Oh well, I guess we have to try to find it in a larger area. */
1251   node_body.buffer = node->contents - min;
1252   node_body.start = 0;
1253   node_body.end = min + max;
1254   node_body.flags = 0;
1255
1256   position = find_node_in_binding (node->nodename, &node_body);
1257
1258   /* If the node couldn't be found, we lose big. */
1259   if (position == -1)
1260     return NULL;
1261
1262   /* Otherwise, the node was found, but the tags table could need updating
1263      (if we used a tag to get here, that is).  Set the flag in NODE->flags. */
1264   node->contents = node_body.buffer + position;
1265   node->contents += skip_node_separator (node->contents);
1266   if (node->flags & N_HasTagsTable)
1267     node->flags |= N_UpdateTags;
1268   return node_body.buffer + position;
1269 }