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