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