Home | History | Annotate | Line # | Download | only in info
      1 /*	$NetBSD: nodes.c,v 1.2 2016/01/14 00:34:52 christos Exp $	*/
      2 
      3 /* nodes.c -- how to get an Info file and node.
      4    Id: nodes.c,v 1.4 2004/04/11 17:56:46 karl Exp
      5 
      6    Copyright (C) 1993, 1998, 1999, 2000, 2002, 2003, 2004 Free Software
      7    Foundation, Inc.
      8 
      9    This program is free software; you can redistribute it and/or modify
     10    it under the terms of the GNU General Public License as published by
     11    the Free Software Foundation; either version 2, or (at your option)
     12    any later version.
     13 
     14    This program is distributed in the hope that it will be useful,
     15    but WITHOUT ANY WARRANTY; without even the implied warranty of
     16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17    GNU General Public License for more details.
     18 
     19    You should have received a copy of the GNU General Public License
     20    along with this program; if not, write to the Free Software
     21    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     22 
     23    Originally written by Brian Fox (bfox (at) ai.mit.edu). */
     24 
     25 #include "info.h"
     26 
     27 #include "nodes.h"
     28 #include "search.h"
     29 #include "filesys.h"
     30 #include "info-utils.h"
     31 
     32 #if defined (HANDLE_MAN_PAGES)
     33 #  include "man.h"
     34 #endif /* HANDLE_MAN_PAGES */
     35 
     36 static void forget_info_file (char *filename);
     37 static void remember_info_file (FILE_BUFFER *file_buffer);
     38 static void free_file_buffer_tags (FILE_BUFFER *file_buffer);
     39 static void free_info_tag (TAG *tag);
     40 static void get_nodes_of_tags_table (FILE_BUFFER *file_buffer,
     41     SEARCH_BINDING *buffer_binding);
     42 static void get_nodes_of_info_file (FILE_BUFFER *file_buffer);
     43 static void get_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer,
     44     SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding);
     45 static void info_reload_file_buffer_contents (FILE_BUFFER *fb);
     46 static char *adjust_nodestart (NODE *node, int min, int max);
     47 static FILE_BUFFER *info_load_file_internal (char *filename, int get_tags);
     48 static FILE_BUFFER *info_find_file_internal (char *filename, int get_tags);
     49 static NODE *info_node_of_file_buffer_tags (FILE_BUFFER *file_buffer,
     50     char *nodename);
     51 
     52 static long get_node_length (SEARCH_BINDING *binding);
     53 
     54 /* Magic number that RMS used to decide how much a tags table pointer could
     55    be off by.  I feel that it should be much smaller, like 4.  */
     56 #define DEFAULT_INFO_FUDGE 1000
     57 
     58 /* Passed to *_internal functions.  INFO_GET_TAGS says to do what is
     59    neccessary to fill in the nodes or tags arrays in FILE_BUFFER. */
     60 #define INFO_NO_TAGS  0
     61 #define INFO_GET_TAGS 1
     62 
     63 /* Global variables.  */
     65 
     66 /* When non-zero, this is a string describing the recent file error. */
     67 char *info_recent_file_error = NULL;
     68 
     69 /* The list of already loaded nodes. */
     70 FILE_BUFFER **info_loaded_files = NULL;
     71 
     72 /* The number of slots currently allocated to LOADED_FILES. */
     73 int info_loaded_files_slots = 0;
     74 
     75 /* Public functions for node manipulation.  */
     77 
     78 /* Used to build `dir' menu from `localdir' files found in INFOPATH. */
     79 extern void maybe_build_dir_node (char *dirname);
     80 
     81 /* Return a pointer to a NODE structure for the Info node (FILENAME)NODENAME.
     82    If FILENAME is NULL, `dir' is used.
     83    IF NODENAME is NULL, `Top' is used.
     84    If the node cannot be found, return NULL. */
     85 NODE *
     86 info_get_node (char *filename, char *nodename)
     87 {
     88   NODE *node;
     89   FILE_BUFFER *file_buffer = NULL;
     90 
     91   info_recent_file_error = NULL;
     92   info_parse_node (nodename, DONT_SKIP_NEWLINES);
     93   nodename = NULL;
     94 
     95   if (info_parsed_filename)
     96     filename = info_parsed_filename;
     97 
     98   if (info_parsed_nodename)
     99     nodename = info_parsed_nodename;
    100 
    101   /* If FILENAME is not specified, it defaults to "dir". */
    102   if (!filename)
    103     filename = "dir";
    104 
    105   /* If the file to be looked up is "dir", build the contents from all of
    106      the "dir"s and "localdir"s found in INFOPATH. */
    107   if (is_dir_name (filename))
    108     maybe_build_dir_node (filename);
    109 
    110   /* Find the correct info file, or give up.  */
    111   file_buffer = info_find_file (filename);
    112   if (!file_buffer)
    113     {
    114       if (filesys_error_number)
    115         info_recent_file_error =
    116           filesys_error_string (filename, filesys_error_number);
    117       return NULL;
    118     }
    119 
    120   /* Look for the node.  */
    121   node = info_get_node_of_file_buffer (nodename, file_buffer);
    122 
    123   /* If the node not found was "Top", try again with different case.  */
    124   if (!node && (nodename == NULL || strcasecmp (nodename, "Top") == 0))
    125     {
    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       if (!node)
    130         node = info_get_node_of_file_buffer ("TOP", file_buffer);
    131     }
    132 
    133   return node;
    134 }
    135 
    136 /* Return a pointer to a NODE structure for the Info node NODENAME in
    137    FILE_BUFFER.  NODENAME can be passed as NULL, in which case the
    138    nodename of "Top" is used.  If the node cannot be found, return a
    139    NULL pointer. */
    140 NODE *
    141 info_get_node_of_file_buffer (char *nodename, FILE_BUFFER *file_buffer)
    142 {
    143   NODE *node = NULL;
    144 
    145   /* If we are unable to find the file, we have to give up.  There isn't
    146      anything else we can do. */
    147   if (!file_buffer)
    148     return NULL;
    149 
    150   /* If the file buffer was gc'ed, reload the contents now. */
    151   if (!file_buffer->contents)
    152     info_reload_file_buffer_contents (file_buffer);
    153 
    154   /* If NODENAME is not specified, it defaults to "Top". */
    155   if (!nodename)
    156     nodename = "Top";
    157 
    158   /* If the name of the node that we wish to find is exactly "*", then the
    159      node body is the contents of the entire file.  Create and return such
    160      a node. */
    161   if (strcmp (nodename, "*") == 0)
    162     {
    163       node = (NODE *)xmalloc (sizeof (NODE));
    164       node->filename = file_buffer->fullpath;
    165       node->parent   = NULL;
    166       node->nodename = xstrdup ("*");
    167       node->contents = file_buffer->contents;
    168       node->nodelen = file_buffer->filesize;
    169       node->flags = 0;
    170       node->display_pos = 0;
    171     }
    172 #if defined (HANDLE_MAN_PAGES)
    173   /* If the file buffer is the magic one associated with manpages, call
    174      the manpage node finding function instead. */
    175   else if (file_buffer->flags & N_IsManPage)
    176     {
    177         node = get_manpage_node (file_buffer, nodename);
    178     }
    179 #endif /* HANDLE_MAN_PAGES */
    180   /* If this is the "main" info file, it might contain a tags table.  Search
    181      the tags table for an entry which matches the node that we want.  If
    182      there is a tags table, get the file which contains this node, but don't
    183      bother building a node list for it. */
    184   else if (file_buffer->tags)
    185     {
    186       node = info_node_of_file_buffer_tags (file_buffer, nodename);
    187     }
    188 
    189   /* Return the results of our node search. */
    190   return node;
    191 }
    192 
    193 /* Locate the file named by FILENAME, and return the information structure
    194    describing this file.  The file may appear in our list of loaded files
    195    already, or it may not.  If it does not already appear, find the file,
    196    and add it to the list of loaded files.  If the file cannot be found,
    197    return a NULL FILE_BUFFER *. */
    198 FILE_BUFFER *
    199 info_find_file (char *filename)
    200 {
    201   return info_find_file_internal (filename, INFO_GET_TAGS);
    202 }
    203 
    204 /* Load the info file FILENAME, remembering information about it in a
    205    file buffer. */
    206 FILE_BUFFER *
    207 info_load_file (char *filename)
    208 {
    209   return info_load_file_internal (filename, INFO_GET_TAGS);
    210 }
    211 
    212 
    213 /* Private functions implementation.  */
    215 
    216 /* The workhorse for info_find_file ().  Non-zero 2nd argument says to
    217    try to build a tags table (or otherwise glean the nodes) for this
    218    file once found.  By default, we build the tags table, but when this
    219    function is called by info_get_node () when we already have a valid
    220    tags table describing the nodes, it is unnecessary. */
    221 static FILE_BUFFER *
    222 info_find_file_internal (char *filename, int get_tags)
    223 {
    224   int i;
    225   FILE_BUFFER *file_buffer;
    226 
    227   /* First try to find the file in our list of already loaded files. */
    228   if (info_loaded_files)
    229     {
    230       for (i = 0; (file_buffer = info_loaded_files[i]); i++)
    231         if ((FILENAME_CMP (filename, file_buffer->filename) == 0)
    232             || (FILENAME_CMP (filename, file_buffer->fullpath) == 0)
    233             || (!IS_ABSOLUTE (filename)
    234                 && FILENAME_CMP (filename,
    235                                 filename_non_directory (file_buffer->fullpath))
    236                     == 0))
    237           {
    238             struct stat new_info, *old_info;
    239 
    240             /* This file is loaded.  If the filename that we want is
    241                specifically "dir", then simply return the file buffer. */
    242             if (is_dir_name (filename_non_directory (filename)))
    243               return file_buffer;
    244 
    245 #if defined (HANDLE_MAN_PAGES)
    246             /* Do the same for the magic MANPAGE file. */
    247             if (file_buffer->flags & N_IsManPage)
    248               return file_buffer;
    249 #endif /* HANDLE_MAN_PAGES */
    250 
    251             /* The file appears to be already loaded, and is not "dir".  Check
    252                to see if it's changed since the last time it was loaded.  */
    253             if (stat (file_buffer->fullpath, &new_info) == -1)
    254               {
    255                 filesys_error_number = errno;
    256                 return NULL;
    257               }
    258 
    259             old_info = &file_buffer->finfo;
    260 
    261             if (new_info.st_size != old_info->st_size
    262                 || new_info.st_mtime != old_info->st_mtime)
    263               {
    264                 /* The file has changed.  Forget that we ever had loaded it
    265                    in the first place. */
    266                 forget_info_file (filename);
    267                 break;
    268               }
    269             else
    270               {
    271                 /* The info file exists, and has not changed since the last
    272                    time it was loaded.  If the caller requested a nodes list
    273                    for this file, and there isn't one here, build the nodes
    274                    for this file_buffer.  In any case, return the file_buffer
    275                    object. */
    276                 if (!file_buffer->contents)
    277                   {
    278                     /* The file's contents have been gc'ed.  Reload it.  */
    279                     info_reload_file_buffer_contents (file_buffer);
    280                     if (!file_buffer->contents)
    281                       return NULL;
    282                   }
    283 
    284                 if (get_tags && !file_buffer->tags)
    285                   build_tags_and_nodes (file_buffer);
    286 
    287                 return file_buffer;
    288               }
    289           }
    290     }
    291 
    292   /* The file wasn't loaded.  Try to load it now. */
    293 #if defined (HANDLE_MAN_PAGES)
    294   /* If the name of the file that we want is our special file buffer for
    295      Unix manual pages, then create the file buffer, and return it now. */
    296   if (strcasecmp (filename, MANPAGE_FILE_BUFFER_NAME) == 0)
    297     file_buffer = create_manpage_file_buffer ();
    298   else
    299 #endif /* HANDLE_MAN_PAGES */
    300     file_buffer = info_load_file_internal (filename, get_tags);
    301 
    302   /* If the file was loaded, remember the name under which it was found. */
    303   if (file_buffer)
    304     remember_info_file (file_buffer);
    305 
    306   return file_buffer;
    307 }
    308 
    309 /* The workhorse function for info_load_file ().  Non-zero second argument
    310    says to build a list of tags (or nodes) for this file.  This is the
    311    default behaviour when info_load_file () is called, but it is not
    312    necessary when loading a subfile for which we already have tags. */
    313 static FILE_BUFFER *
    314 info_load_file_internal (char *filename, int get_tags)
    315 {
    316   char *fullpath, *contents;
    317   long filesize;
    318   struct stat finfo;
    319   int retcode, compressed;
    320   FILE_BUFFER *file_buffer = NULL;
    321 
    322   /* Get the full pathname of this file, as known by the info system.
    323      That is to say, search along INFOPATH and expand tildes, etc. */
    324   fullpath = info_find_fullpath (filename);
    325 
    326   /* Did we actually find the file? */
    327   retcode = stat (fullpath, &finfo);
    328 
    329   /* If the file referenced by the name returned from info_find_fullpath ()
    330      doesn't exist, then try again with the last part of the filename
    331      appearing in lowercase. */
    332   /* This is probably not needed at all on those systems which define
    333      FILENAME_CMP to be strcasecmp.  But let's do it anyway, lest some
    334      network redirector supports case sensitivity.  */
    335   if (retcode < 0)
    336     {
    337       char *lowered_name;
    338       char *tmp_basename;
    339 
    340       lowered_name = xstrdup (filename);
    341       tmp_basename = filename_non_directory (lowered_name);
    342 
    343       while (*tmp_basename)
    344         {
    345           if (isupper (*tmp_basename))
    346             *tmp_basename = tolower (*tmp_basename);
    347 
    348           tmp_basename++;
    349         }
    350 
    351       fullpath = info_find_fullpath (lowered_name);
    352 
    353       retcode = stat (fullpath, &finfo);
    354       free (lowered_name);
    355     }
    356 
    357   /* If the file wasn't found, give up, returning a NULL pointer. */
    358   if (retcode < 0)
    359     {
    360       filesys_error_number = errno;
    361       return NULL;
    362     }
    363 
    364   /* Otherwise, try to load the file. */
    365   contents = filesys_read_info_file (fullpath, &filesize, &finfo, &compressed);
    366 
    367   if (!contents)
    368     return NULL;
    369 
    370   /* The file was found, and can be read.  Allocate FILE_BUFFER and fill
    371      in the various members. */
    372   file_buffer = make_file_buffer ();
    373   file_buffer->filename = xstrdup (filename);
    374   file_buffer->fullpath = xstrdup (fullpath);
    375   file_buffer->finfo = finfo;
    376   file_buffer->filesize = filesize;
    377   file_buffer->contents = contents;
    378   if (compressed)
    379     file_buffer->flags |= N_IsCompressed;
    380 
    381   /* If requested, build the tags and nodes for this file buffer. */
    382   if (get_tags)
    383     build_tags_and_nodes (file_buffer);
    384 
    385   return file_buffer;
    386 }
    387 
    388 /* 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 = (SUBFILE *)xmalloc (sizeof (SUBFILE));
    732         subfile->filename = (char *)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 = (char **) xmalloc ((1 + i) * sizeof (char *));
    794 
    795         for (i = 0; subfiles[i]; i++)
    796           {
    797             char *fullpath;
    798 
    799             fullpath = (char *) 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 
   1055 /* Managing file_buffers, nodes, and tags.  */
   1057 
   1058 /* Create a new, empty file buffer. */
   1059 FILE_BUFFER *
   1060 make_file_buffer (void)
   1061 {
   1062   FILE_BUFFER *file_buffer = xmalloc (sizeof (FILE_BUFFER));
   1063 
   1064   file_buffer->filename = file_buffer->fullpath = NULL;
   1065   file_buffer->contents = NULL;
   1066   file_buffer->tags = NULL;
   1067   file_buffer->subfiles = NULL;
   1068   file_buffer->tags_slots = 0;
   1069   file_buffer->flags = 0;
   1070 
   1071   return file_buffer;
   1072 }
   1073 
   1074 /* Add FILE_BUFFER to our list of already loaded info files. */
   1075 static void
   1076 remember_info_file (FILE_BUFFER *file_buffer)
   1077 {
   1078   int i;
   1079 
   1080   for (i = 0; info_loaded_files && info_loaded_files[i]; i++)
   1081     ;
   1082 
   1083   add_pointer_to_array (file_buffer, i, info_loaded_files,
   1084                         info_loaded_files_slots, 10, FILE_BUFFER *);
   1085 }
   1086 
   1087 /* Forget the contents, tags table, nodes list, and names of FILENAME. */
   1088 static void
   1089 forget_info_file (char *filename)
   1090 {
   1091   int i;
   1092   FILE_BUFFER *file_buffer;
   1093 
   1094   if (!info_loaded_files)
   1095     return;
   1096 
   1097   for (i = 0; (file_buffer = info_loaded_files[i]); i++)
   1098     if (FILENAME_CMP (filename, file_buffer->filename) == 0
   1099         || FILENAME_CMP (filename, file_buffer->fullpath) == 0)
   1100       {
   1101         free (file_buffer->filename);
   1102         free (file_buffer->fullpath);
   1103 
   1104         if (file_buffer->contents)
   1105           free (file_buffer->contents);
   1106 
   1107         /* free_file_buffer_tags () also kills the subfiles list, since
   1108            the subfiles list is only of use in conjunction with tags. */
   1109         free_file_buffer_tags (file_buffer);
   1110 
   1111         /* Move rest of list down.  */
   1112         while (info_loaded_files[i + 1])
   1113           {
   1114             info_loaded_files[i] = info_loaded_files[i + 1];
   1115             i++;
   1116           }
   1117         info_loaded_files[i] = 0;
   1118 
   1119         break;
   1120       }
   1121 }
   1122 
   1123 /* Free the tags (if any) associated with FILE_BUFFER. */
   1124 static void
   1125 free_file_buffer_tags (FILE_BUFFER *file_buffer)
   1126 {
   1127   int i;
   1128 
   1129   if (file_buffer->tags)
   1130     {
   1131       TAG *tag;
   1132 
   1133       for (i = 0; (tag = file_buffer->tags[i]); i++)
   1134         free_info_tag (tag);
   1135 
   1136       free (file_buffer->tags);
   1137       file_buffer->tags = NULL;
   1138       file_buffer->tags_slots = 0;
   1139     }
   1140 
   1141   if (file_buffer->subfiles)
   1142     {
   1143       for (i = 0; file_buffer->subfiles[i]; i++)
   1144         free (file_buffer->subfiles[i]);
   1145 
   1146       free (file_buffer->subfiles);
   1147       file_buffer->subfiles = NULL;
   1148     }
   1149 }
   1150 
   1151 /* Free the data associated with TAG, as well as TAG itself. */
   1152 static void
   1153 free_info_tag (TAG *tag)
   1154 {
   1155   free (tag->nodename);
   1156 
   1157   /* We don't free tag->filename, because that filename is part of the
   1158      subfiles list for the containing FILE_BUFFER.  free_info_tags ()
   1159      will free the subfiles when it is appropriate. */
   1160 
   1161   free (tag);
   1162 }
   1163 
   1164 /* Load the contents of FILE_BUFFER->contents.  This function is called
   1165    when a file buffer was loaded, and then in order to conserve memory, the
   1166    file buffer's contents were freed and the pointer was zero'ed.  Note that
   1167    the file was already loaded at least once successfully, so the tags and/or
   1168    nodes members are still correctly filled. */
   1169 static void
   1170 info_reload_file_buffer_contents (FILE_BUFFER *fb)
   1171 {
   1172   int is_compressed;
   1173 
   1174 #if defined (HANDLE_MAN_PAGES)
   1175   /* If this is the magic manpage node, don't try to reload, just give up. */
   1176   if (fb->flags & N_IsManPage)
   1177     return;
   1178 #endif
   1179 
   1180   fb->flags &= ~N_IsCompressed;
   1181 
   1182   /* Let the filesystem do all the work for us. */
   1183   fb->contents =
   1184     filesys_read_info_file (fb->fullpath, &(fb->filesize), &(fb->finfo),
   1185                             &is_compressed);
   1186   if (is_compressed)
   1187     fb->flags |= N_IsCompressed;
   1188 }
   1189 
   1190 /* Return the actual starting memory location of NODE, side-effecting
   1191    NODE->contents.  MIN and MAX are bounds for a search if one is necessary.
   1192    Because of the way that tags are implemented, the physical nodestart may
   1193    not actually be where the tag says it is.  If that is the case, but the
   1194    node was found anyway, set N_UpdateTags in NODE->flags.  If the node is
   1195    found, return non-zero.  NODE->contents is returned positioned right after
   1196    the node separator that precedes this node, while the return value is
   1197    position directly on the separator that precedes this node.  If the node
   1198    could not be found, return a NULL pointer. */
   1199 static char *
   1200 adjust_nodestart (NODE *node, int min, int max)
   1201 {
   1202   long position;
   1203   SEARCH_BINDING node_body;
   1204 
   1205   /* Define the node body. */
   1206   node_body.buffer = node->contents;
   1207   node_body.start = 0;
   1208   node_body.end = max;
   1209   node_body.flags = 0;
   1210 
   1211   /* Try the optimal case first.  Who knows?  This file may actually be
   1212      formatted (mostly) correctly. */
   1213   if (node_body.buffer[0] != INFO_COOKIE && min > 2)
   1214     node_body.buffer -= 3;
   1215 
   1216   position = find_node_separator (&node_body);
   1217 
   1218   /* If we found a node start, then check it out. */
   1219   if (position != -1)
   1220     {
   1221       int sep_len;
   1222 
   1223       sep_len = skip_node_separator (node->contents);
   1224 
   1225       /* If we managed to skip a node separator, then check for this node
   1226          being the right one. */
   1227       if (sep_len != 0)
   1228         {
   1229           char *nodedef, *nodestart;
   1230           int offset;
   1231 
   1232           nodestart = node_body.buffer + position + sep_len;
   1233           nodedef = nodestart;
   1234           offset = string_in_line (INFO_NODE_LABEL, nodedef);
   1235 
   1236           if (offset != -1)
   1237             {
   1238               nodedef += offset;
   1239               nodedef += skip_whitespace (nodedef);
   1240               offset = skip_node_characters (nodedef, DONT_SKIP_NEWLINES);
   1241               if (((unsigned int) offset == strlen (node->nodename)) &&
   1242                   (strncmp (node->nodename, nodedef, offset) == 0))
   1243                 {
   1244                   node->contents = nodestart;
   1245                   return node_body.buffer + position;
   1246                 }
   1247             }
   1248         }
   1249     }
   1250 
   1251   /* Oh well, I guess we have to try to find it in a larger area. */
   1252   node_body.buffer = node->contents - min;
   1253   node_body.start = 0;
   1254   node_body.end = min + max;
   1255   node_body.flags = 0;
   1256 
   1257   position = find_node_in_binding (node->nodename, &node_body);
   1258 
   1259   /* If the node couldn't be found, we lose big. */
   1260   if (position == -1)
   1261     return NULL;
   1262 
   1263   /* Otherwise, the node was found, but the tags table could need updating
   1264      (if we used a tag to get here, that is).  Set the flag in NODE->flags. */
   1265   node->contents = node_body.buffer + position;
   1266   node->contents += skip_node_separator (node->contents);
   1267   if (node->flags & N_HasTagsTable)
   1268     node->flags |= N_UpdateTags;
   1269   return node_body.buffer + position;
   1270 }
   1271