Back to index

tetex-bin  3.0
nodes.c
Go to the documentation of this file.
00001 /* nodes.c -- how to get an Info file and node.
00002    $Id: nodes.c,v 1.4 2004/04/11 17:56:46 karl Exp $
00003 
00004    Copyright (C) 1993, 1998, 1999, 2000, 2002, 2003, 2004 Free Software
00005    Foundation, Inc.
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2, or (at your option)
00010    any later version.
00011 
00012    This program is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015    GNU General Public License for more details.
00016 
00017    You should have received a copy of the GNU General Public License
00018    along with this program; if not, write to the Free Software
00019    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020 
00021    Originally written by Brian Fox (bfox@ai.mit.edu). */
00022 
00023 #include "info.h"
00024 
00025 #include "nodes.h"
00026 #include "search.h"
00027 #include "filesys.h"
00028 #include "info-utils.h"
00029 
00030 #if defined (HANDLE_MAN_PAGES)
00031 #  include "man.h"
00032 #endif /* HANDLE_MAN_PAGES */
00033 
00034 static void forget_info_file (char *filename);
00035 static void remember_info_file (FILE_BUFFER *file_buffer);
00036 static void free_file_buffer_tags (FILE_BUFFER *file_buffer);
00037 static void free_info_tag (TAG *tag);
00038 static void get_nodes_of_tags_table (FILE_BUFFER *file_buffer,
00039     SEARCH_BINDING *buffer_binding);
00040 static void get_nodes_of_info_file (FILE_BUFFER *file_buffer);
00041 static void get_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer,
00042     SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding);
00043 static void info_reload_file_buffer_contents (FILE_BUFFER *fb);
00044 static char *adjust_nodestart (NODE *node, int min, int max);
00045 static FILE_BUFFER *info_load_file_internal (char *filename, int get_tags);
00046 static FILE_BUFFER *info_find_file_internal (char *filename, int get_tags);
00047 static NODE *info_node_of_file_buffer_tags (FILE_BUFFER *file_buffer,
00048     char *nodename);
00049 
00050 static long get_node_length (SEARCH_BINDING *binding);
00051 
00052 /* Magic number that RMS used to decide how much a tags table pointer could
00053    be off by.  I feel that it should be much smaller, like 4.  */
00054 #define DEFAULT_INFO_FUDGE 1000
00055 
00056 /* Passed to *_internal functions.  INFO_GET_TAGS says to do what is
00057    neccessary to fill in the nodes or tags arrays in FILE_BUFFER. */
00058 #define INFO_NO_TAGS  0
00059 #define INFO_GET_TAGS 1
00060 
00061 /* Global variables.  */
00062 
00063 /* When non-zero, this is a string describing the recent file error. */
00064 char *info_recent_file_error = NULL;
00065 
00066 /* The list of already loaded nodes. */
00067 FILE_BUFFER **info_loaded_files = NULL;
00068 
00069 /* The number of slots currently allocated to LOADED_FILES. */
00070 int info_loaded_files_slots = 0;
00071 
00072 /* Public functions for node manipulation.  */
00073 
00074 /* Used to build `dir' menu from `localdir' files found in INFOPATH. */
00075 extern void maybe_build_dir_node (char *dirname);
00076 
00077 /* Return a pointer to a NODE structure for the Info node (FILENAME)NODENAME.
00078    If FILENAME is NULL, `dir' is used.
00079    IF NODENAME is NULL, `Top' is used.
00080    If the node cannot be found, return NULL. */
00081 NODE *
00082 info_get_node (char *filename, char *nodename)
00083 {
00084   NODE *node;
00085   FILE_BUFFER *file_buffer = NULL;
00086 
00087   info_recent_file_error = NULL;
00088   info_parse_node (nodename, DONT_SKIP_NEWLINES);
00089   nodename = NULL;
00090 
00091   if (info_parsed_filename)
00092     filename = info_parsed_filename;
00093 
00094   if (info_parsed_nodename)
00095     nodename = info_parsed_nodename;
00096 
00097   /* If FILENAME is not specified, it defaults to "dir". */
00098   if (!filename)
00099     filename = "dir";
00100 
00101   /* If the file to be looked up is "dir", build the contents from all of
00102      the "dir"s and "localdir"s found in INFOPATH. */
00103   if (is_dir_name (filename))
00104     maybe_build_dir_node (filename);
00105 
00106   /* Find the correct info file, or give up.  */
00107   file_buffer = info_find_file (filename);
00108   if (!file_buffer)
00109     {
00110       if (filesys_error_number)
00111         info_recent_file_error =
00112           filesys_error_string (filename, filesys_error_number);
00113       return NULL;
00114     }
00115 
00116   /* Look for the node.  */
00117   node = info_get_node_of_file_buffer (nodename, file_buffer);
00118 
00119   /* If the node not found was "Top", try again with different case.  */
00120   if (!node && (nodename == NULL || strcasecmp (nodename, "Top") == 0))
00121     {
00122       node = info_get_node_of_file_buffer ("Top", file_buffer);
00123       if (!node)
00124         node = info_get_node_of_file_buffer ("top", file_buffer);
00125       if (!node)
00126         node = info_get_node_of_file_buffer ("TOP", file_buffer);
00127     }
00128 
00129   return node;
00130 }
00131 
00132 /* Return a pointer to a NODE structure for the Info node NODENAME in
00133    FILE_BUFFER.  NODENAME can be passed as NULL, in which case the
00134    nodename of "Top" is used.  If the node cannot be found, return a
00135    NULL pointer. */
00136 NODE *
00137 info_get_node_of_file_buffer (char *nodename, FILE_BUFFER *file_buffer)
00138 {
00139   NODE *node = NULL;
00140 
00141   /* If we are unable to find the file, we have to give up.  There isn't
00142      anything else we can do. */
00143   if (!file_buffer)
00144     return NULL;
00145 
00146   /* If the file buffer was gc'ed, reload the contents now. */
00147   if (!file_buffer->contents)
00148     info_reload_file_buffer_contents (file_buffer);
00149 
00150   /* If NODENAME is not specified, it defaults to "Top". */
00151   if (!nodename)
00152     nodename = "Top";
00153 
00154   /* If the name of the node that we wish to find is exactly "*", then the
00155      node body is the contents of the entire file.  Create and return such
00156      a node. */
00157   if (strcmp (nodename, "*") == 0)
00158     {
00159       node = (NODE *)xmalloc (sizeof (NODE));
00160       node->filename = file_buffer->fullpath;
00161       node->parent   = NULL;
00162       node->nodename = xstrdup ("*");
00163       node->contents = file_buffer->contents;
00164       node->nodelen = file_buffer->filesize;
00165       node->flags = 0;
00166       node->display_pos = 0;
00167     }
00168 #if defined (HANDLE_MAN_PAGES)
00169   /* If the file buffer is the magic one associated with manpages, call
00170      the manpage node finding function instead. */
00171   else if (file_buffer->flags & N_IsManPage)
00172     {
00173         node = get_manpage_node (file_buffer, nodename);
00174     }
00175 #endif /* HANDLE_MAN_PAGES */
00176   /* If this is the "main" info file, it might contain a tags table.  Search
00177      the tags table for an entry which matches the node that we want.  If
00178      there is a tags table, get the file which contains this node, but don't
00179      bother building a node list for it. */
00180   else if (file_buffer->tags)
00181     {
00182       node = info_node_of_file_buffer_tags (file_buffer, nodename);
00183     }
00184 
00185   /* Return the results of our node search. */
00186   return node;
00187 }
00188 
00189 /* Locate the file named by FILENAME, and return the information structure
00190    describing this file.  The file may appear in our list of loaded files
00191    already, or it may not.  If it does not already appear, find the file,
00192    and add it to the list of loaded files.  If the file cannot be found,
00193    return a NULL FILE_BUFFER *. */
00194 FILE_BUFFER *
00195 info_find_file (char *filename)
00196 {
00197   return info_find_file_internal (filename, INFO_GET_TAGS);
00198 }
00199 
00200 /* Load the info file FILENAME, remembering information about it in a
00201    file buffer. */
00202 FILE_BUFFER *
00203 info_load_file (char *filename)
00204 {
00205   return info_load_file_internal (filename, INFO_GET_TAGS);
00206 }
00207 
00208 
00209 /* Private functions implementation.  */
00210 
00211 /* The workhorse for info_find_file ().  Non-zero 2nd argument says to
00212    try to build a tags table (or otherwise glean the nodes) for this
00213    file once found.  By default, we build the tags table, but when this
00214    function is called by info_get_node () when we already have a valid
00215    tags table describing the nodes, it is unnecessary. */
00216 static FILE_BUFFER *
00217 info_find_file_internal (char *filename, int get_tags)
00218 {
00219   int i;
00220   FILE_BUFFER *file_buffer;
00221 
00222   /* First try to find the file in our list of already loaded files. */
00223   if (info_loaded_files)
00224     {
00225       for (i = 0; (file_buffer = info_loaded_files[i]); i++)
00226         if ((FILENAME_CMP (filename, file_buffer->filename) == 0)
00227             || (FILENAME_CMP (filename, file_buffer->fullpath) == 0)
00228             || (!IS_ABSOLUTE (filename)
00229                 && FILENAME_CMP (filename,
00230                                 filename_non_directory (file_buffer->fullpath))
00231                     == 0))
00232           {
00233             struct stat new_info, *old_info;
00234 
00235             /* This file is loaded.  If the filename that we want is
00236                specifically "dir", then simply return the file buffer. */
00237             if (is_dir_name (filename_non_directory (filename)))
00238               return file_buffer;
00239 
00240 #if defined (HANDLE_MAN_PAGES)
00241             /* Do the same for the magic MANPAGE file. */
00242             if (file_buffer->flags & N_IsManPage)
00243               return file_buffer;
00244 #endif /* HANDLE_MAN_PAGES */
00245 
00246             /* The file appears to be already loaded, and is not "dir".  Check
00247                to see if it's changed since the last time it was loaded.  */
00248             if (stat (file_buffer->fullpath, &new_info) == -1)
00249               {
00250                 filesys_error_number = errno;
00251                 return NULL;
00252               }
00253 
00254             old_info = &file_buffer->finfo;
00255 
00256             if (new_info.st_size != old_info->st_size
00257                 || new_info.st_mtime != old_info->st_mtime)
00258               {
00259                 /* The file has changed.  Forget that we ever had loaded it
00260                    in the first place. */
00261                 forget_info_file (filename);
00262                 break;
00263               }
00264             else
00265               {
00266                 /* The info file exists, and has not changed since the last
00267                    time it was loaded.  If the caller requested a nodes list
00268                    for this file, and there isn't one here, build the nodes
00269                    for this file_buffer.  In any case, return the file_buffer
00270                    object. */
00271                 if (!file_buffer->contents)
00272                   {
00273                     /* The file's contents have been gc'ed.  Reload it.  */
00274                     info_reload_file_buffer_contents (file_buffer);
00275                     if (!file_buffer->contents)
00276                       return NULL;
00277                   }
00278 
00279                 if (get_tags && !file_buffer->tags)
00280                   build_tags_and_nodes (file_buffer);
00281 
00282                 return file_buffer;
00283               }
00284           }
00285     }
00286 
00287   /* The file wasn't loaded.  Try to load it now. */
00288 #if defined (HANDLE_MAN_PAGES)
00289   /* If the name of the file that we want is our special file buffer for
00290      Unix manual pages, then create the file buffer, and return it now. */
00291   if (strcasecmp (filename, MANPAGE_FILE_BUFFER_NAME) == 0)
00292     file_buffer = create_manpage_file_buffer ();
00293   else
00294 #endif /* HANDLE_MAN_PAGES */
00295     file_buffer = info_load_file_internal (filename, get_tags);
00296 
00297   /* If the file was loaded, remember the name under which it was found. */
00298   if (file_buffer)
00299     remember_info_file (file_buffer);
00300 
00301   return file_buffer;
00302 }
00303 
00304 /* The workhorse function for info_load_file ().  Non-zero second argument
00305    says to build a list of tags (or nodes) for this file.  This is the
00306    default behaviour when info_load_file () is called, but it is not
00307    necessary when loading a subfile for which we already have tags. */
00308 static FILE_BUFFER *
00309 info_load_file_internal (char *filename, int get_tags)
00310 {
00311   char *fullpath, *contents;
00312   long filesize;
00313   struct stat finfo;
00314   int retcode, compressed;
00315   FILE_BUFFER *file_buffer = NULL;
00316 
00317   /* Get the full pathname of this file, as known by the info system.
00318      That is to say, search along INFOPATH and expand tildes, etc. */
00319   fullpath = info_find_fullpath (filename);
00320 
00321   /* Did we actually find the file? */
00322   retcode = stat (fullpath, &finfo);
00323 
00324   /* If the file referenced by the name returned from info_find_fullpath ()
00325      doesn't exist, then try again with the last part of the filename
00326      appearing in lowercase. */
00327   /* This is probably not needed at all on those systems which define
00328      FILENAME_CMP to be strcasecmp.  But let's do it anyway, lest some
00329      network redirector supports case sensitivity.  */
00330   if (retcode < 0)
00331     {
00332       char *lowered_name;
00333       char *tmp_basename;
00334 
00335       lowered_name = xstrdup (filename);
00336       tmp_basename = filename_non_directory (lowered_name);
00337 
00338       while (*tmp_basename)
00339         {
00340           if (isupper (*tmp_basename))
00341             *tmp_basename = tolower (*tmp_basename);
00342 
00343           tmp_basename++;
00344         }
00345 
00346       fullpath = info_find_fullpath (lowered_name);
00347 
00348       retcode = stat (fullpath, &finfo);
00349       free (lowered_name);
00350     }
00351 
00352   /* If the file wasn't found, give up, returning a NULL pointer. */
00353   if (retcode < 0)
00354     {
00355       filesys_error_number = errno;
00356       return NULL;
00357     }
00358 
00359   /* Otherwise, try to load the file. */
00360   contents = filesys_read_info_file (fullpath, &filesize, &finfo, &compressed);
00361 
00362   if (!contents)
00363     return NULL;
00364 
00365   /* The file was found, and can be read.  Allocate FILE_BUFFER and fill
00366      in the various members. */
00367   file_buffer = make_file_buffer ();
00368   file_buffer->filename = xstrdup (filename);
00369   file_buffer->fullpath = xstrdup (fullpath);
00370   file_buffer->finfo = finfo;
00371   file_buffer->filesize = filesize;
00372   file_buffer->contents = contents;
00373   if (compressed)
00374     file_buffer->flags |= N_IsCompressed;
00375 
00376   /* If requested, build the tags and nodes for this file buffer. */
00377   if (get_tags)
00378     build_tags_and_nodes (file_buffer);
00379 
00380   return file_buffer;
00381 }
00382 
00383 /* Grovel FILE_BUFFER->contents finding tags and nodes, and filling in the
00384    various slots.  This can also be used to rebuild a tag or node table. */
00385 void
00386 build_tags_and_nodes (FILE_BUFFER *file_buffer)
00387 {
00388   SEARCH_BINDING binding;
00389   long position;
00390 
00391   free_file_buffer_tags (file_buffer);
00392   file_buffer->flags &= ~N_HasTagsTable;
00393 
00394   /* See if there is a tags table in this info file. */
00395   binding.buffer = file_buffer->contents;
00396   binding.start = file_buffer->filesize;
00397   binding.end = binding.start - 1000;
00398   if (binding.end < 0)
00399     binding.end = 0;
00400   binding.flags = S_FoldCase;
00401 
00402   position = search_backward (TAGS_TABLE_END_LABEL, &binding);
00403 
00404   /* If there is a tag table, find the start of it, and grovel over it
00405      extracting tag information. */
00406   if (position != -1)
00407     while (1)
00408       {
00409         long tags_table_begin, tags_table_end;
00410 
00411         binding.end = position;
00412         binding.start = binding.end - 5 - strlen (TAGS_TABLE_END_LABEL);
00413         if (binding.start < 0)
00414           binding.start = 0;
00415 
00416         position = find_node_separator (&binding);
00417 
00418         /* For this test, (and all others here) failure indicates a bogus
00419            tags table.  Grovel the file. */
00420         if (position == -1)
00421           break;
00422 
00423         /* Remember the end of the tags table. */
00424         binding.start = position;
00425         tags_table_end = binding.start;
00426         binding.end = 0;
00427 
00428         /* Locate the start of the tags table. */
00429         position = search_backward (TAGS_TABLE_BEG_LABEL, &binding);
00430 
00431         if (position == -1)
00432           break;
00433 
00434         binding.end = position;
00435         binding.start = binding.end - 5 - strlen (TAGS_TABLE_BEG_LABEL);
00436         position = find_node_separator (&binding);
00437 
00438         if (position == -1)
00439           break;
00440 
00441         /* The file contains a valid tags table.  Fill the FILE_BUFFER's
00442            tags member. */
00443         file_buffer->flags |= N_HasTagsTable;
00444         tags_table_begin = position;
00445 
00446         /* If this isn't an indirect tags table, just remember the nodes
00447            described locally in this tags table.  Note that binding.end
00448            is pointing to just after the beginning label. */
00449         binding.start = binding.end;
00450         binding.end = file_buffer->filesize;
00451 
00452         if (!looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, &binding))
00453           {
00454             binding.start = tags_table_begin;
00455             binding.end = tags_table_end;
00456             get_nodes_of_tags_table (file_buffer, &binding);
00457             return;
00458           }
00459         else
00460           {
00461             /* This is an indirect tags table.  Build TAGS member. */
00462             SEARCH_BINDING indirect;
00463 
00464             indirect.start = tags_table_begin;
00465             indirect.end = 0;
00466             indirect.buffer = binding.buffer;
00467             indirect.flags = S_FoldCase;
00468 
00469             position = search_backward (INDIRECT_TAGS_TABLE_LABEL, &indirect);
00470 
00471             if (position == -1)
00472               {
00473                 /* This file is malformed.  Give up. */
00474                 return;
00475               }
00476 
00477             indirect.start = position;
00478             indirect.end = tags_table_begin;
00479             binding.start = tags_table_begin;
00480             binding.end = tags_table_end;
00481             get_tags_of_indirect_tags_table (file_buffer, &indirect, &binding);
00482             return;
00483           }
00484       }
00485 
00486   /* This file doesn't contain any kind of tags table.  Grovel the
00487      file and build node entries for it. */
00488   get_nodes_of_info_file (file_buffer);
00489 }
00490 
00491 /* Search through FILE_BUFFER->contents building an array of TAG *,
00492    one entry per each node present in the file.  Store the tags in
00493    FILE_BUFFER->tags, and the number of allocated slots in
00494    FILE_BUFFER->tags_slots. */
00495 static void
00496 get_nodes_of_info_file (FILE_BUFFER *file_buffer)
00497 {
00498   long nodestart;
00499   int tags_index = 0;
00500   SEARCH_BINDING binding;
00501 
00502   binding.buffer = file_buffer->contents;
00503   binding.start = 0;
00504   binding.end = file_buffer->filesize;
00505   binding.flags = S_FoldCase;
00506 
00507   while ((nodestart = find_node_separator (&binding)) != -1)
00508     {
00509       int start, end;
00510       char *nodeline;
00511       TAG *entry;
00512       int anchor = 0;
00513 
00514       /* Skip past the characters just found. */
00515       binding.start = nodestart;
00516       binding.start += skip_node_separator (binding.buffer + binding.start);
00517 
00518       /* Move to the start of the line defining the node. */
00519       nodeline = binding.buffer + binding.start;
00520 
00521       /* Find "Node:" */
00522       start = string_in_line (INFO_NODE_LABEL, nodeline);
00523       /* No Node:.  Maybe it's a Ref:.  */
00524       if (start == -1)
00525         {
00526           start = string_in_line (INFO_REF_LABEL, nodeline);
00527           if (start != -1)
00528             anchor = 1;
00529         }
00530 
00531       /* If not there, this is not the start of a node. */
00532       if (start == -1)
00533         continue;
00534 
00535       /* Find the start of the nodename. */
00536       start += skip_whitespace (nodeline + start);
00537 
00538       /* Find the end of the nodename. */
00539       end = start +
00540         skip_node_characters (nodeline + start, DONT_SKIP_NEWLINES);
00541 
00542       /* Okay, we have isolated the node name, and we know where the
00543          node starts.  Remember this information. */
00544       entry = xmalloc (sizeof (TAG));
00545       entry->nodename = xmalloc (1 + (end - start));
00546       strncpy (entry->nodename, nodeline + start, end - start);
00547       entry->nodename[end - start] = 0;
00548       entry->nodestart = nodestart;
00549       if (anchor)
00550         entry->nodelen = 0;
00551       else
00552         {
00553           SEARCH_BINDING node_body;
00554 
00555           node_body.buffer = binding.buffer + binding.start;
00556           node_body.start = 0;
00557           node_body.end = binding.end - binding.start;
00558           node_body.flags = S_FoldCase;
00559           entry->nodelen = get_node_length (&node_body);
00560         }
00561 
00562       entry->filename = file_buffer->fullpath;
00563 
00564       /* Add this tag to the array of tag structures in this FILE_BUFFER. */
00565       add_pointer_to_array (entry, tags_index, file_buffer->tags,
00566                             file_buffer->tags_slots, 100, TAG *);
00567     }
00568 }
00569 
00570 /* Return the length of the node which starts at BINDING. */
00571 static long
00572 get_node_length (SEARCH_BINDING *binding)
00573 {
00574   int i;
00575   char *body;
00576 
00577   /* [A node] ends with either a ^_, a ^L, or end of file.  */
00578   for (i = binding->start, body = binding->buffer; i < binding->end; i++)
00579     {
00580       if (body[i] == INFO_FF || body[i] == INFO_COOKIE)
00581         break;
00582     }
00583   return i - binding->start;
00584 }
00585 
00586 /* Build and save the array of nodes in FILE_BUFFER by searching through the
00587    contents of BUFFER_BINDING for a tags table, and groveling the contents. */
00588 static void
00589 get_nodes_of_tags_table (FILE_BUFFER *file_buffer,
00590     SEARCH_BINDING *buffer_binding)
00591 {
00592   int name_offset;
00593   SEARCH_BINDING *tmp_search;
00594   long position;
00595   int tags_index = 0;
00596 
00597   tmp_search = copy_binding (buffer_binding);
00598 
00599   /* Find the start of the tags table. */
00600   position = find_tags_table (tmp_search);
00601 
00602   /* If none, we're all done. */
00603   if (position == -1)
00604     return;
00605 
00606   /* Move to one character before the start of the actual table. */
00607   tmp_search->start = position;
00608   tmp_search->start += skip_node_separator
00609     (tmp_search->buffer + tmp_search->start);
00610   tmp_search->start += strlen (TAGS_TABLE_BEG_LABEL);
00611   tmp_search->start--;
00612 
00613   /* The tag table consists of lines containing node names and positions.
00614      Do each line until we find one that doesn't contain a node name. */
00615   while ((position = search_forward ("\n", tmp_search)) != -1)
00616     {
00617       TAG *entry;
00618       char *nodedef;
00619       unsigned p;
00620       int anchor = 0;
00621 
00622       /* Prepare to skip this line. */
00623       tmp_search->start = position;
00624       tmp_search->start++;
00625 
00626       /* Skip past informative "(Indirect)" tags table line. */
00627       if (!tags_index && looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, tmp_search))
00628         continue;
00629 
00630       /* Find the label preceding the node name. */
00631       name_offset =
00632         string_in_line (INFO_NODE_LABEL, tmp_search->buffer + tmp_search->start);
00633 
00634       /* If no node label, maybe it's an anchor.  */
00635       if (name_offset == -1)
00636         {
00637           name_offset = string_in_line (INFO_REF_LABEL,
00638               tmp_search->buffer + tmp_search->start);
00639           if (name_offset != -1)
00640             anchor = 1;
00641         }
00642 
00643       /* If not there, not a defining line, so we must be out of the
00644          tags table.  */
00645       if (name_offset == -1)
00646         break;
00647 
00648       entry = xmalloc (sizeof (TAG));
00649 
00650       /* Find the beginning of the node definition. */
00651       tmp_search->start += name_offset;
00652       nodedef = tmp_search->buffer + tmp_search->start;
00653       nodedef += skip_whitespace (nodedef);
00654 
00655       /* Move past the node's name in this tag to the TAGSEP character. */
00656       for (p = 0; nodedef[p] && nodedef[p] != INFO_TAGSEP; p++)
00657         ;
00658       if (nodedef[p] != INFO_TAGSEP)
00659         continue;
00660 
00661       entry->nodename = xmalloc (p + 1);
00662       strncpy (entry->nodename, nodedef, p);
00663       entry->nodename[p] = 0;
00664       p++;
00665       entry->nodestart = atol (nodedef + p);
00666 
00667       /* If a node, we don't know the length yet, but if it's an
00668          anchor, the length is 0. */
00669       entry->nodelen = anchor ? 0 : -1;
00670 
00671       /* The filename of this node is currently known as the same as the
00672          name of this file. */
00673       entry->filename = file_buffer->fullpath;
00674 
00675       /* Add this node structure to the array of node structures in this
00676          FILE_BUFFER. */
00677       add_pointer_to_array (entry, tags_index, file_buffer->tags,
00678                             file_buffer->tags_slots, 100, TAG *);
00679     }
00680   free (tmp_search);
00681 }
00682 
00683 /* A structure used only in `get_tags_of_indirect_tags_table' to hold onto
00684    an intermediate value. */
00685 typedef struct {
00686   char *filename;
00687   long first_byte;
00688 } SUBFILE;
00689 
00690 /* Remember in FILE_BUFFER the nodenames, subfilenames, and offsets within the
00691    subfiles of every node which appears in TAGS_BINDING.  The 2nd argument is
00692    a binding surrounding the indirect files list. */
00693 static void
00694 get_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer,
00695     SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding)
00696 {
00697   int i;
00698   SUBFILE **subfiles = NULL;
00699   int subfiles_index = 0, subfiles_slots = 0;
00700   TAG *entry;
00701 
00702   /* First get the list of tags from the tags table.  Then lookup the
00703      associated file in the indirect list for each tag, and update it. */
00704   get_nodes_of_tags_table (file_buffer, tags_binding);
00705 
00706   /* We have the list of tags in file_buffer->tags.  Get the list of
00707      subfiles from the indirect table. */
00708   {
00709     char *start, *end, *line;
00710     SUBFILE *subfile;
00711 
00712     start = indirect_binding->buffer + indirect_binding->start;
00713     end = indirect_binding->buffer + indirect_binding->end;
00714     line = start;
00715 
00716     while (line < end)
00717       {
00718         int colon;
00719 
00720         colon = string_in_line (":", line);
00721 
00722         if (colon == -1)
00723           break;
00724 
00725         subfile = (SUBFILE *)xmalloc (sizeof (SUBFILE));
00726         subfile->filename = (char *)xmalloc (colon);
00727         strncpy (subfile->filename, line, colon - 1);
00728         subfile->filename[colon - 1] = 0;
00729         subfile->first_byte = (long) atol (line + colon);
00730 
00731         add_pointer_to_array
00732           (subfile, subfiles_index, subfiles, subfiles_slots, 10, SUBFILE *);
00733 
00734         while (*line++ != '\n');
00735       }
00736   }
00737 
00738   /* If we have successfully built the indirect files table, then
00739      merge the information in the two tables. */
00740   if (!subfiles)
00741     {
00742       free_file_buffer_tags (file_buffer);
00743       return;
00744     }
00745   else
00746     {
00747       int tags_index;
00748       long header_length;
00749       SEARCH_BINDING binding;
00750 
00751       /* Find the length of the header of the file containing the indirect
00752          tags table.  This header appears at the start of every file.  We
00753          want the absolute position of each node within each subfile, so
00754          we subtract the start of the containing subfile from the logical
00755          position of the node, and then add the length of the header in. */
00756       binding.buffer = file_buffer->contents;
00757       binding.start = 0;
00758       binding.end = file_buffer->filesize;
00759       binding.flags = S_FoldCase;
00760 
00761       header_length = find_node_separator (&binding);
00762       if (header_length == -1)
00763         header_length = 0;
00764 
00765       /* Build the file buffer's list of subfiles. */
00766       {
00767         char *containing_dir = xstrdup (file_buffer->fullpath);
00768         char *temp = filename_non_directory (containing_dir);
00769         int len_containing_dir;
00770 
00771         if (temp > containing_dir)
00772           {
00773             if (HAVE_DRIVE (file_buffer->fullpath) &&
00774                 temp == containing_dir + 2)
00775               {
00776                 /* Avoid converting "d:foo" into "d:/foo" below.  */
00777                 *temp = '.';
00778                 temp += 2;
00779               }
00780             temp[-1] = 0;
00781           }
00782 
00783         len_containing_dir = strlen (containing_dir);
00784 
00785         for (i = 0; subfiles[i]; i++);
00786 
00787         file_buffer->subfiles = (char **) xmalloc ((1 + i) * sizeof (char *));
00788 
00789         for (i = 0; subfiles[i]; i++)
00790           {
00791             char *fullpath;
00792 
00793             fullpath = (char *) xmalloc
00794               (2 + strlen (subfiles[i]->filename) + len_containing_dir);
00795 
00796             sprintf (fullpath, "%s/%s",
00797                      containing_dir, subfiles[i]->filename);
00798 
00799             file_buffer->subfiles[i] = fullpath;
00800           }
00801         file_buffer->subfiles[i] = NULL;
00802         free (containing_dir);
00803       }
00804 
00805       /* For each node in the file's tags table, remember the starting
00806          position. */
00807       for (tags_index = 0; (entry = file_buffer->tags[tags_index]);
00808            tags_index++)
00809         {
00810           for (i = 0;
00811                subfiles[i] && entry->nodestart >= subfiles[i]->first_byte;
00812                i++);
00813 
00814           /* If the Info file containing the indirect tags table is
00815              malformed, then give up. */
00816           if (!i)
00817             {
00818               /* The Info file containing the indirect tags table is
00819                  malformed.  Give up. */
00820               for (i = 0; subfiles[i]; i++)
00821                 {
00822                   free (subfiles[i]->filename);
00823                   free (subfiles[i]);
00824                   free (file_buffer->subfiles[i]);
00825                 }
00826               file_buffer->subfiles = NULL;
00827               free_file_buffer_tags (file_buffer);
00828               return;
00829             }
00830 
00831           /* SUBFILES[i] is the index of the first subfile whose logical
00832              first byte is greater than the logical offset of this node's
00833              starting position.  This means that the subfile directly
00834              preceding this one is the one containing the node. */
00835 
00836           entry->filename = file_buffer->subfiles[i - 1];
00837           entry->nodestart -= subfiles[i - 1]->first_byte;
00838           entry->nodestart += header_length;
00839         }
00840 
00841       /* We have successfully built the tags table.  Remember that it
00842          was indirect. */
00843       file_buffer->flags |= N_TagsIndirect;
00844     }
00845 
00846   /* Free the structures assigned to SUBFILES.  Free the names as well
00847      as the structures themselves, then finally, the array. */
00848   for (i = 0; subfiles[i]; i++)
00849     {
00850       free (subfiles[i]->filename);
00851       free (subfiles[i]);
00852     }
00853   free (subfiles);
00854 }
00855 
00856 
00857 /* Return the node that contains TAG in FILE_BUFFER, else
00858    (pathologically) NULL.  Called from info_node_of_file_buffer_tags.  */
00859 static NODE *
00860 find_node_of_anchor (FILE_BUFFER *file_buffer, TAG *tag)
00861 {
00862   int anchor_pos, node_pos;
00863   TAG *node_tag;
00864   NODE *node;
00865 
00866   /* Look through the tag list for the anchor.  */
00867   for (anchor_pos = 0; file_buffer->tags[anchor_pos]; anchor_pos++)
00868     {
00869       TAG *t = file_buffer->tags[anchor_pos];
00870       if (t->nodestart == tag->nodestart)
00871         break;
00872     }
00873 
00874   /* Should not happen, because we should always find the anchor.  */
00875   if (!file_buffer->tags[anchor_pos])
00876     return NULL;
00877 
00878   /* We've found the anchor.  Look backwards in the tag table for the
00879      preceding node (we're assuming the tags are given in order),
00880      skipping over any preceding anchors.  */
00881   for (node_pos = anchor_pos - 1;
00882        node_pos >= 0 && file_buffer->tags[node_pos]->nodelen == 0;
00883        node_pos--)
00884     ;
00885 
00886   /* An info file with an anchor before any nodes is pathological, but
00887      it's possible, so don't crash.  */
00888   if (node_pos < 0)
00889     return NULL;
00890 
00891   /* We have the tag for the node that contained the anchor tag.  */
00892   node_tag = file_buffer->tags[node_pos];
00893 
00894   /* Look up the node name in the tag table to get the actual node.
00895      This is a recursive call, but it can't recurse again, because we
00896      call it with a real node.  */
00897   node = info_node_of_file_buffer_tags (file_buffer, node_tag->nodename);
00898 
00899   /* Start displaying the node at the anchor position.  */
00900   if (node)
00901     { /* The nodestart for real nodes is three characters before the `F'
00902          in the `File:' line (a newline, the CTRL-_, and another
00903          newline).  The nodestart for anchors is the actual position.
00904          But we offset by only 2, rather than 3, because if an anchor is
00905          at the beginning of a paragraph, it's nicer for it to end up on
00906          the beginning of the first line of the paragraph rather than
00907          the blank line before it.  (makeinfo has no way of knowing that
00908          a paragraph is going to start, so we can't fix it there.)  */
00909       node->display_pos = file_buffer->tags[anchor_pos]->nodestart
00910                           - (node_tag->nodestart + 2);
00911 
00912       /* Otherwise an anchor at the end of a node ends up displaying at
00913          the end of the last line of the node (way over on the right of
00914          the screen), which looks wrong.  */
00915       if (node->display_pos >= (unsigned long) node->nodelen)
00916         node->display_pos = node->nodelen - 1;
00917 
00918       /* Don't search in the node for the xref text, it's not there.  */
00919       node->flags |= N_FromAnchor;
00920     }
00921 
00922   return node;
00923 }
00924 
00925 
00926 /* Return the node from FILE_BUFFER which matches NODENAME by searching
00927    the tags table in FILE_BUFFER, or NULL.  */
00928 static NODE *
00929 info_node_of_file_buffer_tags (FILE_BUFFER *file_buffer, char *nodename)
00930 {
00931   TAG *tag;
00932   int i;
00933 
00934   /* If no tags at all (possibly a misformatted info file), quit.  */
00935   if (!file_buffer->tags) {
00936     return NULL;
00937   }
00938 
00939   for (i = 0; (tag = file_buffer->tags[i]); i++)
00940     if (strcmp (nodename, tag->nodename) == 0)
00941       {
00942         FILE_BUFFER *subfile = info_find_file_internal (tag->filename,
00943                                                         INFO_NO_TAGS);
00944         if (!subfile)
00945           return NULL;
00946 
00947         if (!subfile->contents)
00948           {
00949             info_reload_file_buffer_contents (subfile);
00950             if (!subfile->contents)
00951               return NULL;
00952           }
00953 
00954         /* If we were able to find this file and load it, then return
00955            the node within it. */
00956         {
00957           NODE *node = xmalloc (sizeof (NODE));
00958           node->filename    = subfile->fullpath;
00959           node->parent      = NULL;
00960           node->nodename    = tag->nodename;
00961           node->contents    = subfile->contents + tag->nodestart;
00962           node->display_pos = 0;
00963           node->flags       = 0;
00964 
00965           if (file_buffer->flags & N_HasTagsTable)
00966             {
00967               node->flags |= N_HasTagsTable;
00968 
00969               if (file_buffer->flags & N_TagsIndirect)
00970                 {
00971                   node->flags |= N_TagsIndirect;
00972                   node->parent = file_buffer->fullpath;
00973                 }
00974             }
00975 
00976           if (subfile->flags & N_IsCompressed)
00977             node->flags |= N_IsCompressed;
00978 
00979           /* If TAG->nodelen hasn't been calculated yet, then we aren't
00980              in a position to trust the entry pointer.  Adjust things so
00981              that ENTRY->nodestart gets the exact address of the start of
00982              the node separator which starts this node, and NODE->contents
00983              gets the address of the line defining this node.  If we cannot
00984              do that, the node isn't really here. */
00985           if (tag->nodelen == -1)
00986             {
00987               int min, max;
00988               char *node_sep;
00989               SEARCH_BINDING node_body;
00990               char *buff_end;
00991 
00992               min = max = DEFAULT_INFO_FUDGE;
00993 
00994               if (tag->nodestart < DEFAULT_INFO_FUDGE)
00995                 min = tag->nodestart;
00996 
00997               if (DEFAULT_INFO_FUDGE >
00998                   (subfile->filesize - tag->nodestart))
00999                 max = subfile->filesize - tag->nodestart;
01000 
01001               /* NODE_SEP gets the address of the separator which defines
01002                  this node, or NULL if the node wasn't found.
01003                  NODE->contents is side-effected to point to right after
01004                  the separator. */
01005               node_sep = adjust_nodestart (node, min, max);
01006               if (node_sep == NULL)
01007                 {
01008                   free (node);
01009                   return NULL;
01010                 }
01011               /* Readjust tag->nodestart. */
01012               tag->nodestart = node_sep - subfile->contents;
01013 
01014               /* Calculate the length of the current node. */
01015               buff_end = subfile->contents + subfile->filesize;
01016 
01017               node_body.buffer = node->contents;
01018               node_body.start = 0;
01019               node_body.end = buff_end - node_body.buffer;
01020               node_body.flags = 0;
01021               tag->nodelen = get_node_length (&node_body);
01022               node->nodelen = tag->nodelen;
01023             }
01024 
01025           else if (tag->nodelen == 0) /* anchor, return containing node */
01026             {
01027               free (node);
01028               node = find_node_of_anchor (file_buffer, tag);
01029             }
01030 
01031           else
01032             {
01033               /* Since we know the length of this node, we have already
01034                  adjusted tag->nodestart to point to the exact start of
01035                  it.  Simply skip the node separator. */
01036               node->contents += skip_node_separator (node->contents);
01037               node->nodelen = tag->nodelen;
01038             }
01039 
01040           return node;
01041         }
01042       }
01043 
01044   /* There was a tag table for this file, and the node wasn't found.
01045      Return NULL, since this file doesn't contain the desired node. */
01046   return NULL;
01047 }
01048 
01049 /* Managing file_buffers, nodes, and tags.  */
01050 
01051 /* Create a new, empty file buffer. */
01052 FILE_BUFFER *
01053 make_file_buffer (void)
01054 {
01055   FILE_BUFFER *file_buffer = xmalloc (sizeof (FILE_BUFFER));
01056 
01057   file_buffer->filename = file_buffer->fullpath = NULL;
01058   file_buffer->contents = NULL;
01059   file_buffer->tags = NULL;
01060   file_buffer->subfiles = NULL;
01061   file_buffer->tags_slots = 0;
01062   file_buffer->flags = 0;
01063 
01064   return file_buffer;
01065 }
01066 
01067 /* Add FILE_BUFFER to our list of already loaded info files. */
01068 static void
01069 remember_info_file (FILE_BUFFER *file_buffer)
01070 {
01071   int i;
01072 
01073   for (i = 0; info_loaded_files && info_loaded_files[i]; i++)
01074     ;
01075 
01076   add_pointer_to_array (file_buffer, i, info_loaded_files,
01077                         info_loaded_files_slots, 10, FILE_BUFFER *);
01078 }
01079 
01080 /* Forget the contents, tags table, nodes list, and names of FILENAME. */
01081 static void
01082 forget_info_file (char *filename)
01083 {
01084   int i;
01085   FILE_BUFFER *file_buffer;
01086 
01087   if (!info_loaded_files)
01088     return;
01089 
01090   for (i = 0; (file_buffer = info_loaded_files[i]); i++)
01091     if (FILENAME_CMP (filename, file_buffer->filename) == 0
01092         || FILENAME_CMP (filename, file_buffer->fullpath) == 0)
01093       {
01094         free (file_buffer->filename);
01095         free (file_buffer->fullpath);
01096 
01097         if (file_buffer->contents)
01098           free (file_buffer->contents);
01099 
01100         /* free_file_buffer_tags () also kills the subfiles list, since
01101            the subfiles list is only of use in conjunction with tags. */
01102         free_file_buffer_tags (file_buffer);
01103 
01104         /* Move rest of list down.  */
01105         while (info_loaded_files[i + 1])
01106           {
01107             info_loaded_files[i] = info_loaded_files[i + 1];
01108             i++;
01109           }
01110         info_loaded_files[i] = 0;
01111 
01112         break;
01113       }
01114 }
01115 
01116 /* Free the tags (if any) associated with FILE_BUFFER. */
01117 static void
01118 free_file_buffer_tags (FILE_BUFFER *file_buffer)
01119 {
01120   int i;
01121 
01122   if (file_buffer->tags)
01123     {
01124       TAG *tag;
01125 
01126       for (i = 0; (tag = file_buffer->tags[i]); i++)
01127         free_info_tag (tag);
01128 
01129       free (file_buffer->tags);
01130       file_buffer->tags = NULL;
01131       file_buffer->tags_slots = 0;
01132     }
01133 
01134   if (file_buffer->subfiles)
01135     {
01136       for (i = 0; file_buffer->subfiles[i]; i++)
01137         free (file_buffer->subfiles[i]);
01138 
01139       free (file_buffer->subfiles);
01140       file_buffer->subfiles = NULL;
01141     }
01142 }
01143 
01144 /* Free the data associated with TAG, as well as TAG itself. */
01145 static void
01146 free_info_tag (TAG *tag)
01147 {
01148   free (tag->nodename);
01149 
01150   /* We don't free tag->filename, because that filename is part of the
01151      subfiles list for the containing FILE_BUFFER.  free_info_tags ()
01152      will free the subfiles when it is appropriate. */
01153 
01154   free (tag);
01155 }
01156 
01157 /* Load the contents of FILE_BUFFER->contents.  This function is called
01158    when a file buffer was loaded, and then in order to conserve memory, the
01159    file buffer's contents were freed and the pointer was zero'ed.  Note that
01160    the file was already loaded at least once successfully, so the tags and/or
01161    nodes members are still correctly filled. */
01162 static void
01163 info_reload_file_buffer_contents (FILE_BUFFER *fb)
01164 {
01165   int is_compressed;
01166 
01167 #if defined (HANDLE_MAN_PAGES)
01168   /* If this is the magic manpage node, don't try to reload, just give up. */
01169   if (fb->flags & N_IsManPage)
01170     return;
01171 #endif
01172 
01173   fb->flags &= ~N_IsCompressed;
01174 
01175   /* Let the filesystem do all the work for us. */
01176   fb->contents =
01177     filesys_read_info_file (fb->fullpath, &(fb->filesize), &(fb->finfo),
01178                             &is_compressed);
01179   if (is_compressed)
01180     fb->flags |= N_IsCompressed;
01181 }
01182 
01183 /* Return the actual starting memory location of NODE, side-effecting
01184    NODE->contents.  MIN and MAX are bounds for a search if one is necessary.
01185    Because of the way that tags are implemented, the physical nodestart may
01186    not actually be where the tag says it is.  If that is the case, but the
01187    node was found anyway, set N_UpdateTags in NODE->flags.  If the node is
01188    found, return non-zero.  NODE->contents is returned positioned right after
01189    the node separator that precedes this node, while the return value is
01190    position directly on the separator that precedes this node.  If the node
01191    could not be found, return a NULL pointer. */
01192 static char *
01193 adjust_nodestart (NODE *node, int min, int max)
01194 {
01195   long position;
01196   SEARCH_BINDING node_body;
01197 
01198   /* Define the node body. */
01199   node_body.buffer = node->contents;
01200   node_body.start = 0;
01201   node_body.end = max;
01202   node_body.flags = 0;
01203 
01204   /* Try the optimal case first.  Who knows?  This file may actually be
01205      formatted (mostly) correctly. */
01206   if (node_body.buffer[0] != INFO_COOKIE && min > 2)
01207     node_body.buffer -= 3;
01208 
01209   position = find_node_separator (&node_body);
01210 
01211   /* If we found a node start, then check it out. */
01212   if (position != -1)
01213     {
01214       int sep_len;
01215 
01216       sep_len = skip_node_separator (node->contents);
01217 
01218       /* If we managed to skip a node separator, then check for this node
01219          being the right one. */
01220       if (sep_len != 0)
01221         {
01222           char *nodedef, *nodestart;
01223           int offset;
01224 
01225           nodestart = node_body.buffer + position + sep_len;
01226           nodedef = nodestart;
01227           offset = string_in_line (INFO_NODE_LABEL, nodedef);
01228 
01229           if (offset != -1)
01230             {
01231               nodedef += offset;
01232               nodedef += skip_whitespace (nodedef);
01233               offset = skip_node_characters (nodedef, DONT_SKIP_NEWLINES);
01234               if (((unsigned int) offset == strlen (node->nodename)) &&
01235                   (strncmp (node->nodename, nodedef, offset) == 0))
01236                 {
01237                   node->contents = nodestart;
01238                   return node_body.buffer + position;
01239                 }
01240             }
01241         }
01242     }
01243 
01244   /* Oh well, I guess we have to try to find it in a larger area. */
01245   node_body.buffer = node->contents - min;
01246   node_body.start = 0;
01247   node_body.end = min + max;
01248   node_body.flags = 0;
01249 
01250   position = find_node_in_binding (node->nodename, &node_body);
01251 
01252   /* If the node couldn't be found, we lose big. */
01253   if (position == -1)
01254     return NULL;
01255 
01256   /* Otherwise, the node was found, but the tags table could need updating
01257      (if we used a tag to get here, that is).  Set the flag in NODE->flags. */
01258   node->contents = node_body.buffer + position;
01259   node->contents += skip_node_separator (node->contents);
01260   if (node->flags & N_HasTagsTable)
01261     node->flags |= N_UpdateTags;
01262   return node_body.buffer + position;
01263 }