xref: /netbsd-src/external/gpl2/texinfo/dist/info/nodes.c (revision d3737e9cfd8cdb680cae0994d1d5f26b365d6d47)
1*d3737e9cSchristos /*	$NetBSD: nodes.c,v 1.2 2016/01/14 00:34:52 christos Exp $	*/
229619d2aSchristos 
329619d2aSchristos /* nodes.c -- how to get an Info file and node.
429619d2aSchristos    Id: nodes.c,v 1.4 2004/04/11 17:56:46 karl Exp
529619d2aSchristos 
629619d2aSchristos    Copyright (C) 1993, 1998, 1999, 2000, 2002, 2003, 2004 Free Software
729619d2aSchristos    Foundation, Inc.
829619d2aSchristos 
929619d2aSchristos    This program is free software; you can redistribute it and/or modify
1029619d2aSchristos    it under the terms of the GNU General Public License as published by
1129619d2aSchristos    the Free Software Foundation; either version 2, or (at your option)
1229619d2aSchristos    any later version.
1329619d2aSchristos 
1429619d2aSchristos    This program is distributed in the hope that it will be useful,
1529619d2aSchristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
1629619d2aSchristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1729619d2aSchristos    GNU General Public License for more details.
1829619d2aSchristos 
1929619d2aSchristos    You should have received a copy of the GNU General Public License
2029619d2aSchristos    along with this program; if not, write to the Free Software
2129619d2aSchristos    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
2229619d2aSchristos 
2329619d2aSchristos    Originally written by Brian Fox (bfox@ai.mit.edu). */
2429619d2aSchristos 
2529619d2aSchristos #include "info.h"
2629619d2aSchristos 
2729619d2aSchristos #include "nodes.h"
2829619d2aSchristos #include "search.h"
2929619d2aSchristos #include "filesys.h"
3029619d2aSchristos #include "info-utils.h"
3129619d2aSchristos 
3229619d2aSchristos #if defined (HANDLE_MAN_PAGES)
3329619d2aSchristos #  include "man.h"
3429619d2aSchristos #endif /* HANDLE_MAN_PAGES */
3529619d2aSchristos 
3629619d2aSchristos static void forget_info_file (char *filename);
3729619d2aSchristos static void remember_info_file (FILE_BUFFER *file_buffer);
3829619d2aSchristos static void free_file_buffer_tags (FILE_BUFFER *file_buffer);
3929619d2aSchristos static void free_info_tag (TAG *tag);
4029619d2aSchristos static void get_nodes_of_tags_table (FILE_BUFFER *file_buffer,
4129619d2aSchristos     SEARCH_BINDING *buffer_binding);
4229619d2aSchristos static void get_nodes_of_info_file (FILE_BUFFER *file_buffer);
4329619d2aSchristos static void get_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer,
4429619d2aSchristos     SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding);
4529619d2aSchristos static void info_reload_file_buffer_contents (FILE_BUFFER *fb);
4629619d2aSchristos static char *adjust_nodestart (NODE *node, int min, int max);
4729619d2aSchristos static FILE_BUFFER *info_load_file_internal (char *filename, int get_tags);
4829619d2aSchristos static FILE_BUFFER *info_find_file_internal (char *filename, int get_tags);
4929619d2aSchristos static NODE *info_node_of_file_buffer_tags (FILE_BUFFER *file_buffer,
5029619d2aSchristos     char *nodename);
5129619d2aSchristos 
5229619d2aSchristos static long get_node_length (SEARCH_BINDING *binding);
5329619d2aSchristos 
5429619d2aSchristos /* Magic number that RMS used to decide how much a tags table pointer could
5529619d2aSchristos    be off by.  I feel that it should be much smaller, like 4.  */
5629619d2aSchristos #define DEFAULT_INFO_FUDGE 1000
5729619d2aSchristos 
5829619d2aSchristos /* Passed to *_internal functions.  INFO_GET_TAGS says to do what is
5929619d2aSchristos    neccessary to fill in the nodes or tags arrays in FILE_BUFFER. */
6029619d2aSchristos #define INFO_NO_TAGS  0
6129619d2aSchristos #define INFO_GET_TAGS 1
6229619d2aSchristos 
6329619d2aSchristos /* Global variables.  */
6429619d2aSchristos 
6529619d2aSchristos /* When non-zero, this is a string describing the recent file error. */
6629619d2aSchristos char *info_recent_file_error = NULL;
6729619d2aSchristos 
6829619d2aSchristos /* The list of already loaded nodes. */
6929619d2aSchristos FILE_BUFFER **info_loaded_files = NULL;
7029619d2aSchristos 
7129619d2aSchristos /* The number of slots currently allocated to LOADED_FILES. */
7229619d2aSchristos int info_loaded_files_slots = 0;
7329619d2aSchristos 
7429619d2aSchristos /* Public functions for node manipulation.  */
7529619d2aSchristos 
7629619d2aSchristos /* Used to build `dir' menu from `localdir' files found in INFOPATH. */
7729619d2aSchristos extern void maybe_build_dir_node (char *dirname);
7829619d2aSchristos 
7929619d2aSchristos /* Return a pointer to a NODE structure for the Info node (FILENAME)NODENAME.
8029619d2aSchristos    If FILENAME is NULL, `dir' is used.
8129619d2aSchristos    IF NODENAME is NULL, `Top' is used.
8229619d2aSchristos    If the node cannot be found, return NULL. */
8329619d2aSchristos NODE *
info_get_node(char * filename,char * nodename)8429619d2aSchristos info_get_node (char *filename, char *nodename)
8529619d2aSchristos {
8629619d2aSchristos   NODE *node;
8729619d2aSchristos   FILE_BUFFER *file_buffer = NULL;
8829619d2aSchristos 
8929619d2aSchristos   info_recent_file_error = NULL;
9029619d2aSchristos   info_parse_node (nodename, DONT_SKIP_NEWLINES);
9129619d2aSchristos   nodename = NULL;
9229619d2aSchristos 
9329619d2aSchristos   if (info_parsed_filename)
9429619d2aSchristos     filename = info_parsed_filename;
9529619d2aSchristos 
9629619d2aSchristos   if (info_parsed_nodename)
9729619d2aSchristos     nodename = info_parsed_nodename;
9829619d2aSchristos 
9929619d2aSchristos   /* If FILENAME is not specified, it defaults to "dir". */
10029619d2aSchristos   if (!filename)
10129619d2aSchristos     filename = "dir";
10229619d2aSchristos 
10329619d2aSchristos   /* If the file to be looked up is "dir", build the contents from all of
10429619d2aSchristos      the "dir"s and "localdir"s found in INFOPATH. */
10529619d2aSchristos   if (is_dir_name (filename))
10629619d2aSchristos     maybe_build_dir_node (filename);
10729619d2aSchristos 
10829619d2aSchristos   /* Find the correct info file, or give up.  */
10929619d2aSchristos   file_buffer = info_find_file (filename);
11029619d2aSchristos   if (!file_buffer)
11129619d2aSchristos     {
11229619d2aSchristos       if (filesys_error_number)
11329619d2aSchristos         info_recent_file_error =
11429619d2aSchristos           filesys_error_string (filename, filesys_error_number);
11529619d2aSchristos       return NULL;
11629619d2aSchristos     }
11729619d2aSchristos 
11829619d2aSchristos   /* Look for the node.  */
11929619d2aSchristos   node = info_get_node_of_file_buffer (nodename, file_buffer);
12029619d2aSchristos 
12129619d2aSchristos   /* If the node not found was "Top", try again with different case.  */
12229619d2aSchristos   if (!node && (nodename == NULL || strcasecmp (nodename, "Top") == 0))
12329619d2aSchristos     {
12429619d2aSchristos       node = info_get_node_of_file_buffer ("Top", file_buffer);
12529619d2aSchristos       if (!node)
12629619d2aSchristos         node = info_get_node_of_file_buffer ("top", file_buffer);
12729619d2aSchristos       if (!node)
12829619d2aSchristos         node = info_get_node_of_file_buffer ("TOP", file_buffer);
12929619d2aSchristos     }
13029619d2aSchristos 
13129619d2aSchristos   return node;
13229619d2aSchristos }
13329619d2aSchristos 
13429619d2aSchristos /* Return a pointer to a NODE structure for the Info node NODENAME in
13529619d2aSchristos    FILE_BUFFER.  NODENAME can be passed as NULL, in which case the
13629619d2aSchristos    nodename of "Top" is used.  If the node cannot be found, return a
13729619d2aSchristos    NULL pointer. */
13829619d2aSchristos NODE *
info_get_node_of_file_buffer(char * nodename,FILE_BUFFER * file_buffer)13929619d2aSchristos info_get_node_of_file_buffer (char *nodename, FILE_BUFFER *file_buffer)
14029619d2aSchristos {
14129619d2aSchristos   NODE *node = NULL;
14229619d2aSchristos 
14329619d2aSchristos   /* If we are unable to find the file, we have to give up.  There isn't
14429619d2aSchristos      anything else we can do. */
14529619d2aSchristos   if (!file_buffer)
14629619d2aSchristos     return NULL;
14729619d2aSchristos 
14829619d2aSchristos   /* If the file buffer was gc'ed, reload the contents now. */
14929619d2aSchristos   if (!file_buffer->contents)
15029619d2aSchristos     info_reload_file_buffer_contents (file_buffer);
15129619d2aSchristos 
15229619d2aSchristos   /* If NODENAME is not specified, it defaults to "Top". */
15329619d2aSchristos   if (!nodename)
15429619d2aSchristos     nodename = "Top";
15529619d2aSchristos 
15629619d2aSchristos   /* If the name of the node that we wish to find is exactly "*", then the
15729619d2aSchristos      node body is the contents of the entire file.  Create and return such
15829619d2aSchristos      a node. */
15929619d2aSchristos   if (strcmp (nodename, "*") == 0)
16029619d2aSchristos     {
16129619d2aSchristos       node = (NODE *)xmalloc (sizeof (NODE));
16229619d2aSchristos       node->filename = file_buffer->fullpath;
16329619d2aSchristos       node->parent   = NULL;
16429619d2aSchristos       node->nodename = xstrdup ("*");
16529619d2aSchristos       node->contents = file_buffer->contents;
16629619d2aSchristos       node->nodelen = file_buffer->filesize;
16729619d2aSchristos       node->flags = 0;
16829619d2aSchristos       node->display_pos = 0;
16929619d2aSchristos     }
17029619d2aSchristos #if defined (HANDLE_MAN_PAGES)
17129619d2aSchristos   /* If the file buffer is the magic one associated with manpages, call
17229619d2aSchristos      the manpage node finding function instead. */
17329619d2aSchristos   else if (file_buffer->flags & N_IsManPage)
17429619d2aSchristos     {
17529619d2aSchristos         node = get_manpage_node (file_buffer, nodename);
17629619d2aSchristos     }
17729619d2aSchristos #endif /* HANDLE_MAN_PAGES */
17829619d2aSchristos   /* If this is the "main" info file, it might contain a tags table.  Search
17929619d2aSchristos      the tags table for an entry which matches the node that we want.  If
18029619d2aSchristos      there is a tags table, get the file which contains this node, but don't
18129619d2aSchristos      bother building a node list for it. */
18229619d2aSchristos   else if (file_buffer->tags)
18329619d2aSchristos     {
18429619d2aSchristos       node = info_node_of_file_buffer_tags (file_buffer, nodename);
18529619d2aSchristos     }
18629619d2aSchristos 
18729619d2aSchristos   /* Return the results of our node search. */
18829619d2aSchristos   return node;
18929619d2aSchristos }
19029619d2aSchristos 
19129619d2aSchristos /* Locate the file named by FILENAME, and return the information structure
19229619d2aSchristos    describing this file.  The file may appear in our list of loaded files
19329619d2aSchristos    already, or it may not.  If it does not already appear, find the file,
19429619d2aSchristos    and add it to the list of loaded files.  If the file cannot be found,
19529619d2aSchristos    return a NULL FILE_BUFFER *. */
19629619d2aSchristos FILE_BUFFER *
info_find_file(char * filename)19729619d2aSchristos info_find_file (char *filename)
19829619d2aSchristos {
19929619d2aSchristos   return info_find_file_internal (filename, INFO_GET_TAGS);
20029619d2aSchristos }
20129619d2aSchristos 
20229619d2aSchristos /* Load the info file FILENAME, remembering information about it in a
20329619d2aSchristos    file buffer. */
20429619d2aSchristos FILE_BUFFER *
info_load_file(char * filename)20529619d2aSchristos info_load_file (char *filename)
20629619d2aSchristos {
20729619d2aSchristos   return info_load_file_internal (filename, INFO_GET_TAGS);
20829619d2aSchristos }
20929619d2aSchristos 
21029619d2aSchristos 
21129619d2aSchristos /* Private functions implementation.  */
21229619d2aSchristos 
21329619d2aSchristos /* The workhorse for info_find_file ().  Non-zero 2nd argument says to
21429619d2aSchristos    try to build a tags table (or otherwise glean the nodes) for this
21529619d2aSchristos    file once found.  By default, we build the tags table, but when this
21629619d2aSchristos    function is called by info_get_node () when we already have a valid
21729619d2aSchristos    tags table describing the nodes, it is unnecessary. */
21829619d2aSchristos static FILE_BUFFER *
info_find_file_internal(char * filename,int get_tags)21929619d2aSchristos info_find_file_internal (char *filename, int get_tags)
22029619d2aSchristos {
22129619d2aSchristos   int i;
22229619d2aSchristos   FILE_BUFFER *file_buffer;
22329619d2aSchristos 
22429619d2aSchristos   /* First try to find the file in our list of already loaded files. */
22529619d2aSchristos   if (info_loaded_files)
22629619d2aSchristos     {
22729619d2aSchristos       for (i = 0; (file_buffer = info_loaded_files[i]); i++)
22829619d2aSchristos         if ((FILENAME_CMP (filename, file_buffer->filename) == 0)
22929619d2aSchristos             || (FILENAME_CMP (filename, file_buffer->fullpath) == 0)
23029619d2aSchristos             || (!IS_ABSOLUTE (filename)
23129619d2aSchristos                 && FILENAME_CMP (filename,
23229619d2aSchristos                                 filename_non_directory (file_buffer->fullpath))
23329619d2aSchristos                     == 0))
23429619d2aSchristos           {
23529619d2aSchristos             struct stat new_info, *old_info;
23629619d2aSchristos 
23729619d2aSchristos             /* This file is loaded.  If the filename that we want is
23829619d2aSchristos                specifically "dir", then simply return the file buffer. */
23929619d2aSchristos             if (is_dir_name (filename_non_directory (filename)))
24029619d2aSchristos               return file_buffer;
24129619d2aSchristos 
24229619d2aSchristos #if defined (HANDLE_MAN_PAGES)
24329619d2aSchristos             /* Do the same for the magic MANPAGE file. */
24429619d2aSchristos             if (file_buffer->flags & N_IsManPage)
24529619d2aSchristos               return file_buffer;
24629619d2aSchristos #endif /* HANDLE_MAN_PAGES */
24729619d2aSchristos 
24829619d2aSchristos             /* The file appears to be already loaded, and is not "dir".  Check
24929619d2aSchristos                to see if it's changed since the last time it was loaded.  */
25029619d2aSchristos             if (stat (file_buffer->fullpath, &new_info) == -1)
25129619d2aSchristos               {
25229619d2aSchristos                 filesys_error_number = errno;
25329619d2aSchristos                 return NULL;
25429619d2aSchristos               }
25529619d2aSchristos 
25629619d2aSchristos             old_info = &file_buffer->finfo;
25729619d2aSchristos 
25829619d2aSchristos             if (new_info.st_size != old_info->st_size
25929619d2aSchristos                 || new_info.st_mtime != old_info->st_mtime)
26029619d2aSchristos               {
26129619d2aSchristos                 /* The file has changed.  Forget that we ever had loaded it
26229619d2aSchristos                    in the first place. */
26329619d2aSchristos                 forget_info_file (filename);
26429619d2aSchristos                 break;
26529619d2aSchristos               }
26629619d2aSchristos             else
26729619d2aSchristos               {
26829619d2aSchristos                 /* The info file exists, and has not changed since the last
26929619d2aSchristos                    time it was loaded.  If the caller requested a nodes list
27029619d2aSchristos                    for this file, and there isn't one here, build the nodes
27129619d2aSchristos                    for this file_buffer.  In any case, return the file_buffer
27229619d2aSchristos                    object. */
27329619d2aSchristos                 if (!file_buffer->contents)
27429619d2aSchristos                   {
27529619d2aSchristos                     /* The file's contents have been gc'ed.  Reload it.  */
27629619d2aSchristos                     info_reload_file_buffer_contents (file_buffer);
27729619d2aSchristos                     if (!file_buffer->contents)
27829619d2aSchristos                       return NULL;
27929619d2aSchristos                   }
28029619d2aSchristos 
28129619d2aSchristos                 if (get_tags && !file_buffer->tags)
28229619d2aSchristos                   build_tags_and_nodes (file_buffer);
28329619d2aSchristos 
28429619d2aSchristos                 return file_buffer;
28529619d2aSchristos               }
28629619d2aSchristos           }
28729619d2aSchristos     }
28829619d2aSchristos 
28929619d2aSchristos   /* The file wasn't loaded.  Try to load it now. */
29029619d2aSchristos #if defined (HANDLE_MAN_PAGES)
29129619d2aSchristos   /* If the name of the file that we want is our special file buffer for
29229619d2aSchristos      Unix manual pages, then create the file buffer, and return it now. */
29329619d2aSchristos   if (strcasecmp (filename, MANPAGE_FILE_BUFFER_NAME) == 0)
29429619d2aSchristos     file_buffer = create_manpage_file_buffer ();
29529619d2aSchristos   else
29629619d2aSchristos #endif /* HANDLE_MAN_PAGES */
29729619d2aSchristos     file_buffer = info_load_file_internal (filename, get_tags);
29829619d2aSchristos 
29929619d2aSchristos   /* If the file was loaded, remember the name under which it was found. */
30029619d2aSchristos   if (file_buffer)
30129619d2aSchristos     remember_info_file (file_buffer);
30229619d2aSchristos 
30329619d2aSchristos   return file_buffer;
30429619d2aSchristos }
30529619d2aSchristos 
30629619d2aSchristos /* The workhorse function for info_load_file ().  Non-zero second argument
30729619d2aSchristos    says to build a list of tags (or nodes) for this file.  This is the
30829619d2aSchristos    default behaviour when info_load_file () is called, but it is not
30929619d2aSchristos    necessary when loading a subfile for which we already have tags. */
31029619d2aSchristos static FILE_BUFFER *
info_load_file_internal(char * filename,int get_tags)31129619d2aSchristos info_load_file_internal (char *filename, int get_tags)
31229619d2aSchristos {
31329619d2aSchristos   char *fullpath, *contents;
31429619d2aSchristos   long filesize;
31529619d2aSchristos   struct stat finfo;
31629619d2aSchristos   int retcode, compressed;
31729619d2aSchristos   FILE_BUFFER *file_buffer = NULL;
31829619d2aSchristos 
31929619d2aSchristos   /* Get the full pathname of this file, as known by the info system.
32029619d2aSchristos      That is to say, search along INFOPATH and expand tildes, etc. */
32129619d2aSchristos   fullpath = info_find_fullpath (filename);
32229619d2aSchristos 
32329619d2aSchristos   /* Did we actually find the file? */
32429619d2aSchristos   retcode = stat (fullpath, &finfo);
32529619d2aSchristos 
32629619d2aSchristos   /* If the file referenced by the name returned from info_find_fullpath ()
32729619d2aSchristos      doesn't exist, then try again with the last part of the filename
32829619d2aSchristos      appearing in lowercase. */
32929619d2aSchristos   /* This is probably not needed at all on those systems which define
33029619d2aSchristos      FILENAME_CMP to be strcasecmp.  But let's do it anyway, lest some
33129619d2aSchristos      network redirector supports case sensitivity.  */
33229619d2aSchristos   if (retcode < 0)
33329619d2aSchristos     {
33429619d2aSchristos       char *lowered_name;
33529619d2aSchristos       char *tmp_basename;
33629619d2aSchristos 
33729619d2aSchristos       lowered_name = xstrdup (filename);
33829619d2aSchristos       tmp_basename = filename_non_directory (lowered_name);
33929619d2aSchristos 
34029619d2aSchristos       while (*tmp_basename)
34129619d2aSchristos         {
34229619d2aSchristos           if (isupper (*tmp_basename))
34329619d2aSchristos             *tmp_basename = tolower (*tmp_basename);
34429619d2aSchristos 
34529619d2aSchristos           tmp_basename++;
34629619d2aSchristos         }
34729619d2aSchristos 
34829619d2aSchristos       fullpath = info_find_fullpath (lowered_name);
34929619d2aSchristos 
35029619d2aSchristos       retcode = stat (fullpath, &finfo);
35129619d2aSchristos       free (lowered_name);
35229619d2aSchristos     }
35329619d2aSchristos 
35429619d2aSchristos   /* If the file wasn't found, give up, returning a NULL pointer. */
35529619d2aSchristos   if (retcode < 0)
35629619d2aSchristos     {
35729619d2aSchristos       filesys_error_number = errno;
35829619d2aSchristos       return NULL;
35929619d2aSchristos     }
36029619d2aSchristos 
36129619d2aSchristos   /* Otherwise, try to load the file. */
36229619d2aSchristos   contents = filesys_read_info_file (fullpath, &filesize, &finfo, &compressed);
36329619d2aSchristos 
36429619d2aSchristos   if (!contents)
36529619d2aSchristos     return NULL;
36629619d2aSchristos 
36729619d2aSchristos   /* The file was found, and can be read.  Allocate FILE_BUFFER and fill
36829619d2aSchristos      in the various members. */
36929619d2aSchristos   file_buffer = make_file_buffer ();
37029619d2aSchristos   file_buffer->filename = xstrdup (filename);
37129619d2aSchristos   file_buffer->fullpath = xstrdup (fullpath);
37229619d2aSchristos   file_buffer->finfo = finfo;
37329619d2aSchristos   file_buffer->filesize = filesize;
37429619d2aSchristos   file_buffer->contents = contents;
37529619d2aSchristos   if (compressed)
37629619d2aSchristos     file_buffer->flags |= N_IsCompressed;
37729619d2aSchristos 
37829619d2aSchristos   /* If requested, build the tags and nodes for this file buffer. */
37929619d2aSchristos   if (get_tags)
38029619d2aSchristos     build_tags_and_nodes (file_buffer);
38129619d2aSchristos 
38229619d2aSchristos   return file_buffer;
38329619d2aSchristos }
38429619d2aSchristos 
38529619d2aSchristos /* Grovel FILE_BUFFER->contents finding tags and nodes, and filling in the
38629619d2aSchristos    various slots.  This can also be used to rebuild a tag or node table. */
38729619d2aSchristos void
build_tags_and_nodes(FILE_BUFFER * file_buffer)38829619d2aSchristos build_tags_and_nodes (FILE_BUFFER *file_buffer)
38929619d2aSchristos {
39029619d2aSchristos   SEARCH_BINDING binding;
39129619d2aSchristos   long position;
39229619d2aSchristos 
39329619d2aSchristos   free_file_buffer_tags (file_buffer);
39429619d2aSchristos   file_buffer->flags &= ~N_HasTagsTable;
39529619d2aSchristos 
39629619d2aSchristos   /* See if there is a tags table in this info file. */
39729619d2aSchristos   binding.buffer = file_buffer->contents;
39829619d2aSchristos   binding.start = file_buffer->filesize;
39929619d2aSchristos   binding.end = binding.start - 1000;
40029619d2aSchristos   if (binding.end < 0)
40129619d2aSchristos     binding.end = 0;
40229619d2aSchristos   binding.flags = S_FoldCase;
40329619d2aSchristos 
40429619d2aSchristos   position = search_backward (TAGS_TABLE_END_LABEL, &binding);
40529619d2aSchristos 
40629619d2aSchristos   /* If there is a tag table, find the start of it, and grovel over it
40729619d2aSchristos      extracting tag information. */
40829619d2aSchristos   if (position != -1)
40929619d2aSchristos     while (1)
41029619d2aSchristos       {
41129619d2aSchristos         long tags_table_begin, tags_table_end;
41229619d2aSchristos 
41329619d2aSchristos         binding.end = position;
41429619d2aSchristos         binding.start = binding.end - 5 - strlen (TAGS_TABLE_END_LABEL);
41529619d2aSchristos         if (binding.start < 0)
41629619d2aSchristos           binding.start = 0;
41729619d2aSchristos 
41829619d2aSchristos         position = find_node_separator (&binding);
41929619d2aSchristos 
42029619d2aSchristos         /* For this test, (and all others here) failure indicates a bogus
42129619d2aSchristos            tags table.  Grovel the file. */
42229619d2aSchristos         if (position == -1)
42329619d2aSchristos           break;
42429619d2aSchristos 
42529619d2aSchristos         /* Remember the end of the tags table. */
42629619d2aSchristos         binding.start = position;
42729619d2aSchristos         tags_table_end = binding.start;
42829619d2aSchristos         binding.end = 0;
42929619d2aSchristos 
43029619d2aSchristos         /* Locate the start of the tags table. */
43129619d2aSchristos         position = search_backward (TAGS_TABLE_BEG_LABEL, &binding);
43229619d2aSchristos 
43329619d2aSchristos         if (position == -1)
43429619d2aSchristos           break;
43529619d2aSchristos 
43629619d2aSchristos         binding.end = position;
43729619d2aSchristos         binding.start = binding.end - 5 - strlen (TAGS_TABLE_BEG_LABEL);
43829619d2aSchristos         position = find_node_separator (&binding);
43929619d2aSchristos 
44029619d2aSchristos         if (position == -1)
44129619d2aSchristos           break;
44229619d2aSchristos 
44329619d2aSchristos         /* The file contains a valid tags table.  Fill the FILE_BUFFER's
44429619d2aSchristos            tags member. */
44529619d2aSchristos         file_buffer->flags |= N_HasTagsTable;
44629619d2aSchristos         tags_table_begin = position;
44729619d2aSchristos 
44829619d2aSchristos         /* If this isn't an indirect tags table, just remember the nodes
44929619d2aSchristos            described locally in this tags table.  Note that binding.end
45029619d2aSchristos            is pointing to just after the beginning label. */
45129619d2aSchristos         binding.start = binding.end;
45229619d2aSchristos         binding.end = file_buffer->filesize;
45329619d2aSchristos 
45429619d2aSchristos         if (!looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, &binding))
45529619d2aSchristos           {
45629619d2aSchristos             binding.start = tags_table_begin;
45729619d2aSchristos             binding.end = tags_table_end;
45829619d2aSchristos             get_nodes_of_tags_table (file_buffer, &binding);
45929619d2aSchristos             return;
46029619d2aSchristos           }
46129619d2aSchristos         else
46229619d2aSchristos           {
46329619d2aSchristos             /* This is an indirect tags table.  Build TAGS member. */
46429619d2aSchristos             SEARCH_BINDING indirect;
46529619d2aSchristos 
46629619d2aSchristos             indirect.start = tags_table_begin;
46729619d2aSchristos             indirect.end = 0;
46829619d2aSchristos             indirect.buffer = binding.buffer;
46929619d2aSchristos             indirect.flags = S_FoldCase;
47029619d2aSchristos 
47129619d2aSchristos             position = search_backward (INDIRECT_TAGS_TABLE_LABEL, &indirect);
47229619d2aSchristos 
47329619d2aSchristos             if (position == -1)
47429619d2aSchristos               {
47529619d2aSchristos                 /* This file is malformed.  Give up. */
47629619d2aSchristos                 return;
47729619d2aSchristos               }
47829619d2aSchristos 
47929619d2aSchristos             indirect.start = position;
48029619d2aSchristos             indirect.end = tags_table_begin;
48129619d2aSchristos             binding.start = tags_table_begin;
48229619d2aSchristos             binding.end = tags_table_end;
48329619d2aSchristos             get_tags_of_indirect_tags_table (file_buffer, &indirect, &binding);
48429619d2aSchristos             return;
48529619d2aSchristos           }
48629619d2aSchristos       }
48729619d2aSchristos 
48829619d2aSchristos   /* This file doesn't contain any kind of tags table.  Grovel the
48929619d2aSchristos      file and build node entries for it. */
49029619d2aSchristos   get_nodes_of_info_file (file_buffer);
49129619d2aSchristos }
49229619d2aSchristos 
49329619d2aSchristos /* Search through FILE_BUFFER->contents building an array of TAG *,
49429619d2aSchristos    one entry per each node present in the file.  Store the tags in
49529619d2aSchristos    FILE_BUFFER->tags, and the number of allocated slots in
49629619d2aSchristos    FILE_BUFFER->tags_slots. */
49729619d2aSchristos static void
get_nodes_of_info_file(FILE_BUFFER * file_buffer)49829619d2aSchristos get_nodes_of_info_file (FILE_BUFFER *file_buffer)
49929619d2aSchristos {
50029619d2aSchristos   long nodestart;
50129619d2aSchristos   int tags_index = 0;
50229619d2aSchristos   SEARCH_BINDING binding;
50329619d2aSchristos 
50429619d2aSchristos   binding.buffer = file_buffer->contents;
50529619d2aSchristos   binding.start = 0;
50629619d2aSchristos   binding.end = file_buffer->filesize;
50729619d2aSchristos   binding.flags = S_FoldCase;
50829619d2aSchristos 
50929619d2aSchristos   while ((nodestart = find_node_separator (&binding)) != -1)
51029619d2aSchristos     {
51129619d2aSchristos       int start, end;
51229619d2aSchristos       char *nodeline;
51329619d2aSchristos       TAG *entry;
51429619d2aSchristos       int anchor = 0;
51529619d2aSchristos 
51629619d2aSchristos       /* Skip past the characters just found. */
51729619d2aSchristos       binding.start = nodestart;
51829619d2aSchristos       binding.start += skip_node_separator (binding.buffer + binding.start);
51929619d2aSchristos 
52029619d2aSchristos       /* Move to the start of the line defining the node. */
52129619d2aSchristos       nodeline = binding.buffer + binding.start;
52229619d2aSchristos 
52329619d2aSchristos       /* Find "Node:" */
52429619d2aSchristos       start = string_in_line (INFO_NODE_LABEL, nodeline);
52529619d2aSchristos       /* No Node:.  Maybe it's a Ref:.  */
52629619d2aSchristos       if (start == -1)
52729619d2aSchristos         {
52829619d2aSchristos           start = string_in_line (INFO_REF_LABEL, nodeline);
52929619d2aSchristos           if (start != -1)
53029619d2aSchristos             anchor = 1;
53129619d2aSchristos         }
53229619d2aSchristos 
53329619d2aSchristos       /* If not there, this is not the start of a node. */
53429619d2aSchristos       if (start == -1)
53529619d2aSchristos         continue;
53629619d2aSchristos 
53729619d2aSchristos       /* Find the start of the nodename. */
53829619d2aSchristos       start += skip_whitespace (nodeline + start);
53929619d2aSchristos 
54029619d2aSchristos       /* Find the end of the nodename. */
54129619d2aSchristos       end = start +
54229619d2aSchristos         skip_node_characters (nodeline + start, DONT_SKIP_NEWLINES);
54329619d2aSchristos 
54429619d2aSchristos       /* Okay, we have isolated the node name, and we know where the
54529619d2aSchristos          node starts.  Remember this information. */
54629619d2aSchristos       entry = xmalloc (sizeof (TAG));
54729619d2aSchristos       entry->nodename = xmalloc (1 + (end - start));
54829619d2aSchristos       strncpy (entry->nodename, nodeline + start, end - start);
54929619d2aSchristos       entry->nodename[end - start] = 0;
55029619d2aSchristos       entry->nodestart = nodestart;
55129619d2aSchristos       if (anchor)
55229619d2aSchristos         entry->nodelen = 0;
55329619d2aSchristos       else
55429619d2aSchristos         {
55529619d2aSchristos           SEARCH_BINDING node_body;
55629619d2aSchristos 
55729619d2aSchristos           node_body.buffer = binding.buffer + binding.start;
55829619d2aSchristos           node_body.start = 0;
55929619d2aSchristos           node_body.end = binding.end - binding.start;
56029619d2aSchristos           node_body.flags = S_FoldCase;
56129619d2aSchristos           entry->nodelen = get_node_length (&node_body);
56229619d2aSchristos         }
56329619d2aSchristos 
56429619d2aSchristos       entry->filename = file_buffer->fullpath;
56529619d2aSchristos 
56629619d2aSchristos       /* Add this tag to the array of tag structures in this FILE_BUFFER. */
56729619d2aSchristos       add_pointer_to_array (entry, tags_index, file_buffer->tags,
56829619d2aSchristos                             file_buffer->tags_slots, 100, TAG *);
56929619d2aSchristos     }
57029619d2aSchristos }
57129619d2aSchristos 
57229619d2aSchristos /* Return the length of the node which starts at BINDING. */
57329619d2aSchristos static long
get_node_length(SEARCH_BINDING * binding)57429619d2aSchristos get_node_length (SEARCH_BINDING *binding)
57529619d2aSchristos {
57629619d2aSchristos   int i;
57729619d2aSchristos   char *body;
57829619d2aSchristos 
57929619d2aSchristos   /* [A node] ends with either a ^_, a ^L, or end of file.  */
58029619d2aSchristos   for (i = binding->start, body = binding->buffer; i < binding->end; i++)
58129619d2aSchristos     {
58229619d2aSchristos       if (body[i] == INFO_FF || body[i] == INFO_COOKIE)
58329619d2aSchristos         break;
58429619d2aSchristos     }
58529619d2aSchristos   return i - binding->start;
58629619d2aSchristos }
58729619d2aSchristos 
58829619d2aSchristos /* Build and save the array of nodes in FILE_BUFFER by searching through the
58929619d2aSchristos    contents of BUFFER_BINDING for a tags table, and groveling the contents. */
59029619d2aSchristos static void
get_nodes_of_tags_table(FILE_BUFFER * file_buffer,SEARCH_BINDING * buffer_binding)59129619d2aSchristos get_nodes_of_tags_table (FILE_BUFFER *file_buffer,
59229619d2aSchristos     SEARCH_BINDING *buffer_binding)
59329619d2aSchristos {
59429619d2aSchristos   int name_offset;
59529619d2aSchristos   SEARCH_BINDING *tmp_search;
59629619d2aSchristos   long position;
59729619d2aSchristos   int tags_index = 0;
59829619d2aSchristos 
59929619d2aSchristos   tmp_search = copy_binding (buffer_binding);
60029619d2aSchristos 
60129619d2aSchristos   /* Find the start of the tags table. */
60229619d2aSchristos   position = find_tags_table (tmp_search);
60329619d2aSchristos 
60429619d2aSchristos   /* If none, we're all done. */
60529619d2aSchristos   if (position == -1)
60629619d2aSchristos     return;
60729619d2aSchristos 
60829619d2aSchristos   /* Move to one character before the start of the actual table. */
60929619d2aSchristos   tmp_search->start = position;
61029619d2aSchristos   tmp_search->start += skip_node_separator
61129619d2aSchristos     (tmp_search->buffer + tmp_search->start);
61229619d2aSchristos   tmp_search->start += strlen (TAGS_TABLE_BEG_LABEL);
61329619d2aSchristos   tmp_search->start--;
61429619d2aSchristos 
61529619d2aSchristos   /* The tag table consists of lines containing node names and positions.
61629619d2aSchristos      Do each line until we find one that doesn't contain a node name. */
61729619d2aSchristos   while ((position = search_forward ("\n", tmp_search)) != -1)
61829619d2aSchristos     {
61929619d2aSchristos       TAG *entry;
62029619d2aSchristos       char *nodedef;
62129619d2aSchristos       unsigned p;
62229619d2aSchristos       int anchor = 0;
62329619d2aSchristos 
62429619d2aSchristos       /* Prepare to skip this line. */
62529619d2aSchristos       tmp_search->start = position;
62629619d2aSchristos       tmp_search->start++;
62729619d2aSchristos 
62829619d2aSchristos       /* Skip past informative "(Indirect)" tags table line. */
62929619d2aSchristos       if (!tags_index && looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, tmp_search))
63029619d2aSchristos         continue;
63129619d2aSchristos 
63229619d2aSchristos       /* Find the label preceding the node name. */
63329619d2aSchristos       name_offset =
63429619d2aSchristos         string_in_line (INFO_NODE_LABEL, tmp_search->buffer + tmp_search->start);
63529619d2aSchristos 
63629619d2aSchristos       /* If no node label, maybe it's an anchor.  */
63729619d2aSchristos       if (name_offset == -1)
63829619d2aSchristos         {
63929619d2aSchristos           name_offset = string_in_line (INFO_REF_LABEL,
64029619d2aSchristos               tmp_search->buffer + tmp_search->start);
64129619d2aSchristos           if (name_offset != -1)
64229619d2aSchristos             anchor = 1;
64329619d2aSchristos         }
64429619d2aSchristos 
64529619d2aSchristos       /* If not there, not a defining line, so we must be out of the
64629619d2aSchristos          tags table.  */
64729619d2aSchristos       if (name_offset == -1)
64829619d2aSchristos         break;
64929619d2aSchristos 
65029619d2aSchristos       entry = xmalloc (sizeof (TAG));
65129619d2aSchristos 
65229619d2aSchristos       /* Find the beginning of the node definition. */
65329619d2aSchristos       tmp_search->start += name_offset;
65429619d2aSchristos       nodedef = tmp_search->buffer + tmp_search->start;
65529619d2aSchristos       nodedef += skip_whitespace (nodedef);
65629619d2aSchristos 
65729619d2aSchristos       /* Move past the node's name in this tag to the TAGSEP character. */
65829619d2aSchristos       for (p = 0; nodedef[p] && nodedef[p] != INFO_TAGSEP; p++)
65929619d2aSchristos         ;
66029619d2aSchristos       if (nodedef[p] != INFO_TAGSEP)
66129619d2aSchristos         continue;
66229619d2aSchristos 
66329619d2aSchristos       entry->nodename = xmalloc (p + 1);
66429619d2aSchristos       strncpy (entry->nodename, nodedef, p);
66529619d2aSchristos       entry->nodename[p] = 0;
66629619d2aSchristos       p++;
66729619d2aSchristos       entry->nodestart = atol (nodedef + p);
66829619d2aSchristos 
66929619d2aSchristos       /* If a node, we don't know the length yet, but if it's an
67029619d2aSchristos          anchor, the length is 0. */
67129619d2aSchristos       entry->nodelen = anchor ? 0 : -1;
67229619d2aSchristos 
67329619d2aSchristos       /* The filename of this node is currently known as the same as the
67429619d2aSchristos          name of this file. */
67529619d2aSchristos       entry->filename = file_buffer->fullpath;
67629619d2aSchristos 
67729619d2aSchristos       /* Add this node structure to the array of node structures in this
67829619d2aSchristos          FILE_BUFFER. */
67929619d2aSchristos       add_pointer_to_array (entry, tags_index, file_buffer->tags,
68029619d2aSchristos                             file_buffer->tags_slots, 100, TAG *);
68129619d2aSchristos     }
68229619d2aSchristos   free (tmp_search);
68329619d2aSchristos }
68429619d2aSchristos 
68529619d2aSchristos /* A structure used only in `get_tags_of_indirect_tags_table' to hold onto
68629619d2aSchristos    an intermediate value. */
68729619d2aSchristos typedef struct {
68829619d2aSchristos   char *filename;
68929619d2aSchristos   long first_byte;
69029619d2aSchristos } SUBFILE;
69129619d2aSchristos 
69229619d2aSchristos /* Remember in FILE_BUFFER the nodenames, subfilenames, and offsets within the
69329619d2aSchristos    subfiles of every node which appears in TAGS_BINDING.  The 2nd argument is
69429619d2aSchristos    a binding surrounding the indirect files list. */
69529619d2aSchristos static void
get_tags_of_indirect_tags_table(FILE_BUFFER * file_buffer,SEARCH_BINDING * indirect_binding,SEARCH_BINDING * tags_binding)69629619d2aSchristos get_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer,
69729619d2aSchristos     SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding)
69829619d2aSchristos {
69929619d2aSchristos   int i;
70029619d2aSchristos   SUBFILE **subfiles = NULL;
70129619d2aSchristos   int subfiles_index = 0, subfiles_slots = 0;
70229619d2aSchristos   TAG *entry;
70329619d2aSchristos 
70429619d2aSchristos   /* First get the list of tags from the tags table.  Then lookup the
70529619d2aSchristos      associated file in the indirect list for each tag, and update it. */
70629619d2aSchristos   get_nodes_of_tags_table (file_buffer, tags_binding);
70729619d2aSchristos 
70829619d2aSchristos   /* We have the list of tags in file_buffer->tags.  Get the list of
70929619d2aSchristos      subfiles from the indirect table. */
71029619d2aSchristos   {
71129619d2aSchristos     char *start, *end, *line;
71229619d2aSchristos     SUBFILE *subfile;
71329619d2aSchristos 
71429619d2aSchristos     start = indirect_binding->buffer + indirect_binding->start;
71529619d2aSchristos     end = indirect_binding->buffer + indirect_binding->end;
71629619d2aSchristos     line = start;
71729619d2aSchristos 
71829619d2aSchristos     while (line < end)
71929619d2aSchristos       {
72029619d2aSchristos         int colon;
72129619d2aSchristos 
72229619d2aSchristos         colon = string_in_line (":", line);
72329619d2aSchristos 
72429619d2aSchristos         if (colon == -1)
72529619d2aSchristos           break;
72629619d2aSchristos 
72729619d2aSchristos         subfile = (SUBFILE *)xmalloc (sizeof (SUBFILE));
72829619d2aSchristos         subfile->filename = (char *)xmalloc (colon);
72929619d2aSchristos         strncpy (subfile->filename, line, colon - 1);
73029619d2aSchristos         subfile->filename[colon - 1] = 0;
73129619d2aSchristos         subfile->first_byte = (long) atol (line + colon);
73229619d2aSchristos 
73329619d2aSchristos         add_pointer_to_array
73429619d2aSchristos           (subfile, subfiles_index, subfiles, subfiles_slots, 10, SUBFILE *);
73529619d2aSchristos 
73629619d2aSchristos         while (*line++ != '\n');
73729619d2aSchristos       }
73829619d2aSchristos   }
73929619d2aSchristos 
74029619d2aSchristos   /* If we have successfully built the indirect files table, then
74129619d2aSchristos      merge the information in the two tables. */
74229619d2aSchristos   if (!subfiles)
74329619d2aSchristos     {
74429619d2aSchristos       free_file_buffer_tags (file_buffer);
74529619d2aSchristos       return;
74629619d2aSchristos     }
74729619d2aSchristos   else
74829619d2aSchristos     {
74929619d2aSchristos       int tags_index;
75029619d2aSchristos       long header_length;
75129619d2aSchristos       SEARCH_BINDING binding;
75229619d2aSchristos 
75329619d2aSchristos       /* Find the length of the header of the file containing the indirect
75429619d2aSchristos          tags table.  This header appears at the start of every file.  We
75529619d2aSchristos          want the absolute position of each node within each subfile, so
75629619d2aSchristos          we subtract the start of the containing subfile from the logical
75729619d2aSchristos          position of the node, and then add the length of the header in. */
75829619d2aSchristos       binding.buffer = file_buffer->contents;
75929619d2aSchristos       binding.start = 0;
76029619d2aSchristos       binding.end = file_buffer->filesize;
76129619d2aSchristos       binding.flags = S_FoldCase;
76229619d2aSchristos 
76329619d2aSchristos       header_length = find_node_separator (&binding);
76429619d2aSchristos       if (header_length == -1)
76529619d2aSchristos         header_length = 0;
76629619d2aSchristos 
76729619d2aSchristos       /* Build the file buffer's list of subfiles. */
76829619d2aSchristos       {
76929619d2aSchristos         char *containing_dir = xstrdup (file_buffer->fullpath);
77029619d2aSchristos         char *temp = filename_non_directory (containing_dir);
77129619d2aSchristos         int len_containing_dir;
77229619d2aSchristos 
77329619d2aSchristos         if (temp > containing_dir)
77429619d2aSchristos           {
77529619d2aSchristos             if (HAVE_DRIVE (file_buffer->fullpath) &&
77629619d2aSchristos                 temp == containing_dir + 2)
77729619d2aSchristos               {
77829619d2aSchristos                 /* Avoid converting "d:foo" into "d:/foo" below.  */
77929619d2aSchristos                 *temp = '.';
78029619d2aSchristos                 temp += 2;
78129619d2aSchristos               }
78229619d2aSchristos             temp[-1] = 0;
78329619d2aSchristos           }
78429619d2aSchristos 
78529619d2aSchristos         len_containing_dir = strlen (containing_dir);
78629619d2aSchristos 
78729619d2aSchristos         for (i = 0; subfiles[i]; i++);
78829619d2aSchristos 
78929619d2aSchristos         file_buffer->subfiles = (char **) xmalloc ((1 + i) * sizeof (char *));
79029619d2aSchristos 
79129619d2aSchristos         for (i = 0; subfiles[i]; i++)
79229619d2aSchristos           {
79329619d2aSchristos             char *fullpath;
79429619d2aSchristos 
79529619d2aSchristos             fullpath = (char *) xmalloc
79629619d2aSchristos               (2 + strlen (subfiles[i]->filename) + len_containing_dir);
79729619d2aSchristos 
79829619d2aSchristos             sprintf (fullpath, "%s/%s",
79929619d2aSchristos                      containing_dir, subfiles[i]->filename);
80029619d2aSchristos 
80129619d2aSchristos             file_buffer->subfiles[i] = fullpath;
80229619d2aSchristos           }
80329619d2aSchristos         file_buffer->subfiles[i] = NULL;
80429619d2aSchristos         free (containing_dir);
80529619d2aSchristos       }
80629619d2aSchristos 
80729619d2aSchristos       /* For each node in the file's tags table, remember the starting
80829619d2aSchristos          position. */
80929619d2aSchristos       for (tags_index = 0; (entry = file_buffer->tags[tags_index]);
81029619d2aSchristos            tags_index++)
81129619d2aSchristos         {
81229619d2aSchristos           for (i = 0;
81329619d2aSchristos                subfiles[i] && entry->nodestart >= subfiles[i]->first_byte;
81429619d2aSchristos                i++);
81529619d2aSchristos 
81629619d2aSchristos           /* If the Info file containing the indirect tags table is
81729619d2aSchristos              malformed, then give up. */
81829619d2aSchristos           if (!i)
81929619d2aSchristos             {
82029619d2aSchristos               /* The Info file containing the indirect tags table is
82129619d2aSchristos                  malformed.  Give up. */
82229619d2aSchristos               for (i = 0; subfiles[i]; i++)
82329619d2aSchristos                 {
82429619d2aSchristos                   free (subfiles[i]->filename);
82529619d2aSchristos                   free (subfiles[i]);
82629619d2aSchristos                   free (file_buffer->subfiles[i]);
82729619d2aSchristos                 }
82829619d2aSchristos               file_buffer->subfiles = NULL;
82929619d2aSchristos               free_file_buffer_tags (file_buffer);
83029619d2aSchristos               return;
83129619d2aSchristos             }
83229619d2aSchristos 
83329619d2aSchristos           /* SUBFILES[i] is the index of the first subfile whose logical
83429619d2aSchristos              first byte is greater than the logical offset of this node's
83529619d2aSchristos              starting position.  This means that the subfile directly
83629619d2aSchristos              preceding this one is the one containing the node. */
83729619d2aSchristos 
83829619d2aSchristos           entry->filename = file_buffer->subfiles[i - 1];
83929619d2aSchristos           entry->nodestart -= subfiles[i - 1]->first_byte;
84029619d2aSchristos           entry->nodestart += header_length;
84129619d2aSchristos         }
84229619d2aSchristos 
84329619d2aSchristos       /* We have successfully built the tags table.  Remember that it
84429619d2aSchristos          was indirect. */
84529619d2aSchristos       file_buffer->flags |= N_TagsIndirect;
84629619d2aSchristos     }
84729619d2aSchristos 
84829619d2aSchristos   /* Free the structures assigned to SUBFILES.  Free the names as well
84929619d2aSchristos      as the structures themselves, then finally, the array. */
85029619d2aSchristos   for (i = 0; subfiles[i]; i++)
85129619d2aSchristos     {
85229619d2aSchristos       free (subfiles[i]->filename);
85329619d2aSchristos       free (subfiles[i]);
85429619d2aSchristos     }
85529619d2aSchristos   free (subfiles);
85629619d2aSchristos }
85729619d2aSchristos 
85829619d2aSchristos 
85929619d2aSchristos /* Return the node that contains TAG in FILE_BUFFER, else
86029619d2aSchristos    (pathologically) NULL.  Called from info_node_of_file_buffer_tags.  */
86129619d2aSchristos static NODE *
find_node_of_anchor(FILE_BUFFER * file_buffer,TAG * tag)86229619d2aSchristos find_node_of_anchor (FILE_BUFFER *file_buffer, TAG *tag)
86329619d2aSchristos {
86429619d2aSchristos   int anchor_pos, node_pos;
86529619d2aSchristos   TAG *node_tag;
86629619d2aSchristos   NODE *node;
86729619d2aSchristos 
86829619d2aSchristos   /* Look through the tag list for the anchor.  */
86929619d2aSchristos   for (anchor_pos = 0; file_buffer->tags[anchor_pos]; anchor_pos++)
87029619d2aSchristos     {
87129619d2aSchristos       TAG *t = file_buffer->tags[anchor_pos];
87229619d2aSchristos       if (t->nodestart == tag->nodestart)
87329619d2aSchristos         break;
87429619d2aSchristos     }
87529619d2aSchristos 
87629619d2aSchristos   /* Should not happen, because we should always find the anchor.  */
87729619d2aSchristos   if (!file_buffer->tags[anchor_pos])
87829619d2aSchristos     return NULL;
87929619d2aSchristos 
88029619d2aSchristos   /* We've found the anchor.  Look backwards in the tag table for the
88129619d2aSchristos      preceding node (we're assuming the tags are given in order),
88229619d2aSchristos      skipping over any preceding anchors.  */
88329619d2aSchristos   for (node_pos = anchor_pos - 1;
88429619d2aSchristos        node_pos >= 0 && file_buffer->tags[node_pos]->nodelen == 0;
88529619d2aSchristos        node_pos--)
88629619d2aSchristos     ;
88729619d2aSchristos 
88829619d2aSchristos   /* An info file with an anchor before any nodes is pathological, but
88929619d2aSchristos      it's possible, so don't crash.  */
89029619d2aSchristos   if (node_pos < 0)
89129619d2aSchristos     return NULL;
89229619d2aSchristos 
89329619d2aSchristos   /* We have the tag for the node that contained the anchor tag.  */
89429619d2aSchristos   node_tag = file_buffer->tags[node_pos];
89529619d2aSchristos 
89629619d2aSchristos   /* Look up the node name in the tag table to get the actual node.
89729619d2aSchristos      This is a recursive call, but it can't recurse again, because we
89829619d2aSchristos      call it with a real node.  */
89929619d2aSchristos   node = info_node_of_file_buffer_tags (file_buffer, node_tag->nodename);
90029619d2aSchristos 
90129619d2aSchristos   /* Start displaying the node at the anchor position.  */
90229619d2aSchristos   if (node)
90329619d2aSchristos     { /* The nodestart for real nodes is three characters before the `F'
90429619d2aSchristos          in the `File:' line (a newline, the CTRL-_, and another
90529619d2aSchristos          newline).  The nodestart for anchors is the actual position.
90629619d2aSchristos          But we offset by only 2, rather than 3, because if an anchor is
90729619d2aSchristos          at the beginning of a paragraph, it's nicer for it to end up on
90829619d2aSchristos          the beginning of the first line of the paragraph rather than
90929619d2aSchristos          the blank line before it.  (makeinfo has no way of knowing that
91029619d2aSchristos          a paragraph is going to start, so we can't fix it there.)  */
91129619d2aSchristos       node->display_pos = file_buffer->tags[anchor_pos]->nodestart
91229619d2aSchristos                           - (node_tag->nodestart + 2);
91329619d2aSchristos 
91429619d2aSchristos       /* Otherwise an anchor at the end of a node ends up displaying at
91529619d2aSchristos          the end of the last line of the node (way over on the right of
91629619d2aSchristos          the screen), which looks wrong.  */
91729619d2aSchristos       if (node->display_pos >= (unsigned long) node->nodelen)
91829619d2aSchristos         node->display_pos = node->nodelen - 1;
91929619d2aSchristos 
92029619d2aSchristos       /* Don't search in the node for the xref text, it's not there.  */
92129619d2aSchristos       node->flags |= N_FromAnchor;
92229619d2aSchristos     }
92329619d2aSchristos 
92429619d2aSchristos   return node;
92529619d2aSchristos }
92629619d2aSchristos 
92729619d2aSchristos 
92829619d2aSchristos /* Return the node from FILE_BUFFER which matches NODENAME by searching
92929619d2aSchristos    the tags table in FILE_BUFFER, or NULL.  */
93029619d2aSchristos static NODE *
info_node_of_file_buffer_tags(FILE_BUFFER * file_buffer,char * nodename)93129619d2aSchristos info_node_of_file_buffer_tags (FILE_BUFFER *file_buffer, char *nodename)
93229619d2aSchristos {
93329619d2aSchristos   TAG *tag;
93429619d2aSchristos   int i;
93529619d2aSchristos 
93629619d2aSchristos   /* If no tags at all (possibly a misformatted info file), quit.  */
93729619d2aSchristos   if (!file_buffer->tags) {
93829619d2aSchristos     return NULL;
93929619d2aSchristos   }
94029619d2aSchristos 
94129619d2aSchristos   for (i = 0; (tag = file_buffer->tags[i]); i++)
94229619d2aSchristos     if (strcmp (nodename, tag->nodename) == 0)
94329619d2aSchristos       {
94429619d2aSchristos         FILE_BUFFER *subfile = info_find_file_internal (tag->filename,
94529619d2aSchristos                                                         INFO_NO_TAGS);
94629619d2aSchristos         if (!subfile)
94729619d2aSchristos           return NULL;
94829619d2aSchristos 
94929619d2aSchristos         if (!subfile->contents)
95029619d2aSchristos           {
95129619d2aSchristos             info_reload_file_buffer_contents (subfile);
95229619d2aSchristos             if (!subfile->contents)
95329619d2aSchristos               return NULL;
95429619d2aSchristos           }
95529619d2aSchristos 
95629619d2aSchristos         /* If we were able to find this file and load it, then return
95729619d2aSchristos            the node within it. */
95829619d2aSchristos         {
95929619d2aSchristos           NODE *node = xmalloc (sizeof (NODE));
96029619d2aSchristos           node->filename    = subfile->fullpath;
96129619d2aSchristos           node->parent      = NULL;
96229619d2aSchristos           node->nodename    = tag->nodename;
96329619d2aSchristos           node->contents    = subfile->contents + tag->nodestart;
96429619d2aSchristos           node->display_pos = 0;
96529619d2aSchristos           node->flags       = 0;
96629619d2aSchristos 
96729619d2aSchristos           if (file_buffer->flags & N_HasTagsTable)
96829619d2aSchristos             {
96929619d2aSchristos               node->flags |= N_HasTagsTable;
97029619d2aSchristos 
97129619d2aSchristos               if (file_buffer->flags & N_TagsIndirect)
97229619d2aSchristos                 {
97329619d2aSchristos                   node->flags |= N_TagsIndirect;
97429619d2aSchristos                   node->parent = file_buffer->fullpath;
97529619d2aSchristos                 }
97629619d2aSchristos             }
97729619d2aSchristos 
97829619d2aSchristos           if (subfile->flags & N_IsCompressed)
97929619d2aSchristos             node->flags |= N_IsCompressed;
98029619d2aSchristos 
98129619d2aSchristos           /* If TAG->nodelen hasn't been calculated yet, then we aren't
98229619d2aSchristos              in a position to trust the entry pointer.  Adjust things so
98329619d2aSchristos              that ENTRY->nodestart gets the exact address of the start of
98429619d2aSchristos              the node separator which starts this node, and NODE->contents
98529619d2aSchristos              gets the address of the line defining this node.  If we cannot
98629619d2aSchristos              do that, the node isn't really here. */
98729619d2aSchristos           if (tag->nodelen == -1)
98829619d2aSchristos             {
98929619d2aSchristos               int min, max;
99029619d2aSchristos               char *node_sep;
99129619d2aSchristos               SEARCH_BINDING node_body;
99229619d2aSchristos               char *buff_end;
99329619d2aSchristos 
99429619d2aSchristos               min = max = DEFAULT_INFO_FUDGE;
99529619d2aSchristos 
99629619d2aSchristos               if (tag->nodestart < DEFAULT_INFO_FUDGE)
99729619d2aSchristos                 min = tag->nodestart;
99829619d2aSchristos 
99929619d2aSchristos               if (DEFAULT_INFO_FUDGE >
100029619d2aSchristos                   (subfile->filesize - tag->nodestart))
100129619d2aSchristos                 max = subfile->filesize - tag->nodestart;
100229619d2aSchristos 
100329619d2aSchristos               /* NODE_SEP gets the address of the separator which defines
100429619d2aSchristos                  this node, or NULL if the node wasn't found.
100529619d2aSchristos                  NODE->contents is side-effected to point to right after
100629619d2aSchristos                  the separator. */
100729619d2aSchristos               node_sep = adjust_nodestart (node, min, max);
100829619d2aSchristos               if (node_sep == NULL)
100929619d2aSchristos                 {
101029619d2aSchristos                   free (node);
101129619d2aSchristos                   return NULL;
101229619d2aSchristos                 }
101329619d2aSchristos               /* Readjust tag->nodestart. */
101429619d2aSchristos               tag->nodestart = node_sep - subfile->contents;
101529619d2aSchristos 
101629619d2aSchristos               /* Calculate the length of the current node. */
101729619d2aSchristos               buff_end = subfile->contents + subfile->filesize;
101829619d2aSchristos 
101929619d2aSchristos               node_body.buffer = node->contents;
102029619d2aSchristos               node_body.start = 0;
102129619d2aSchristos               node_body.end = buff_end - node_body.buffer;
102229619d2aSchristos               node_body.flags = 0;
102329619d2aSchristos               tag->nodelen = get_node_length (&node_body);
102429619d2aSchristos               node->nodelen = tag->nodelen;
102529619d2aSchristos             }
102629619d2aSchristos 
102729619d2aSchristos           else if (tag->nodelen == 0) /* anchor, return containing node */
102829619d2aSchristos             {
102929619d2aSchristos               free (node);
103029619d2aSchristos               node = find_node_of_anchor (file_buffer, tag);
103129619d2aSchristos             }
103229619d2aSchristos 
103329619d2aSchristos           else
103429619d2aSchristos             {
103529619d2aSchristos               /* Since we know the length of this node, we have already
103629619d2aSchristos                  adjusted tag->nodestart to point to the exact start of
103729619d2aSchristos                  it.  Simply skip the node separator. */
103829619d2aSchristos               node->contents += skip_node_separator (node->contents);
103929619d2aSchristos               node->nodelen = tag->nodelen;
104029619d2aSchristos             }
104129619d2aSchristos 
104229619d2aSchristos           return node;
104329619d2aSchristos         }
104429619d2aSchristos       }
104529619d2aSchristos 
104629619d2aSchristos   /* There was a tag table for this file, and the node wasn't found.
104729619d2aSchristos      Return NULL, since this file doesn't contain the desired node. */
104829619d2aSchristos   return NULL;
104929619d2aSchristos }
105029619d2aSchristos 
105129619d2aSchristos /* Managing file_buffers, nodes, and tags.  */
105229619d2aSchristos 
105329619d2aSchristos /* Create a new, empty file buffer. */
105429619d2aSchristos FILE_BUFFER *
make_file_buffer(void)105529619d2aSchristos make_file_buffer (void)
105629619d2aSchristos {
105729619d2aSchristos   FILE_BUFFER *file_buffer = xmalloc (sizeof (FILE_BUFFER));
105829619d2aSchristos 
105929619d2aSchristos   file_buffer->filename = file_buffer->fullpath = NULL;
106029619d2aSchristos   file_buffer->contents = NULL;
106129619d2aSchristos   file_buffer->tags = NULL;
106229619d2aSchristos   file_buffer->subfiles = NULL;
106329619d2aSchristos   file_buffer->tags_slots = 0;
106429619d2aSchristos   file_buffer->flags = 0;
106529619d2aSchristos 
106629619d2aSchristos   return file_buffer;
106729619d2aSchristos }
106829619d2aSchristos 
106929619d2aSchristos /* Add FILE_BUFFER to our list of already loaded info files. */
107029619d2aSchristos static void
remember_info_file(FILE_BUFFER * file_buffer)107129619d2aSchristos remember_info_file (FILE_BUFFER *file_buffer)
107229619d2aSchristos {
107329619d2aSchristos   int i;
107429619d2aSchristos 
107529619d2aSchristos   for (i = 0; info_loaded_files && info_loaded_files[i]; i++)
107629619d2aSchristos     ;
107729619d2aSchristos 
107829619d2aSchristos   add_pointer_to_array (file_buffer, i, info_loaded_files,
107929619d2aSchristos                         info_loaded_files_slots, 10, FILE_BUFFER *);
108029619d2aSchristos }
108129619d2aSchristos 
108229619d2aSchristos /* Forget the contents, tags table, nodes list, and names of FILENAME. */
108329619d2aSchristos static void
forget_info_file(char * filename)108429619d2aSchristos forget_info_file (char *filename)
108529619d2aSchristos {
108629619d2aSchristos   int i;
108729619d2aSchristos   FILE_BUFFER *file_buffer;
108829619d2aSchristos 
108929619d2aSchristos   if (!info_loaded_files)
109029619d2aSchristos     return;
109129619d2aSchristos 
109229619d2aSchristos   for (i = 0; (file_buffer = info_loaded_files[i]); i++)
109329619d2aSchristos     if (FILENAME_CMP (filename, file_buffer->filename) == 0
109429619d2aSchristos         || FILENAME_CMP (filename, file_buffer->fullpath) == 0)
109529619d2aSchristos       {
109629619d2aSchristos         free (file_buffer->filename);
109729619d2aSchristos         free (file_buffer->fullpath);
109829619d2aSchristos 
109929619d2aSchristos         if (file_buffer->contents)
110029619d2aSchristos           free (file_buffer->contents);
110129619d2aSchristos 
110229619d2aSchristos         /* free_file_buffer_tags () also kills the subfiles list, since
110329619d2aSchristos            the subfiles list is only of use in conjunction with tags. */
110429619d2aSchristos         free_file_buffer_tags (file_buffer);
110529619d2aSchristos 
110629619d2aSchristos         /* Move rest of list down.  */
110729619d2aSchristos         while (info_loaded_files[i + 1])
110829619d2aSchristos           {
110929619d2aSchristos             info_loaded_files[i] = info_loaded_files[i + 1];
111029619d2aSchristos             i++;
111129619d2aSchristos           }
111229619d2aSchristos         info_loaded_files[i] = 0;
111329619d2aSchristos 
111429619d2aSchristos         break;
111529619d2aSchristos       }
111629619d2aSchristos }
111729619d2aSchristos 
111829619d2aSchristos /* Free the tags (if any) associated with FILE_BUFFER. */
111929619d2aSchristos static void
free_file_buffer_tags(FILE_BUFFER * file_buffer)112029619d2aSchristos free_file_buffer_tags (FILE_BUFFER *file_buffer)
112129619d2aSchristos {
112229619d2aSchristos   int i;
112329619d2aSchristos 
112429619d2aSchristos   if (file_buffer->tags)
112529619d2aSchristos     {
112629619d2aSchristos       TAG *tag;
112729619d2aSchristos 
112829619d2aSchristos       for (i = 0; (tag = file_buffer->tags[i]); i++)
112929619d2aSchristos         free_info_tag (tag);
113029619d2aSchristos 
113129619d2aSchristos       free (file_buffer->tags);
113229619d2aSchristos       file_buffer->tags = NULL;
113329619d2aSchristos       file_buffer->tags_slots = 0;
113429619d2aSchristos     }
113529619d2aSchristos 
113629619d2aSchristos   if (file_buffer->subfiles)
113729619d2aSchristos     {
113829619d2aSchristos       for (i = 0; file_buffer->subfiles[i]; i++)
113929619d2aSchristos         free (file_buffer->subfiles[i]);
114029619d2aSchristos 
114129619d2aSchristos       free (file_buffer->subfiles);
114229619d2aSchristos       file_buffer->subfiles = NULL;
114329619d2aSchristos     }
114429619d2aSchristos }
114529619d2aSchristos 
114629619d2aSchristos /* Free the data associated with TAG, as well as TAG itself. */
114729619d2aSchristos static void
free_info_tag(TAG * tag)114829619d2aSchristos free_info_tag (TAG *tag)
114929619d2aSchristos {
115029619d2aSchristos   free (tag->nodename);
115129619d2aSchristos 
115229619d2aSchristos   /* We don't free tag->filename, because that filename is part of the
115329619d2aSchristos      subfiles list for the containing FILE_BUFFER.  free_info_tags ()
115429619d2aSchristos      will free the subfiles when it is appropriate. */
115529619d2aSchristos 
115629619d2aSchristos   free (tag);
115729619d2aSchristos }
115829619d2aSchristos 
115929619d2aSchristos /* Load the contents of FILE_BUFFER->contents.  This function is called
116029619d2aSchristos    when a file buffer was loaded, and then in order to conserve memory, the
116129619d2aSchristos    file buffer's contents were freed and the pointer was zero'ed.  Note that
116229619d2aSchristos    the file was already loaded at least once successfully, so the tags and/or
116329619d2aSchristos    nodes members are still correctly filled. */
116429619d2aSchristos static void
info_reload_file_buffer_contents(FILE_BUFFER * fb)116529619d2aSchristos info_reload_file_buffer_contents (FILE_BUFFER *fb)
116629619d2aSchristos {
116729619d2aSchristos   int is_compressed;
116829619d2aSchristos 
116929619d2aSchristos #if defined (HANDLE_MAN_PAGES)
117029619d2aSchristos   /* If this is the magic manpage node, don't try to reload, just give up. */
117129619d2aSchristos   if (fb->flags & N_IsManPage)
117229619d2aSchristos     return;
117329619d2aSchristos #endif
117429619d2aSchristos 
117529619d2aSchristos   fb->flags &= ~N_IsCompressed;
117629619d2aSchristos 
117729619d2aSchristos   /* Let the filesystem do all the work for us. */
117829619d2aSchristos   fb->contents =
117929619d2aSchristos     filesys_read_info_file (fb->fullpath, &(fb->filesize), &(fb->finfo),
118029619d2aSchristos                             &is_compressed);
118129619d2aSchristos   if (is_compressed)
118229619d2aSchristos     fb->flags |= N_IsCompressed;
118329619d2aSchristos }
118429619d2aSchristos 
118529619d2aSchristos /* Return the actual starting memory location of NODE, side-effecting
118629619d2aSchristos    NODE->contents.  MIN and MAX are bounds for a search if one is necessary.
118729619d2aSchristos    Because of the way that tags are implemented, the physical nodestart may
118829619d2aSchristos    not actually be where the tag says it is.  If that is the case, but the
118929619d2aSchristos    node was found anyway, set N_UpdateTags in NODE->flags.  If the node is
119029619d2aSchristos    found, return non-zero.  NODE->contents is returned positioned right after
119129619d2aSchristos    the node separator that precedes this node, while the return value is
119229619d2aSchristos    position directly on the separator that precedes this node.  If the node
119329619d2aSchristos    could not be found, return a NULL pointer. */
119429619d2aSchristos static char *
adjust_nodestart(NODE * node,int min,int max)119529619d2aSchristos adjust_nodestart (NODE *node, int min, int max)
119629619d2aSchristos {
119729619d2aSchristos   long position;
119829619d2aSchristos   SEARCH_BINDING node_body;
119929619d2aSchristos 
120029619d2aSchristos   /* Define the node body. */
120129619d2aSchristos   node_body.buffer = node->contents;
120229619d2aSchristos   node_body.start = 0;
120329619d2aSchristos   node_body.end = max;
120429619d2aSchristos   node_body.flags = 0;
120529619d2aSchristos 
120629619d2aSchristos   /* Try the optimal case first.  Who knows?  This file may actually be
120729619d2aSchristos      formatted (mostly) correctly. */
120829619d2aSchristos   if (node_body.buffer[0] != INFO_COOKIE && min > 2)
120929619d2aSchristos     node_body.buffer -= 3;
121029619d2aSchristos 
121129619d2aSchristos   position = find_node_separator (&node_body);
121229619d2aSchristos 
121329619d2aSchristos   /* If we found a node start, then check it out. */
121429619d2aSchristos   if (position != -1)
121529619d2aSchristos     {
121629619d2aSchristos       int sep_len;
121729619d2aSchristos 
121829619d2aSchristos       sep_len = skip_node_separator (node->contents);
121929619d2aSchristos 
122029619d2aSchristos       /* If we managed to skip a node separator, then check for this node
122129619d2aSchristos          being the right one. */
122229619d2aSchristos       if (sep_len != 0)
122329619d2aSchristos         {
122429619d2aSchristos           char *nodedef, *nodestart;
122529619d2aSchristos           int offset;
122629619d2aSchristos 
122729619d2aSchristos           nodestart = node_body.buffer + position + sep_len;
122829619d2aSchristos           nodedef = nodestart;
122929619d2aSchristos           offset = string_in_line (INFO_NODE_LABEL, nodedef);
123029619d2aSchristos 
123129619d2aSchristos           if (offset != -1)
123229619d2aSchristos             {
123329619d2aSchristos               nodedef += offset;
123429619d2aSchristos               nodedef += skip_whitespace (nodedef);
123529619d2aSchristos               offset = skip_node_characters (nodedef, DONT_SKIP_NEWLINES);
123629619d2aSchristos               if (((unsigned int) offset == strlen (node->nodename)) &&
123729619d2aSchristos                   (strncmp (node->nodename, nodedef, offset) == 0))
123829619d2aSchristos                 {
123929619d2aSchristos                   node->contents = nodestart;
124029619d2aSchristos                   return node_body.buffer + position;
124129619d2aSchristos                 }
124229619d2aSchristos             }
124329619d2aSchristos         }
124429619d2aSchristos     }
124529619d2aSchristos 
124629619d2aSchristos   /* Oh well, I guess we have to try to find it in a larger area. */
124729619d2aSchristos   node_body.buffer = node->contents - min;
124829619d2aSchristos   node_body.start = 0;
124929619d2aSchristos   node_body.end = min + max;
125029619d2aSchristos   node_body.flags = 0;
125129619d2aSchristos 
125229619d2aSchristos   position = find_node_in_binding (node->nodename, &node_body);
125329619d2aSchristos 
125429619d2aSchristos   /* If the node couldn't be found, we lose big. */
125529619d2aSchristos   if (position == -1)
125629619d2aSchristos     return NULL;
125729619d2aSchristos 
125829619d2aSchristos   /* Otherwise, the node was found, but the tags table could need updating
125929619d2aSchristos      (if we used a tag to get here, that is).  Set the flag in NODE->flags. */
126029619d2aSchristos   node->contents = node_body.buffer + position;
126129619d2aSchristos   node->contents += skip_node_separator (node->contents);
126229619d2aSchristos   if (node->flags & N_HasTagsTable)
126329619d2aSchristos     node->flags |= N_UpdateTags;
126429619d2aSchristos   return node_body.buffer + position;
126529619d2aSchristos }
1266