11cc83814Sespie /* nodes.c -- how to get an Info file and node.
2*a1acfa9bSespie $Id: nodes.c,v 1.1.1.5 2006/07/17 16:03:43 espie Exp $
3fbc94a17Sniklas
4*a1acfa9bSespie Copyright (C) 1993, 1998, 1999, 2000, 2002, 2003, 2004 Free Software
5*a1acfa9bSespie Foundation, Inc.
6fbc94a17Sniklas
7fbc94a17Sniklas This program is free software; you can redistribute it and/or modify
8fbc94a17Sniklas it under the terms of the GNU General Public License as published by
9fbc94a17Sniklas the Free Software Foundation; either version 2, or (at your option)
10fbc94a17Sniklas any later version.
11fbc94a17Sniklas
12fbc94a17Sniklas This program is distributed in the hope that it will be useful,
13fbc94a17Sniklas but WITHOUT ANY WARRANTY; without even the implied warranty of
14fbc94a17Sniklas MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15fbc94a17Sniklas GNU General Public License for more details.
16fbc94a17Sniklas
17fbc94a17Sniklas You should have received a copy of the GNU General Public License
18fbc94a17Sniklas along with this program; if not, write to the Free Software
19fbc94a17Sniklas Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20fbc94a17Sniklas
21*a1acfa9bSespie Originally written by Brian Fox (bfox@ai.mit.edu). */
22fbc94a17Sniklas
23840175f0Skstailey #include "info.h"
24840175f0Skstailey
25fbc94a17Sniklas #include "nodes.h"
26fbc94a17Sniklas #include "search.h"
27fbc94a17Sniklas #include "filesys.h"
28fbc94a17Sniklas #include "info-utils.h"
29fbc94a17Sniklas
30fbc94a17Sniklas #if defined (HANDLE_MAN_PAGES)
31fbc94a17Sniklas # include "man.h"
32fbc94a17Sniklas #endif /* HANDLE_MAN_PAGES */
33fbc94a17Sniklas
34*a1acfa9bSespie static void forget_info_file (char *filename);
35*a1acfa9bSespie static void remember_info_file (FILE_BUFFER *file_buffer);
36*a1acfa9bSespie static void free_file_buffer_tags (FILE_BUFFER *file_buffer);
37*a1acfa9bSespie static void free_info_tag (TAG *tag);
38*a1acfa9bSespie static void get_nodes_of_tags_table (FILE_BUFFER *file_buffer,
39*a1acfa9bSespie SEARCH_BINDING *buffer_binding);
40*a1acfa9bSespie static void get_nodes_of_info_file (FILE_BUFFER *file_buffer);
41*a1acfa9bSespie static void get_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer,
42*a1acfa9bSespie SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding);
43*a1acfa9bSespie static void info_reload_file_buffer_contents (FILE_BUFFER *fb);
44*a1acfa9bSespie static char *adjust_nodestart (NODE *node, int min, int max);
45*a1acfa9bSespie static FILE_BUFFER *info_load_file_internal (char *filename, int get_tags);
46*a1acfa9bSespie static FILE_BUFFER *info_find_file_internal (char *filename, int get_tags);
47*a1acfa9bSespie static NODE *info_node_of_file_buffer_tags (FILE_BUFFER *file_buffer,
48*a1acfa9bSespie char *nodename);
49fbc94a17Sniklas
50*a1acfa9bSespie static long get_node_length (SEARCH_BINDING *binding);
51fbc94a17Sniklas
52fbc94a17Sniklas /* Magic number that RMS used to decide how much a tags table pointer could
531cc83814Sespie be off by. I feel that it should be much smaller, like 4. */
54fbc94a17Sniklas #define DEFAULT_INFO_FUDGE 1000
55fbc94a17Sniklas
56fbc94a17Sniklas /* Passed to *_internal functions. INFO_GET_TAGS says to do what is
57fbc94a17Sniklas neccessary to fill in the nodes or tags arrays in FILE_BUFFER. */
58fbc94a17Sniklas #define INFO_NO_TAGS 0
59fbc94a17Sniklas #define INFO_GET_TAGS 1
60fbc94a17Sniklas
611cc83814Sespie /* Global variables. */
62fbc94a17Sniklas
63fbc94a17Sniklas /* When non-zero, this is a string describing the recent file error. */
641cc83814Sespie char *info_recent_file_error = NULL;
65fbc94a17Sniklas
66fbc94a17Sniklas /* The list of already loaded nodes. */
671cc83814Sespie FILE_BUFFER **info_loaded_files = NULL;
68fbc94a17Sniklas
69fbc94a17Sniklas /* The number of slots currently allocated to LOADED_FILES. */
70fbc94a17Sniklas int info_loaded_files_slots = 0;
71fbc94a17Sniklas
721cc83814Sespie /* Public functions for node manipulation. */
73fbc94a17Sniklas
741cc83814Sespie /* Used to build `dir' menu from `localdir' files found in INFOPATH. */
75*a1acfa9bSespie extern void maybe_build_dir_node (char *dirname);
76fbc94a17Sniklas
77fbc94a17Sniklas /* Return a pointer to a NODE structure for the Info node (FILENAME)NODENAME.
781cc83814Sespie If FILENAME is NULL, `dir' is used.
791cc83814Sespie IF NODENAME is NULL, `Top' is used.
801cc83814Sespie If the node cannot be found, return NULL. */
81fbc94a17Sniklas NODE *
info_get_node(char * filename,char * nodename)82*a1acfa9bSespie info_get_node (char *filename, char *nodename)
83fbc94a17Sniklas {
84fbc94a17Sniklas NODE *node;
851cc83814Sespie FILE_BUFFER *file_buffer = NULL;
86fbc94a17Sniklas
871cc83814Sespie info_recent_file_error = NULL;
88fbc94a17Sniklas info_parse_node (nodename, DONT_SKIP_NEWLINES);
891cc83814Sespie nodename = NULL;
90fbc94a17Sniklas
91fbc94a17Sniklas if (info_parsed_filename)
92fbc94a17Sniklas filename = info_parsed_filename;
93fbc94a17Sniklas
94fbc94a17Sniklas if (info_parsed_nodename)
95fbc94a17Sniklas nodename = info_parsed_nodename;
96fbc94a17Sniklas
97fbc94a17Sniklas /* If FILENAME is not specified, it defaults to "dir". */
98fbc94a17Sniklas if (!filename)
99fbc94a17Sniklas filename = "dir";
100fbc94a17Sniklas
101fbc94a17Sniklas /* If the file to be looked up is "dir", build the contents from all of
102fbc94a17Sniklas the "dir"s and "localdir"s found in INFOPATH. */
1031cc83814Sespie if (is_dir_name (filename))
104fbc94a17Sniklas maybe_build_dir_node (filename);
105fbc94a17Sniklas
1061cc83814Sespie /* Find the correct info file, or give up. */
107fbc94a17Sniklas file_buffer = info_find_file (filename);
108fbc94a17Sniklas if (!file_buffer)
109fbc94a17Sniklas {
110fbc94a17Sniklas if (filesys_error_number)
111fbc94a17Sniklas info_recent_file_error =
112fbc94a17Sniklas filesys_error_string (filename, filesys_error_number);
1131cc83814Sespie return NULL;
114fbc94a17Sniklas }
115fbc94a17Sniklas
1161cc83814Sespie /* Look for the node. */
117fbc94a17Sniklas node = info_get_node_of_file_buffer (nodename, file_buffer);
1181cc83814Sespie
1191cc83814Sespie /* If the node not found was "Top", try again with different case. */
120fbc94a17Sniklas if (!node && (nodename == NULL || strcasecmp (nodename, "Top") == 0))
121fbc94a17Sniklas {
122fbc94a17Sniklas node = info_get_node_of_file_buffer ("Top", file_buffer);
123fbc94a17Sniklas if (!node)
124fbc94a17Sniklas node = info_get_node_of_file_buffer ("top", file_buffer);
125fbc94a17Sniklas if (!node)
126fbc94a17Sniklas node = info_get_node_of_file_buffer ("TOP", file_buffer);
127fbc94a17Sniklas }
1281cc83814Sespie
1291cc83814Sespie return node;
130fbc94a17Sniklas }
131fbc94a17Sniklas
132fbc94a17Sniklas /* Return a pointer to a NODE structure for the Info node NODENAME in
133fbc94a17Sniklas FILE_BUFFER. NODENAME can be passed as NULL, in which case the
134fbc94a17Sniklas nodename of "Top" is used. If the node cannot be found, return a
135fbc94a17Sniklas NULL pointer. */
136fbc94a17Sniklas NODE *
info_get_node_of_file_buffer(char * nodename,FILE_BUFFER * file_buffer)137*a1acfa9bSespie info_get_node_of_file_buffer (char *nodename, FILE_BUFFER *file_buffer)
138fbc94a17Sniklas {
1391cc83814Sespie NODE *node = NULL;
140fbc94a17Sniklas
141fbc94a17Sniklas /* If we are unable to find the file, we have to give up. There isn't
142fbc94a17Sniklas anything else we can do. */
143fbc94a17Sniklas if (!file_buffer)
1441cc83814Sespie return NULL;
145fbc94a17Sniklas
146fbc94a17Sniklas /* If the file buffer was gc'ed, reload the contents now. */
147fbc94a17Sniklas if (!file_buffer->contents)
148fbc94a17Sniklas info_reload_file_buffer_contents (file_buffer);
149fbc94a17Sniklas
150fbc94a17Sniklas /* If NODENAME is not specified, it defaults to "Top". */
151fbc94a17Sniklas if (!nodename)
152fbc94a17Sniklas nodename = "Top";
153fbc94a17Sniklas
154fbc94a17Sniklas /* If the name of the node that we wish to find is exactly "*", then the
155fbc94a17Sniklas node body is the contents of the entire file. Create and return such
156fbc94a17Sniklas a node. */
157fbc94a17Sniklas if (strcmp (nodename, "*") == 0)
158fbc94a17Sniklas {
159fbc94a17Sniklas node = (NODE *)xmalloc (sizeof (NODE));
160fbc94a17Sniklas node->filename = file_buffer->fullpath;
1611cc83814Sespie node->parent = NULL;
162840175f0Skstailey node->nodename = xstrdup ("*");
163fbc94a17Sniklas node->contents = file_buffer->contents;
164fbc94a17Sniklas node->nodelen = file_buffer->filesize;
165fbc94a17Sniklas node->flags = 0;
1661cc83814Sespie node->display_pos = 0;
167fbc94a17Sniklas }
168fbc94a17Sniklas #if defined (HANDLE_MAN_PAGES)
169fbc94a17Sniklas /* If the file buffer is the magic one associated with manpages, call
170fbc94a17Sniklas the manpage node finding function instead. */
171fbc94a17Sniklas else if (file_buffer->flags & N_IsManPage)
172fbc94a17Sniklas {
173fbc94a17Sniklas node = get_manpage_node (file_buffer, nodename);
174fbc94a17Sniklas }
175fbc94a17Sniklas #endif /* HANDLE_MAN_PAGES */
176fbc94a17Sniklas /* If this is the "main" info file, it might contain a tags table. Search
177fbc94a17Sniklas the tags table for an entry which matches the node that we want. If
178fbc94a17Sniklas there is a tags table, get the file which contains this node, but don't
179fbc94a17Sniklas bother building a node list for it. */
180fbc94a17Sniklas else if (file_buffer->tags)
181fbc94a17Sniklas {
182fbc94a17Sniklas node = info_node_of_file_buffer_tags (file_buffer, nodename);
183fbc94a17Sniklas }
184fbc94a17Sniklas
185fbc94a17Sniklas /* Return the results of our node search. */
1861cc83814Sespie return node;
187fbc94a17Sniklas }
188fbc94a17Sniklas
189fbc94a17Sniklas /* Locate the file named by FILENAME, and return the information structure
190fbc94a17Sniklas describing this file. The file may appear in our list of loaded files
191fbc94a17Sniklas already, or it may not. If it does not already appear, find the file,
192fbc94a17Sniklas and add it to the list of loaded files. If the file cannot be found,
193fbc94a17Sniklas return a NULL FILE_BUFFER *. */
194fbc94a17Sniklas FILE_BUFFER *
info_find_file(char * filename)195*a1acfa9bSespie info_find_file (char *filename)
196fbc94a17Sniklas {
1971cc83814Sespie return info_find_file_internal (filename, INFO_GET_TAGS);
198fbc94a17Sniklas }
199fbc94a17Sniklas
200fbc94a17Sniklas /* Load the info file FILENAME, remembering information about it in a
201fbc94a17Sniklas file buffer. */
202fbc94a17Sniklas FILE_BUFFER *
info_load_file(char * filename)203*a1acfa9bSespie info_load_file (char *filename)
204fbc94a17Sniklas {
2051cc83814Sespie return info_load_file_internal (filename, INFO_GET_TAGS);
206fbc94a17Sniklas }
207fbc94a17Sniklas
208fbc94a17Sniklas
2091cc83814Sespie /* Private functions implementation. */
210fbc94a17Sniklas
211fbc94a17Sniklas /* The workhorse for info_find_file (). Non-zero 2nd argument says to
212fbc94a17Sniklas try to build a tags table (or otherwise glean the nodes) for this
213fbc94a17Sniklas file once found. By default, we build the tags table, but when this
214fbc94a17Sniklas function is called by info_get_node () when we already have a valid
215fbc94a17Sniklas tags table describing the nodes, it is unnecessary. */
216fbc94a17Sniklas static FILE_BUFFER *
info_find_file_internal(char * filename,int get_tags)217*a1acfa9bSespie info_find_file_internal (char *filename, int get_tags)
218fbc94a17Sniklas {
2191cc83814Sespie int i;
2201cc83814Sespie FILE_BUFFER *file_buffer;
221fbc94a17Sniklas
222fbc94a17Sniklas /* First try to find the file in our list of already loaded files. */
223fbc94a17Sniklas if (info_loaded_files)
224fbc94a17Sniklas {
225840175f0Skstailey for (i = 0; (file_buffer = info_loaded_files[i]); i++)
2263fb98d4aSespie if ((FILENAME_CMP (filename, file_buffer->filename) == 0)
2273fb98d4aSespie || (FILENAME_CMP (filename, file_buffer->fullpath) == 0)
2283fb98d4aSespie || (!IS_ABSOLUTE (filename)
2293fb98d4aSespie && FILENAME_CMP (filename,
2303fb98d4aSespie filename_non_directory (file_buffer->fullpath))
2313fb98d4aSespie == 0))
232fbc94a17Sniklas {
233fbc94a17Sniklas struct stat new_info, *old_info;
234fbc94a17Sniklas
235fbc94a17Sniklas /* This file is loaded. If the filename that we want is
236fbc94a17Sniklas specifically "dir", then simply return the file buffer. */
2371cc83814Sespie if (is_dir_name (filename_non_directory (filename)))
2381cc83814Sespie return file_buffer;
239fbc94a17Sniklas
240fbc94a17Sniklas #if defined (HANDLE_MAN_PAGES)
241fbc94a17Sniklas /* Do the same for the magic MANPAGE file. */
242fbc94a17Sniklas if (file_buffer->flags & N_IsManPage)
2431cc83814Sespie return file_buffer;
244fbc94a17Sniklas #endif /* HANDLE_MAN_PAGES */
245fbc94a17Sniklas
2463fb98d4aSespie /* The file appears to be already loaded, and is not "dir". Check
2473fb98d4aSespie to see if it's changed since the last time it was loaded. */
248fbc94a17Sniklas if (stat (file_buffer->fullpath, &new_info) == -1)
249fbc94a17Sniklas {
250fbc94a17Sniklas filesys_error_number = errno;
2511cc83814Sespie return NULL;
252fbc94a17Sniklas }
253fbc94a17Sniklas
254fbc94a17Sniklas old_info = &file_buffer->finfo;
255fbc94a17Sniklas
2563fb98d4aSespie if (new_info.st_size != old_info->st_size
2573fb98d4aSespie || new_info.st_mtime != old_info->st_mtime)
258fbc94a17Sniklas {
259fbc94a17Sniklas /* The file has changed. Forget that we ever had loaded it
260fbc94a17Sniklas in the first place. */
261fbc94a17Sniklas forget_info_file (filename);
262fbc94a17Sniklas break;
263fbc94a17Sniklas }
264fbc94a17Sniklas else
265fbc94a17Sniklas {
266fbc94a17Sniklas /* The info file exists, and has not changed since the last
267fbc94a17Sniklas time it was loaded. If the caller requested a nodes list
268fbc94a17Sniklas for this file, and there isn't one here, build the nodes
269fbc94a17Sniklas for this file_buffer. In any case, return the file_buffer
270fbc94a17Sniklas object. */
2711cc83814Sespie if (!file_buffer->contents)
2721cc83814Sespie {
2731cc83814Sespie /* The file's contents have been gc'ed. Reload it. */
2741cc83814Sespie info_reload_file_buffer_contents (file_buffer);
2751cc83814Sespie if (!file_buffer->contents)
2761cc83814Sespie return NULL;
2771cc83814Sespie }
2781cc83814Sespie
279fbc94a17Sniklas if (get_tags && !file_buffer->tags)
280fbc94a17Sniklas build_tags_and_nodes (file_buffer);
281fbc94a17Sniklas
2821cc83814Sespie return file_buffer;
283fbc94a17Sniklas }
284fbc94a17Sniklas }
285fbc94a17Sniklas }
286fbc94a17Sniklas
287fbc94a17Sniklas /* The file wasn't loaded. Try to load it now. */
288fbc94a17Sniklas #if defined (HANDLE_MAN_PAGES)
289fbc94a17Sniklas /* If the name of the file that we want is our special file buffer for
290fbc94a17Sniklas Unix manual pages, then create the file buffer, and return it now. */
291fbc94a17Sniklas if (strcasecmp (filename, MANPAGE_FILE_BUFFER_NAME) == 0)
292fbc94a17Sniklas file_buffer = create_manpage_file_buffer ();
293fbc94a17Sniklas else
294fbc94a17Sniklas #endif /* HANDLE_MAN_PAGES */
295fbc94a17Sniklas file_buffer = info_load_file_internal (filename, get_tags);
296fbc94a17Sniklas
297fbc94a17Sniklas /* If the file was loaded, remember the name under which it was found. */
298fbc94a17Sniklas if (file_buffer)
299fbc94a17Sniklas remember_info_file (file_buffer);
300fbc94a17Sniklas
3011cc83814Sespie return file_buffer;
302fbc94a17Sniklas }
303fbc94a17Sniklas
304fbc94a17Sniklas /* The workhorse function for info_load_file (). Non-zero second argument
305fbc94a17Sniklas says to build a list of tags (or nodes) for this file. This is the
306fbc94a17Sniklas default behaviour when info_load_file () is called, but it is not
307fbc94a17Sniklas necessary when loading a subfile for which we already have tags. */
308fbc94a17Sniklas static FILE_BUFFER *
info_load_file_internal(char * filename,int get_tags)309*a1acfa9bSespie info_load_file_internal (char *filename, int get_tags)
310fbc94a17Sniklas {
311fbc94a17Sniklas char *fullpath, *contents;
312fbc94a17Sniklas long filesize;
313fbc94a17Sniklas struct stat finfo;
3141cc83814Sespie int retcode, compressed;
3151cc83814Sespie FILE_BUFFER *file_buffer = NULL;
316fbc94a17Sniklas
317fbc94a17Sniklas /* Get the full pathname of this file, as known by the info system.
318fbc94a17Sniklas That is to say, search along INFOPATH and expand tildes, etc. */
319fbc94a17Sniklas fullpath = info_find_fullpath (filename);
320fbc94a17Sniklas
321fbc94a17Sniklas /* Did we actually find the file? */
322fbc94a17Sniklas retcode = stat (fullpath, &finfo);
323fbc94a17Sniklas
324fbc94a17Sniklas /* If the file referenced by the name returned from info_find_fullpath ()
325fbc94a17Sniklas doesn't exist, then try again with the last part of the filename
326fbc94a17Sniklas appearing in lowercase. */
3271cc83814Sespie /* This is probably not needed at all on those systems which define
3281cc83814Sespie FILENAME_CMP to be strcasecmp. But let's do it anyway, lest some
3291cc83814Sespie network redirector supports case sensitivity. */
330fbc94a17Sniklas if (retcode < 0)
331fbc94a17Sniklas {
332fbc94a17Sniklas char *lowered_name;
333*a1acfa9bSespie char *tmp_basename;
334fbc94a17Sniklas
335840175f0Skstailey lowered_name = xstrdup (filename);
336*a1acfa9bSespie tmp_basename = filename_non_directory (lowered_name);
337fbc94a17Sniklas
338*a1acfa9bSespie while (*tmp_basename)
339fbc94a17Sniklas {
340*a1acfa9bSespie if (isupper (*tmp_basename))
341*a1acfa9bSespie *tmp_basename = tolower (*tmp_basename);
342fbc94a17Sniklas
343*a1acfa9bSespie tmp_basename++;
344fbc94a17Sniklas }
345fbc94a17Sniklas
346fbc94a17Sniklas fullpath = info_find_fullpath (lowered_name);
347fbc94a17Sniklas
348fbc94a17Sniklas retcode = stat (fullpath, &finfo);
349*a1acfa9bSespie free (lowered_name);
350fbc94a17Sniklas }
351fbc94a17Sniklas
352fbc94a17Sniklas /* If the file wasn't found, give up, returning a NULL pointer. */
353fbc94a17Sniklas if (retcode < 0)
354fbc94a17Sniklas {
355fbc94a17Sniklas filesys_error_number = errno;
3561cc83814Sespie return NULL;
357fbc94a17Sniklas }
358fbc94a17Sniklas
359fbc94a17Sniklas /* Otherwise, try to load the file. */
3601cc83814Sespie contents = filesys_read_info_file (fullpath, &filesize, &finfo, &compressed);
361fbc94a17Sniklas
362fbc94a17Sniklas if (!contents)
3631cc83814Sespie return NULL;
364fbc94a17Sniklas
365fbc94a17Sniklas /* The file was found, and can be read. Allocate FILE_BUFFER and fill
366fbc94a17Sniklas in the various members. */
367fbc94a17Sniklas file_buffer = make_file_buffer ();
368840175f0Skstailey file_buffer->filename = xstrdup (filename);
369840175f0Skstailey file_buffer->fullpath = xstrdup (fullpath);
370fbc94a17Sniklas file_buffer->finfo = finfo;
371fbc94a17Sniklas file_buffer->filesize = filesize;
372fbc94a17Sniklas file_buffer->contents = contents;
3731cc83814Sespie if (compressed)
374fbc94a17Sniklas file_buffer->flags |= N_IsCompressed;
375fbc94a17Sniklas
376fbc94a17Sniklas /* If requested, build the tags and nodes for this file buffer. */
377fbc94a17Sniklas if (get_tags)
378fbc94a17Sniklas build_tags_and_nodes (file_buffer);
379fbc94a17Sniklas
3801cc83814Sespie return file_buffer;
381fbc94a17Sniklas }
3821cc83814Sespie
383fbc94a17Sniklas /* Grovel FILE_BUFFER->contents finding tags and nodes, and filling in the
384fbc94a17Sniklas various slots. This can also be used to rebuild a tag or node table. */
385fbc94a17Sniklas void
build_tags_and_nodes(FILE_BUFFER * file_buffer)386*a1acfa9bSespie build_tags_and_nodes (FILE_BUFFER *file_buffer)
387fbc94a17Sniklas {
388fbc94a17Sniklas SEARCH_BINDING binding;
389fbc94a17Sniklas long position;
390fbc94a17Sniklas
391fbc94a17Sniklas free_file_buffer_tags (file_buffer);
392fbc94a17Sniklas file_buffer->flags &= ~N_HasTagsTable;
393fbc94a17Sniklas
394fbc94a17Sniklas /* See if there is a tags table in this info file. */
395fbc94a17Sniklas binding.buffer = file_buffer->contents;
396fbc94a17Sniklas binding.start = file_buffer->filesize;
397fbc94a17Sniklas binding.end = binding.start - 1000;
398fbc94a17Sniklas if (binding.end < 0)
399fbc94a17Sniklas binding.end = 0;
400fbc94a17Sniklas binding.flags = S_FoldCase;
401fbc94a17Sniklas
402fbc94a17Sniklas position = search_backward (TAGS_TABLE_END_LABEL, &binding);
403fbc94a17Sniklas
404fbc94a17Sniklas /* If there is a tag table, find the start of it, and grovel over it
405fbc94a17Sniklas extracting tag information. */
406fbc94a17Sniklas if (position != -1)
407fbc94a17Sniklas while (1)
408fbc94a17Sniklas {
409fbc94a17Sniklas long tags_table_begin, tags_table_end;
410fbc94a17Sniklas
411fbc94a17Sniklas binding.end = position;
412fbc94a17Sniklas binding.start = binding.end - 5 - strlen (TAGS_TABLE_END_LABEL);
413fbc94a17Sniklas if (binding.start < 0)
414fbc94a17Sniklas binding.start = 0;
415fbc94a17Sniklas
416fbc94a17Sniklas position = find_node_separator (&binding);
417fbc94a17Sniklas
418fbc94a17Sniklas /* For this test, (and all others here) failure indicates a bogus
419fbc94a17Sniklas tags table. Grovel the file. */
420fbc94a17Sniklas if (position == -1)
421fbc94a17Sniklas break;
422fbc94a17Sniklas
423fbc94a17Sniklas /* Remember the end of the tags table. */
424fbc94a17Sniklas binding.start = position;
425fbc94a17Sniklas tags_table_end = binding.start;
426fbc94a17Sniklas binding.end = 0;
427fbc94a17Sniklas
428fbc94a17Sniklas /* Locate the start of the tags table. */
429fbc94a17Sniklas position = search_backward (TAGS_TABLE_BEG_LABEL, &binding);
430fbc94a17Sniklas
431fbc94a17Sniklas if (position == -1)
432fbc94a17Sniklas break;
433fbc94a17Sniklas
434fbc94a17Sniklas binding.end = position;
435fbc94a17Sniklas binding.start = binding.end - 5 - strlen (TAGS_TABLE_BEG_LABEL);
436fbc94a17Sniklas position = find_node_separator (&binding);
437fbc94a17Sniklas
438fbc94a17Sniklas if (position == -1)
439fbc94a17Sniklas break;
440fbc94a17Sniklas
441fbc94a17Sniklas /* The file contains a valid tags table. Fill the FILE_BUFFER's
442fbc94a17Sniklas tags member. */
443fbc94a17Sniklas file_buffer->flags |= N_HasTagsTable;
444fbc94a17Sniklas tags_table_begin = position;
445fbc94a17Sniklas
446fbc94a17Sniklas /* If this isn't an indirect tags table, just remember the nodes
447fbc94a17Sniklas described locally in this tags table. Note that binding.end
448fbc94a17Sniklas is pointing to just after the beginning label. */
449fbc94a17Sniklas binding.start = binding.end;
450fbc94a17Sniklas binding.end = file_buffer->filesize;
451fbc94a17Sniklas
452fbc94a17Sniklas if (!looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, &binding))
453fbc94a17Sniklas {
454fbc94a17Sniklas binding.start = tags_table_begin;
455fbc94a17Sniklas binding.end = tags_table_end;
456fbc94a17Sniklas get_nodes_of_tags_table (file_buffer, &binding);
457fbc94a17Sniklas return;
458fbc94a17Sniklas }
459fbc94a17Sniklas else
460fbc94a17Sniklas {
461fbc94a17Sniklas /* This is an indirect tags table. Build TAGS member. */
462fbc94a17Sniklas SEARCH_BINDING indirect;
463fbc94a17Sniklas
464fbc94a17Sniklas indirect.start = tags_table_begin;
465fbc94a17Sniklas indirect.end = 0;
466fbc94a17Sniklas indirect.buffer = binding.buffer;
467fbc94a17Sniklas indirect.flags = S_FoldCase;
468fbc94a17Sniklas
469fbc94a17Sniklas position = search_backward (INDIRECT_TAGS_TABLE_LABEL, &indirect);
470fbc94a17Sniklas
471fbc94a17Sniklas if (position == -1)
472fbc94a17Sniklas {
473fbc94a17Sniklas /* This file is malformed. Give up. */
474fbc94a17Sniklas return;
475fbc94a17Sniklas }
476fbc94a17Sniklas
477fbc94a17Sniklas indirect.start = position;
478fbc94a17Sniklas indirect.end = tags_table_begin;
479fbc94a17Sniklas binding.start = tags_table_begin;
480fbc94a17Sniklas binding.end = tags_table_end;
481fbc94a17Sniklas get_tags_of_indirect_tags_table (file_buffer, &indirect, &binding);
482fbc94a17Sniklas return;
483fbc94a17Sniklas }
484fbc94a17Sniklas }
485fbc94a17Sniklas
486fbc94a17Sniklas /* This file doesn't contain any kind of tags table. Grovel the
487fbc94a17Sniklas file and build node entries for it. */
488fbc94a17Sniklas get_nodes_of_info_file (file_buffer);
489fbc94a17Sniklas }
490fbc94a17Sniklas
491fbc94a17Sniklas /* Search through FILE_BUFFER->contents building an array of TAG *,
492fbc94a17Sniklas one entry per each node present in the file. Store the tags in
493fbc94a17Sniklas FILE_BUFFER->tags, and the number of allocated slots in
494fbc94a17Sniklas FILE_BUFFER->tags_slots. */
495fbc94a17Sniklas static void
get_nodes_of_info_file(FILE_BUFFER * file_buffer)496*a1acfa9bSespie get_nodes_of_info_file (FILE_BUFFER *file_buffer)
497fbc94a17Sniklas {
498fbc94a17Sniklas long nodestart;
499fbc94a17Sniklas int tags_index = 0;
500fbc94a17Sniklas SEARCH_BINDING binding;
501fbc94a17Sniklas
502fbc94a17Sniklas binding.buffer = file_buffer->contents;
503fbc94a17Sniklas binding.start = 0;
504fbc94a17Sniklas binding.end = file_buffer->filesize;
505fbc94a17Sniklas binding.flags = S_FoldCase;
506fbc94a17Sniklas
507fbc94a17Sniklas while ((nodestart = find_node_separator (&binding)) != -1)
508fbc94a17Sniklas {
509fbc94a17Sniklas int start, end;
510fbc94a17Sniklas char *nodeline;
511fbc94a17Sniklas TAG *entry;
5121cc83814Sespie int anchor = 0;
513fbc94a17Sniklas
514fbc94a17Sniklas /* Skip past the characters just found. */
515fbc94a17Sniklas binding.start = nodestart;
516fbc94a17Sniklas binding.start += skip_node_separator (binding.buffer + binding.start);
517fbc94a17Sniklas
518fbc94a17Sniklas /* Move to the start of the line defining the node. */
519fbc94a17Sniklas nodeline = binding.buffer + binding.start;
520fbc94a17Sniklas
521fbc94a17Sniklas /* Find "Node:" */
522fbc94a17Sniklas start = string_in_line (INFO_NODE_LABEL, nodeline);
5231cc83814Sespie /* No Node:. Maybe it's a Ref:. */
5241cc83814Sespie if (start == -1)
5251cc83814Sespie {
5261cc83814Sespie start = string_in_line (INFO_REF_LABEL, nodeline);
5271cc83814Sespie if (start != -1)
5281cc83814Sespie anchor = 1;
5291cc83814Sespie }
530fbc94a17Sniklas
531fbc94a17Sniklas /* If not there, this is not the start of a node. */
532fbc94a17Sniklas if (start == -1)
533fbc94a17Sniklas continue;
534fbc94a17Sniklas
535fbc94a17Sniklas /* Find the start of the nodename. */
536fbc94a17Sniklas start += skip_whitespace (nodeline + start);
537fbc94a17Sniklas
538fbc94a17Sniklas /* Find the end of the nodename. */
539fbc94a17Sniklas end = start +
540fbc94a17Sniklas skip_node_characters (nodeline + start, DONT_SKIP_NEWLINES);
541fbc94a17Sniklas
542fbc94a17Sniklas /* Okay, we have isolated the node name, and we know where the
5431cc83814Sespie node starts. Remember this information. */
5441cc83814Sespie entry = xmalloc (sizeof (TAG));
5451cc83814Sespie entry->nodename = xmalloc (1 + (end - start));
546fbc94a17Sniklas strncpy (entry->nodename, nodeline + start, end - start);
5471cc83814Sespie entry->nodename[end - start] = 0;
548fbc94a17Sniklas entry->nodestart = nodestart;
5491cc83814Sespie if (anchor)
5501cc83814Sespie entry->nodelen = 0;
5511cc83814Sespie else
552fbc94a17Sniklas {
553fbc94a17Sniklas SEARCH_BINDING node_body;
554fbc94a17Sniklas
555fbc94a17Sniklas node_body.buffer = binding.buffer + binding.start;
556fbc94a17Sniklas node_body.start = 0;
557fbc94a17Sniklas node_body.end = binding.end - binding.start;
558fbc94a17Sniklas node_body.flags = S_FoldCase;
559fbc94a17Sniklas entry->nodelen = get_node_length (&node_body);
560fbc94a17Sniklas }
561fbc94a17Sniklas
562fbc94a17Sniklas entry->filename = file_buffer->fullpath;
563fbc94a17Sniklas
564fbc94a17Sniklas /* Add this tag to the array of tag structures in this FILE_BUFFER. */
565fbc94a17Sniklas add_pointer_to_array (entry, tags_index, file_buffer->tags,
566fbc94a17Sniklas file_buffer->tags_slots, 100, TAG *);
567fbc94a17Sniklas }
568fbc94a17Sniklas }
569fbc94a17Sniklas
570fbc94a17Sniklas /* Return the length of the node which starts at BINDING. */
571fbc94a17Sniklas static long
get_node_length(SEARCH_BINDING * binding)572*a1acfa9bSespie get_node_length (SEARCH_BINDING *binding)
573fbc94a17Sniklas {
5741cc83814Sespie int i;
575fbc94a17Sniklas char *body;
576fbc94a17Sniklas
5771cc83814Sespie /* [A node] ends with either a ^_, a ^L, or end of file. */
578fbc94a17Sniklas for (i = binding->start, body = binding->buffer; i < binding->end; i++)
579fbc94a17Sniklas {
580fbc94a17Sniklas if (body[i] == INFO_FF || body[i] == INFO_COOKIE)
581fbc94a17Sniklas break;
582fbc94a17Sniklas }
5831cc83814Sespie return i - binding->start;
584fbc94a17Sniklas }
585fbc94a17Sniklas
586fbc94a17Sniklas /* Build and save the array of nodes in FILE_BUFFER by searching through the
587fbc94a17Sniklas contents of BUFFER_BINDING for a tags table, and groveling the contents. */
588fbc94a17Sniklas static void
get_nodes_of_tags_table(FILE_BUFFER * file_buffer,SEARCH_BINDING * buffer_binding)589*a1acfa9bSespie get_nodes_of_tags_table (FILE_BUFFER *file_buffer,
590*a1acfa9bSespie SEARCH_BINDING *buffer_binding)
591fbc94a17Sniklas {
5921cc83814Sespie int name_offset;
593*a1acfa9bSespie SEARCH_BINDING *tmp_search;
594fbc94a17Sniklas long position;
5951cc83814Sespie int tags_index = 0;
596fbc94a17Sniklas
597*a1acfa9bSespie tmp_search = copy_binding (buffer_binding);
598fbc94a17Sniklas
599fbc94a17Sniklas /* Find the start of the tags table. */
600*a1acfa9bSespie position = find_tags_table (tmp_search);
601fbc94a17Sniklas
602fbc94a17Sniklas /* If none, we're all done. */
603fbc94a17Sniklas if (position == -1)
604fbc94a17Sniklas return;
605fbc94a17Sniklas
606fbc94a17Sniklas /* Move to one character before the start of the actual table. */
607*a1acfa9bSespie tmp_search->start = position;
608*a1acfa9bSespie tmp_search->start += skip_node_separator
609*a1acfa9bSespie (tmp_search->buffer + tmp_search->start);
610*a1acfa9bSespie tmp_search->start += strlen (TAGS_TABLE_BEG_LABEL);
611*a1acfa9bSespie tmp_search->start--;
612fbc94a17Sniklas
613fbc94a17Sniklas /* The tag table consists of lines containing node names and positions.
614fbc94a17Sniklas Do each line until we find one that doesn't contain a node name. */
615*a1acfa9bSespie while ((position = search_forward ("\n", tmp_search)) != -1)
616fbc94a17Sniklas {
617fbc94a17Sniklas TAG *entry;
618fbc94a17Sniklas char *nodedef;
6191cc83814Sespie unsigned p;
6201cc83814Sespie int anchor = 0;
621fbc94a17Sniklas
622fbc94a17Sniklas /* Prepare to skip this line. */
623*a1acfa9bSespie tmp_search->start = position;
624*a1acfa9bSespie tmp_search->start++;
625fbc94a17Sniklas
626fbc94a17Sniklas /* Skip past informative "(Indirect)" tags table line. */
627*a1acfa9bSespie if (!tags_index && looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, tmp_search))
628fbc94a17Sniklas continue;
629fbc94a17Sniklas
630fbc94a17Sniklas /* Find the label preceding the node name. */
6311cc83814Sespie name_offset =
632*a1acfa9bSespie string_in_line (INFO_NODE_LABEL, tmp_search->buffer + tmp_search->start);
633fbc94a17Sniklas
6341cc83814Sespie /* If no node label, maybe it's an anchor. */
6351cc83814Sespie if (name_offset == -1)
6361cc83814Sespie {
6371cc83814Sespie name_offset = string_in_line (INFO_REF_LABEL,
638*a1acfa9bSespie tmp_search->buffer + tmp_search->start);
6391cc83814Sespie if (name_offset != -1)
6401cc83814Sespie anchor = 1;
6411cc83814Sespie }
6421cc83814Sespie
643fbc94a17Sniklas /* If not there, not a defining line, so we must be out of the
644fbc94a17Sniklas tags table. */
6451cc83814Sespie if (name_offset == -1)
646fbc94a17Sniklas break;
647fbc94a17Sniklas
6481cc83814Sespie entry = xmalloc (sizeof (TAG));
6491cc83814Sespie
6501cc83814Sespie /* Find the beginning of the node definition. */
651*a1acfa9bSespie tmp_search->start += name_offset;
652*a1acfa9bSespie nodedef = tmp_search->buffer + tmp_search->start;
653fbc94a17Sniklas nodedef += skip_whitespace (nodedef);
654fbc94a17Sniklas
6551cc83814Sespie /* Move past the node's name in this tag to the TAGSEP character. */
6561cc83814Sespie for (p = 0; nodedef[p] && nodedef[p] != INFO_TAGSEP; p++)
6571cc83814Sespie ;
6581cc83814Sespie if (nodedef[p] != INFO_TAGSEP)
659fbc94a17Sniklas continue;
660fbc94a17Sniklas
6611cc83814Sespie entry->nodename = xmalloc (p + 1);
6621cc83814Sespie strncpy (entry->nodename, nodedef, p);
6631cc83814Sespie entry->nodename[p] = 0;
6641cc83814Sespie p++;
6651cc83814Sespie entry->nodestart = atol (nodedef + p);
666fbc94a17Sniklas
6671cc83814Sespie /* If a node, we don't know the length yet, but if it's an
6681cc83814Sespie anchor, the length is 0. */
6691cc83814Sespie entry->nodelen = anchor ? 0 : -1;
670fbc94a17Sniklas
671fbc94a17Sniklas /* The filename of this node is currently known as the same as the
672fbc94a17Sniklas name of this file. */
673fbc94a17Sniklas entry->filename = file_buffer->fullpath;
674fbc94a17Sniklas
675fbc94a17Sniklas /* Add this node structure to the array of node structures in this
676fbc94a17Sniklas FILE_BUFFER. */
677fbc94a17Sniklas add_pointer_to_array (entry, tags_index, file_buffer->tags,
678fbc94a17Sniklas file_buffer->tags_slots, 100, TAG *);
679fbc94a17Sniklas }
680*a1acfa9bSespie free (tmp_search);
681fbc94a17Sniklas }
682fbc94a17Sniklas
6831cc83814Sespie /* A structure used only in `get_tags_of_indirect_tags_table' to hold onto
684fbc94a17Sniklas an intermediate value. */
685fbc94a17Sniklas typedef struct {
686fbc94a17Sniklas char *filename;
687fbc94a17Sniklas long first_byte;
688fbc94a17Sniklas } SUBFILE;
689fbc94a17Sniklas
690fbc94a17Sniklas /* Remember in FILE_BUFFER the nodenames, subfilenames, and offsets within the
691fbc94a17Sniklas subfiles of every node which appears in TAGS_BINDING. The 2nd argument is
692fbc94a17Sniklas a binding surrounding the indirect files list. */
693fbc94a17Sniklas static void
get_tags_of_indirect_tags_table(FILE_BUFFER * file_buffer,SEARCH_BINDING * indirect_binding,SEARCH_BINDING * tags_binding)694*a1acfa9bSespie get_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer,
695*a1acfa9bSespie SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding)
696fbc94a17Sniklas {
6971cc83814Sespie int i;
6981cc83814Sespie SUBFILE **subfiles = NULL;
699fbc94a17Sniklas int subfiles_index = 0, subfiles_slots = 0;
700fbc94a17Sniklas TAG *entry;
701fbc94a17Sniklas
702fbc94a17Sniklas /* First get the list of tags from the tags table. Then lookup the
703fbc94a17Sniklas associated file in the indirect list for each tag, and update it. */
704fbc94a17Sniklas get_nodes_of_tags_table (file_buffer, tags_binding);
705fbc94a17Sniklas
706fbc94a17Sniklas /* We have the list of tags in file_buffer->tags. Get the list of
707fbc94a17Sniklas subfiles from the indirect table. */
708fbc94a17Sniklas {
709fbc94a17Sniklas char *start, *end, *line;
710fbc94a17Sniklas SUBFILE *subfile;
711fbc94a17Sniklas
712fbc94a17Sniklas start = indirect_binding->buffer + indirect_binding->start;
713fbc94a17Sniklas end = indirect_binding->buffer + indirect_binding->end;
714fbc94a17Sniklas line = start;
715fbc94a17Sniklas
716fbc94a17Sniklas while (line < end)
717fbc94a17Sniklas {
718fbc94a17Sniklas int colon;
719fbc94a17Sniklas
720fbc94a17Sniklas colon = string_in_line (":", line);
721fbc94a17Sniklas
722fbc94a17Sniklas if (colon == -1)
723fbc94a17Sniklas break;
724fbc94a17Sniklas
725fbc94a17Sniklas subfile = (SUBFILE *)xmalloc (sizeof (SUBFILE));
726fbc94a17Sniklas subfile->filename = (char *)xmalloc (colon);
727fbc94a17Sniklas strncpy (subfile->filename, line, colon - 1);
7281cc83814Sespie subfile->filename[colon - 1] = 0;
729fbc94a17Sniklas subfile->first_byte = (long) atol (line + colon);
730fbc94a17Sniklas
731fbc94a17Sniklas add_pointer_to_array
732fbc94a17Sniklas (subfile, subfiles_index, subfiles, subfiles_slots, 10, SUBFILE *);
733fbc94a17Sniklas
734fbc94a17Sniklas while (*line++ != '\n');
735fbc94a17Sniklas }
736fbc94a17Sniklas }
737fbc94a17Sniklas
738fbc94a17Sniklas /* If we have successfully built the indirect files table, then
739fbc94a17Sniklas merge the information in the two tables. */
740fbc94a17Sniklas if (!subfiles)
741fbc94a17Sniklas {
742fbc94a17Sniklas free_file_buffer_tags (file_buffer);
743fbc94a17Sniklas return;
744fbc94a17Sniklas }
745fbc94a17Sniklas else
746fbc94a17Sniklas {
7471cc83814Sespie int tags_index;
748fbc94a17Sniklas long header_length;
749fbc94a17Sniklas SEARCH_BINDING binding;
750fbc94a17Sniklas
751fbc94a17Sniklas /* Find the length of the header of the file containing the indirect
752fbc94a17Sniklas tags table. This header appears at the start of every file. We
753fbc94a17Sniklas want the absolute position of each node within each subfile, so
754fbc94a17Sniklas we subtract the start of the containing subfile from the logical
755fbc94a17Sniklas position of the node, and then add the length of the header in. */
756fbc94a17Sniklas binding.buffer = file_buffer->contents;
757fbc94a17Sniklas binding.start = 0;
758fbc94a17Sniklas binding.end = file_buffer->filesize;
759fbc94a17Sniklas binding.flags = S_FoldCase;
760fbc94a17Sniklas
761fbc94a17Sniklas header_length = find_node_separator (&binding);
762fbc94a17Sniklas if (header_length == -1)
763fbc94a17Sniklas header_length = 0;
764fbc94a17Sniklas
765fbc94a17Sniklas /* Build the file buffer's list of subfiles. */
766fbc94a17Sniklas {
7671cc83814Sespie char *containing_dir = xstrdup (file_buffer->fullpath);
7681cc83814Sespie char *temp = filename_non_directory (containing_dir);
769fbc94a17Sniklas int len_containing_dir;
770fbc94a17Sniklas
7711cc83814Sespie if (temp > containing_dir)
7721cc83814Sespie {
7731cc83814Sespie if (HAVE_DRIVE (file_buffer->fullpath) &&
7741cc83814Sespie temp == containing_dir + 2)
7751cc83814Sespie {
7761cc83814Sespie /* Avoid converting "d:foo" into "d:/foo" below. */
7771cc83814Sespie *temp = '.';
7781cc83814Sespie temp += 2;
7791cc83814Sespie }
7801cc83814Sespie temp[-1] = 0;
7811cc83814Sespie }
782fbc94a17Sniklas
783fbc94a17Sniklas len_containing_dir = strlen (containing_dir);
784fbc94a17Sniklas
785fbc94a17Sniklas for (i = 0; subfiles[i]; i++);
786fbc94a17Sniklas
787fbc94a17Sniklas file_buffer->subfiles = (char **) xmalloc ((1 + i) * sizeof (char *));
788fbc94a17Sniklas
789fbc94a17Sniklas for (i = 0; subfiles[i]; i++)
790fbc94a17Sniklas {
791fbc94a17Sniklas char *fullpath;
792fbc94a17Sniklas
793fbc94a17Sniklas fullpath = (char *) xmalloc
794fbc94a17Sniklas (2 + strlen (subfiles[i]->filename) + len_containing_dir);
795fbc94a17Sniklas
796fbc94a17Sniklas sprintf (fullpath, "%s/%s",
797fbc94a17Sniklas containing_dir, subfiles[i]->filename);
798fbc94a17Sniklas
799fbc94a17Sniklas file_buffer->subfiles[i] = fullpath;
800fbc94a17Sniklas }
8011cc83814Sespie file_buffer->subfiles[i] = NULL;
802fbc94a17Sniklas free (containing_dir);
803fbc94a17Sniklas }
804fbc94a17Sniklas
805fbc94a17Sniklas /* For each node in the file's tags table, remember the starting
806fbc94a17Sniklas position. */
807840175f0Skstailey for (tags_index = 0; (entry = file_buffer->tags[tags_index]);
808fbc94a17Sniklas tags_index++)
809fbc94a17Sniklas {
810fbc94a17Sniklas for (i = 0;
811fbc94a17Sniklas subfiles[i] && entry->nodestart >= subfiles[i]->first_byte;
812fbc94a17Sniklas i++);
813fbc94a17Sniklas
814fbc94a17Sniklas /* If the Info file containing the indirect tags table is
815fbc94a17Sniklas malformed, then give up. */
816fbc94a17Sniklas if (!i)
817fbc94a17Sniklas {
818fbc94a17Sniklas /* The Info file containing the indirect tags table is
819fbc94a17Sniklas malformed. Give up. */
820fbc94a17Sniklas for (i = 0; subfiles[i]; i++)
821fbc94a17Sniklas {
822fbc94a17Sniklas free (subfiles[i]->filename);
823fbc94a17Sniklas free (subfiles[i]);
824fbc94a17Sniklas free (file_buffer->subfiles[i]);
825fbc94a17Sniklas }
8261cc83814Sespie file_buffer->subfiles = NULL;
827fbc94a17Sniklas free_file_buffer_tags (file_buffer);
828fbc94a17Sniklas return;
829fbc94a17Sniklas }
830fbc94a17Sniklas
831fbc94a17Sniklas /* SUBFILES[i] is the index of the first subfile whose logical
832fbc94a17Sniklas first byte is greater than the logical offset of this node's
833fbc94a17Sniklas starting position. This means that the subfile directly
834fbc94a17Sniklas preceding this one is the one containing the node. */
835fbc94a17Sniklas
836fbc94a17Sniklas entry->filename = file_buffer->subfiles[i - 1];
837fbc94a17Sniklas entry->nodestart -= subfiles[i - 1]->first_byte;
838fbc94a17Sniklas entry->nodestart += header_length;
839fbc94a17Sniklas }
840fbc94a17Sniklas
841fbc94a17Sniklas /* We have successfully built the tags table. Remember that it
842fbc94a17Sniklas was indirect. */
843fbc94a17Sniklas file_buffer->flags |= N_TagsIndirect;
844fbc94a17Sniklas }
845fbc94a17Sniklas
846fbc94a17Sniklas /* Free the structures assigned to SUBFILES. Free the names as well
847fbc94a17Sniklas as the structures themselves, then finally, the array. */
848fbc94a17Sniklas for (i = 0; subfiles[i]; i++)
849fbc94a17Sniklas {
850fbc94a17Sniklas free (subfiles[i]->filename);
851fbc94a17Sniklas free (subfiles[i]);
852fbc94a17Sniklas }
853fbc94a17Sniklas free (subfiles);
854fbc94a17Sniklas }
855fbc94a17Sniklas
8561cc83814Sespie
8571cc83814Sespie /* Return the node that contains TAG in FILE_BUFFER, else
8581cc83814Sespie (pathologically) NULL. Called from info_node_of_file_buffer_tags. */
8591cc83814Sespie static NODE *
find_node_of_anchor(FILE_BUFFER * file_buffer,TAG * tag)860*a1acfa9bSespie find_node_of_anchor (FILE_BUFFER *file_buffer, TAG *tag)
8611cc83814Sespie {
8621cc83814Sespie int anchor_pos, node_pos;
8631cc83814Sespie TAG *node_tag;
8641cc83814Sespie NODE *node;
8651cc83814Sespie
8661cc83814Sespie /* Look through the tag list for the anchor. */
8671cc83814Sespie for (anchor_pos = 0; file_buffer->tags[anchor_pos]; anchor_pos++)
8681cc83814Sespie {
8691cc83814Sespie TAG *t = file_buffer->tags[anchor_pos];
8701cc83814Sespie if (t->nodestart == tag->nodestart)
8711cc83814Sespie break;
8721cc83814Sespie }
8731cc83814Sespie
8741cc83814Sespie /* Should not happen, because we should always find the anchor. */
8751cc83814Sespie if (!file_buffer->tags[anchor_pos])
8761cc83814Sespie return NULL;
8771cc83814Sespie
8781cc83814Sespie /* We've found the anchor. Look backwards in the tag table for the
8791cc83814Sespie preceding node (we're assuming the tags are given in order),
8801cc83814Sespie skipping over any preceding anchors. */
8811cc83814Sespie for (node_pos = anchor_pos - 1;
8821cc83814Sespie node_pos >= 0 && file_buffer->tags[node_pos]->nodelen == 0;
8831cc83814Sespie node_pos--)
8841cc83814Sespie ;
8851cc83814Sespie
8861cc83814Sespie /* An info file with an anchor before any nodes is pathological, but
8871cc83814Sespie it's possible, so don't crash. */
8881cc83814Sespie if (node_pos < 0)
8891cc83814Sespie return NULL;
8901cc83814Sespie
8911cc83814Sespie /* We have the tag for the node that contained the anchor tag. */
8921cc83814Sespie node_tag = file_buffer->tags[node_pos];
8931cc83814Sespie
8941cc83814Sespie /* Look up the node name in the tag table to get the actual node.
8951cc83814Sespie This is a recursive call, but it can't recurse again, because we
8961cc83814Sespie call it with a real node. */
8971cc83814Sespie node = info_node_of_file_buffer_tags (file_buffer, node_tag->nodename);
8981cc83814Sespie
8991cc83814Sespie /* Start displaying the node at the anchor position. */
9001cc83814Sespie if (node)
9011cc83814Sespie { /* The nodestart for real nodes is three characters before the `F'
9021cc83814Sespie in the `File:' line (a newline, the CTRL-_, and another
9031cc83814Sespie newline). The nodestart for anchors is the actual position.
9041cc83814Sespie But we offset by only 2, rather than 3, because if an anchor is
9051cc83814Sespie at the beginning of a paragraph, it's nicer for it to end up on
9061cc83814Sespie the beginning of the first line of the paragraph rather than
9071cc83814Sespie the blank line before it. (makeinfo has no way of knowing that
9081cc83814Sespie a paragraph is going to start, so we can't fix it there.) */
9091cc83814Sespie node->display_pos = file_buffer->tags[anchor_pos]->nodestart
9101cc83814Sespie - (node_tag->nodestart + 2);
9111cc83814Sespie
9121cc83814Sespie /* Otherwise an anchor at the end of a node ends up displaying at
9131cc83814Sespie the end of the last line of the node (way over on the right of
9141cc83814Sespie the screen), which looks wrong. */
915*a1acfa9bSespie if (node->display_pos >= (unsigned long) node->nodelen)
9161cc83814Sespie node->display_pos = node->nodelen - 1;
9171cc83814Sespie
9181cc83814Sespie /* Don't search in the node for the xref text, it's not there. */
9191cc83814Sespie node->flags |= N_FromAnchor;
9201cc83814Sespie }
9211cc83814Sespie
9221cc83814Sespie return node;
9231cc83814Sespie }
9241cc83814Sespie
9251cc83814Sespie
926fbc94a17Sniklas /* Return the node from FILE_BUFFER which matches NODENAME by searching
9271cc83814Sespie the tags table in FILE_BUFFER, or NULL. */
928fbc94a17Sniklas static NODE *
info_node_of_file_buffer_tags(FILE_BUFFER * file_buffer,char * nodename)929*a1acfa9bSespie info_node_of_file_buffer_tags (FILE_BUFFER *file_buffer, char *nodename)
930fbc94a17Sniklas {
931fbc94a17Sniklas TAG *tag;
9321cc83814Sespie int i;
933fbc94a17Sniklas
9343fb98d4aSespie /* If no tags at all (possibly a misformatted info file), quit. */
9353fb98d4aSespie if (!file_buffer->tags) {
9363fb98d4aSespie return NULL;
9373fb98d4aSespie }
9383fb98d4aSespie
939840175f0Skstailey for (i = 0; (tag = file_buffer->tags[i]); i++)
940fbc94a17Sniklas if (strcmp (nodename, tag->nodename) == 0)
941fbc94a17Sniklas {
9423fb98d4aSespie FILE_BUFFER *subfile = info_find_file_internal (tag->filename,
9433fb98d4aSespie INFO_NO_TAGS);
944fbc94a17Sniklas if (!subfile)
9451cc83814Sespie return NULL;
946fbc94a17Sniklas
947fbc94a17Sniklas if (!subfile->contents)
948fbc94a17Sniklas {
949fbc94a17Sniklas info_reload_file_buffer_contents (subfile);
950fbc94a17Sniklas if (!subfile->contents)
9511cc83814Sespie return NULL;
952fbc94a17Sniklas }
953fbc94a17Sniklas
954fbc94a17Sniklas /* If we were able to find this file and load it, then return
955fbc94a17Sniklas the node within it. */
956fbc94a17Sniklas {
9571cc83814Sespie NODE *node = xmalloc (sizeof (NODE));
9581cc83814Sespie node->filename = subfile->fullpath;
9591cc83814Sespie node->parent = NULL;
960fbc94a17Sniklas node->nodename = tag->nodename;
961fbc94a17Sniklas node->contents = subfile->contents + tag->nodestart;
9621cc83814Sespie node->display_pos = 0;
963fbc94a17Sniklas node->flags = 0;
964fbc94a17Sniklas
965fbc94a17Sniklas if (file_buffer->flags & N_HasTagsTable)
966fbc94a17Sniklas {
967fbc94a17Sniklas node->flags |= N_HasTagsTable;
968fbc94a17Sniklas
969fbc94a17Sniklas if (file_buffer->flags & N_TagsIndirect)
970fbc94a17Sniklas {
971fbc94a17Sniklas node->flags |= N_TagsIndirect;
972fbc94a17Sniklas node->parent = file_buffer->fullpath;
973fbc94a17Sniklas }
974fbc94a17Sniklas }
975fbc94a17Sniklas
976fbc94a17Sniklas if (subfile->flags & N_IsCompressed)
977fbc94a17Sniklas node->flags |= N_IsCompressed;
978fbc94a17Sniklas
979fbc94a17Sniklas /* If TAG->nodelen hasn't been calculated yet, then we aren't
980fbc94a17Sniklas in a position to trust the entry pointer. Adjust things so
981fbc94a17Sniklas that ENTRY->nodestart gets the exact address of the start of
982fbc94a17Sniklas the node separator which starts this node, and NODE->contents
983fbc94a17Sniklas gets the address of the line defining this node. If we cannot
984fbc94a17Sniklas do that, the node isn't really here. */
985fbc94a17Sniklas if (tag->nodelen == -1)
986fbc94a17Sniklas {
987fbc94a17Sniklas int min, max;
988fbc94a17Sniklas char *node_sep;
989fbc94a17Sniklas SEARCH_BINDING node_body;
990fbc94a17Sniklas char *buff_end;
991fbc94a17Sniklas
992fbc94a17Sniklas min = max = DEFAULT_INFO_FUDGE;
993fbc94a17Sniklas
994fbc94a17Sniklas if (tag->nodestart < DEFAULT_INFO_FUDGE)
995fbc94a17Sniklas min = tag->nodestart;
996fbc94a17Sniklas
997fbc94a17Sniklas if (DEFAULT_INFO_FUDGE >
998fbc94a17Sniklas (subfile->filesize - tag->nodestart))
999fbc94a17Sniklas max = subfile->filesize - tag->nodestart;
1000fbc94a17Sniklas
1001fbc94a17Sniklas /* NODE_SEP gets the address of the separator which defines
10021cc83814Sespie this node, or NULL if the node wasn't found.
1003fbc94a17Sniklas NODE->contents is side-effected to point to right after
1004fbc94a17Sniklas the separator. */
1005fbc94a17Sniklas node_sep = adjust_nodestart (node, min, max);
10061cc83814Sespie if (node_sep == NULL)
1007fbc94a17Sniklas {
1008fbc94a17Sniklas free (node);
10091cc83814Sespie return NULL;
1010fbc94a17Sniklas }
1011fbc94a17Sniklas /* Readjust tag->nodestart. */
1012fbc94a17Sniklas tag->nodestart = node_sep - subfile->contents;
1013fbc94a17Sniklas
1014fbc94a17Sniklas /* Calculate the length of the current node. */
1015fbc94a17Sniklas buff_end = subfile->contents + subfile->filesize;
1016fbc94a17Sniklas
1017fbc94a17Sniklas node_body.buffer = node->contents;
1018fbc94a17Sniklas node_body.start = 0;
1019fbc94a17Sniklas node_body.end = buff_end - node_body.buffer;
1020fbc94a17Sniklas node_body.flags = 0;
1021fbc94a17Sniklas tag->nodelen = get_node_length (&node_body);
10221cc83814Sespie node->nodelen = tag->nodelen;
1023fbc94a17Sniklas }
10241cc83814Sespie
10251cc83814Sespie else if (tag->nodelen == 0) /* anchor, return containing node */
10261cc83814Sespie {
10271cc83814Sespie free (node);
10281cc83814Sespie node = find_node_of_anchor (file_buffer, tag);
10291cc83814Sespie }
10301cc83814Sespie
1031fbc94a17Sniklas else
1032fbc94a17Sniklas {
1033fbc94a17Sniklas /* Since we know the length of this node, we have already
1034fbc94a17Sniklas adjusted tag->nodestart to point to the exact start of
1035fbc94a17Sniklas it. Simply skip the node separator. */
1036fbc94a17Sniklas node->contents += skip_node_separator (node->contents);
10371cc83814Sespie node->nodelen = tag->nodelen;
1038fbc94a17Sniklas }
1039fbc94a17Sniklas
10401cc83814Sespie return node;
1041fbc94a17Sniklas }
1042fbc94a17Sniklas }
1043fbc94a17Sniklas
1044fbc94a17Sniklas /* There was a tag table for this file, and the node wasn't found.
1045fbc94a17Sniklas Return NULL, since this file doesn't contain the desired node. */
10461cc83814Sespie return NULL;
1047fbc94a17Sniklas }
1048fbc94a17Sniklas
1049fbc94a17Sniklas /* Managing file_buffers, nodes, and tags. */
1050fbc94a17Sniklas
1051fbc94a17Sniklas /* Create a new, empty file buffer. */
1052fbc94a17Sniklas FILE_BUFFER *
make_file_buffer(void)1053*a1acfa9bSespie make_file_buffer (void)
1054fbc94a17Sniklas {
10551cc83814Sespie FILE_BUFFER *file_buffer = xmalloc (sizeof (FILE_BUFFER));
1056fbc94a17Sniklas
10571cc83814Sespie file_buffer->filename = file_buffer->fullpath = NULL;
10581cc83814Sespie file_buffer->contents = NULL;
10591cc83814Sespie file_buffer->tags = NULL;
10601cc83814Sespie file_buffer->subfiles = NULL;
1061fbc94a17Sniklas file_buffer->tags_slots = 0;
1062fbc94a17Sniklas file_buffer->flags = 0;
1063fbc94a17Sniklas
10641cc83814Sespie return file_buffer;
1065fbc94a17Sniklas }
1066fbc94a17Sniklas
1067fbc94a17Sniklas /* Add FILE_BUFFER to our list of already loaded info files. */
1068fbc94a17Sniklas static void
remember_info_file(FILE_BUFFER * file_buffer)1069*a1acfa9bSespie remember_info_file (FILE_BUFFER *file_buffer)
1070fbc94a17Sniklas {
1071fbc94a17Sniklas int i;
1072fbc94a17Sniklas
1073fbc94a17Sniklas for (i = 0; info_loaded_files && info_loaded_files[i]; i++)
1074fbc94a17Sniklas ;
1075fbc94a17Sniklas
1076fbc94a17Sniklas add_pointer_to_array (file_buffer, i, info_loaded_files,
1077fbc94a17Sniklas info_loaded_files_slots, 10, FILE_BUFFER *);
1078fbc94a17Sniklas }
1079fbc94a17Sniklas
1080fbc94a17Sniklas /* Forget the contents, tags table, nodes list, and names of FILENAME. */
1081fbc94a17Sniklas static void
forget_info_file(char * filename)1082*a1acfa9bSespie forget_info_file (char *filename)
1083fbc94a17Sniklas {
10841cc83814Sespie int i;
1085fbc94a17Sniklas FILE_BUFFER *file_buffer;
1086fbc94a17Sniklas
1087fbc94a17Sniklas if (!info_loaded_files)
1088fbc94a17Sniklas return;
1089fbc94a17Sniklas
1090*a1acfa9bSespie for (i = 0; (file_buffer = info_loaded_files[i]); i++)
10911cc83814Sespie if (FILENAME_CMP (filename, file_buffer->filename) == 0
10921cc83814Sespie || FILENAME_CMP (filename, file_buffer->fullpath) == 0)
1093fbc94a17Sniklas {
1094fbc94a17Sniklas free (file_buffer->filename);
1095fbc94a17Sniklas free (file_buffer->fullpath);
1096fbc94a17Sniklas
1097fbc94a17Sniklas if (file_buffer->contents)
1098fbc94a17Sniklas free (file_buffer->contents);
1099fbc94a17Sniklas
11001cc83814Sespie /* free_file_buffer_tags () also kills the subfiles list, since
11011cc83814Sespie the subfiles list is only of use in conjunction with tags. */
1102fbc94a17Sniklas free_file_buffer_tags (file_buffer);
1103fbc94a17Sniklas
11041cc83814Sespie /* Move rest of list down. */
11051cc83814Sespie while (info_loaded_files[i + 1])
11061cc83814Sespie {
11071cc83814Sespie info_loaded_files[i] = info_loaded_files[i + 1];
11081cc83814Sespie i++;
11091cc83814Sespie }
11101cc83814Sespie info_loaded_files[i] = 0;
1111fbc94a17Sniklas
1112fbc94a17Sniklas break;
1113fbc94a17Sniklas }
1114fbc94a17Sniklas }
1115fbc94a17Sniklas
1116fbc94a17Sniklas /* Free the tags (if any) associated with FILE_BUFFER. */
1117fbc94a17Sniklas static void
free_file_buffer_tags(FILE_BUFFER * file_buffer)1118*a1acfa9bSespie free_file_buffer_tags (FILE_BUFFER *file_buffer)
1119fbc94a17Sniklas {
11201cc83814Sespie int i;
1121fbc94a17Sniklas
1122fbc94a17Sniklas if (file_buffer->tags)
1123fbc94a17Sniklas {
11241cc83814Sespie TAG *tag;
1125fbc94a17Sniklas
1126840175f0Skstailey for (i = 0; (tag = file_buffer->tags[i]); i++)
1127fbc94a17Sniklas free_info_tag (tag);
1128fbc94a17Sniklas
1129fbc94a17Sniklas free (file_buffer->tags);
11301cc83814Sespie file_buffer->tags = NULL;
1131fbc94a17Sniklas file_buffer->tags_slots = 0;
1132fbc94a17Sniklas }
1133fbc94a17Sniklas
1134fbc94a17Sniklas if (file_buffer->subfiles)
1135fbc94a17Sniklas {
1136fbc94a17Sniklas for (i = 0; file_buffer->subfiles[i]; i++)
1137fbc94a17Sniklas free (file_buffer->subfiles[i]);
1138fbc94a17Sniklas
1139fbc94a17Sniklas free (file_buffer->subfiles);
11401cc83814Sespie file_buffer->subfiles = NULL;
1141fbc94a17Sniklas }
1142fbc94a17Sniklas }
1143fbc94a17Sniklas
1144fbc94a17Sniklas /* Free the data associated with TAG, as well as TAG itself. */
1145fbc94a17Sniklas static void
free_info_tag(TAG * tag)1146*a1acfa9bSespie free_info_tag (TAG *tag)
1147fbc94a17Sniklas {
1148fbc94a17Sniklas free (tag->nodename);
1149fbc94a17Sniklas
1150fbc94a17Sniklas /* We don't free tag->filename, because that filename is part of the
1151fbc94a17Sniklas subfiles list for the containing FILE_BUFFER. free_info_tags ()
1152fbc94a17Sniklas will free the subfiles when it is appropriate. */
1153fbc94a17Sniklas
1154fbc94a17Sniklas free (tag);
1155fbc94a17Sniklas }
1156fbc94a17Sniklas
1157fbc94a17Sniklas /* Load the contents of FILE_BUFFER->contents. This function is called
1158fbc94a17Sniklas when a file buffer was loaded, and then in order to conserve memory, the
1159fbc94a17Sniklas file buffer's contents were freed and the pointer was zero'ed. Note that
1160fbc94a17Sniklas the file was already loaded at least once successfully, so the tags and/or
1161fbc94a17Sniklas nodes members are still correctly filled. */
1162fbc94a17Sniklas static void
info_reload_file_buffer_contents(FILE_BUFFER * fb)1163*a1acfa9bSespie info_reload_file_buffer_contents (FILE_BUFFER *fb)
1164fbc94a17Sniklas {
11651cc83814Sespie int is_compressed;
1166fbc94a17Sniklas
1167fbc94a17Sniklas #if defined (HANDLE_MAN_PAGES)
1168fbc94a17Sniklas /* If this is the magic manpage node, don't try to reload, just give up. */
1169fbc94a17Sniklas if (fb->flags & N_IsManPage)
1170fbc94a17Sniklas return;
1171fbc94a17Sniklas #endif
1172fbc94a17Sniklas
1173fbc94a17Sniklas fb->flags &= ~N_IsCompressed;
1174fbc94a17Sniklas
1175fbc94a17Sniklas /* Let the filesystem do all the work for us. */
1176fbc94a17Sniklas fb->contents =
11771cc83814Sespie filesys_read_info_file (fb->fullpath, &(fb->filesize), &(fb->finfo),
11781cc83814Sespie &is_compressed);
11791cc83814Sespie if (is_compressed)
1180fbc94a17Sniklas fb->flags |= N_IsCompressed;
1181fbc94a17Sniklas }
1182fbc94a17Sniklas
1183fbc94a17Sniklas /* Return the actual starting memory location of NODE, side-effecting
1184fbc94a17Sniklas NODE->contents. MIN and MAX are bounds for a search if one is necessary.
1185fbc94a17Sniklas Because of the way that tags are implemented, the physical nodestart may
1186fbc94a17Sniklas not actually be where the tag says it is. If that is the case, but the
1187fbc94a17Sniklas node was found anyway, set N_UpdateTags in NODE->flags. If the node is
1188fbc94a17Sniklas found, return non-zero. NODE->contents is returned positioned right after
1189fbc94a17Sniklas the node separator that precedes this node, while the return value is
1190fbc94a17Sniklas position directly on the separator that precedes this node. If the node
1191fbc94a17Sniklas could not be found, return a NULL pointer. */
1192fbc94a17Sniklas static char *
adjust_nodestart(NODE * node,int min,int max)1193*a1acfa9bSespie adjust_nodestart (NODE *node, int min, int max)
1194fbc94a17Sniklas {
1195fbc94a17Sniklas long position;
1196fbc94a17Sniklas SEARCH_BINDING node_body;
1197fbc94a17Sniklas
1198fbc94a17Sniklas /* Define the node body. */
1199fbc94a17Sniklas node_body.buffer = node->contents;
1200fbc94a17Sniklas node_body.start = 0;
1201fbc94a17Sniklas node_body.end = max;
1202fbc94a17Sniklas node_body.flags = 0;
1203fbc94a17Sniklas
1204fbc94a17Sniklas /* Try the optimal case first. Who knows? This file may actually be
1205fbc94a17Sniklas formatted (mostly) correctly. */
1206fbc94a17Sniklas if (node_body.buffer[0] != INFO_COOKIE && min > 2)
1207fbc94a17Sniklas node_body.buffer -= 3;
1208fbc94a17Sniklas
1209fbc94a17Sniklas position = find_node_separator (&node_body);
1210fbc94a17Sniklas
1211fbc94a17Sniklas /* If we found a node start, then check it out. */
1212fbc94a17Sniklas if (position != -1)
1213fbc94a17Sniklas {
1214fbc94a17Sniklas int sep_len;
1215fbc94a17Sniklas
1216fbc94a17Sniklas sep_len = skip_node_separator (node->contents);
1217fbc94a17Sniklas
1218fbc94a17Sniklas /* If we managed to skip a node separator, then check for this node
1219fbc94a17Sniklas being the right one. */
1220fbc94a17Sniklas if (sep_len != 0)
1221fbc94a17Sniklas {
1222fbc94a17Sniklas char *nodedef, *nodestart;
1223fbc94a17Sniklas int offset;
1224fbc94a17Sniklas
1225fbc94a17Sniklas nodestart = node_body.buffer + position + sep_len;
1226fbc94a17Sniklas nodedef = nodestart;
1227fbc94a17Sniklas offset = string_in_line (INFO_NODE_LABEL, nodedef);
1228fbc94a17Sniklas
1229fbc94a17Sniklas if (offset != -1)
1230fbc94a17Sniklas {
1231fbc94a17Sniklas nodedef += offset;
1232fbc94a17Sniklas nodedef += skip_whitespace (nodedef);
1233fbc94a17Sniklas offset = skip_node_characters (nodedef, DONT_SKIP_NEWLINES);
1234*a1acfa9bSespie if (((unsigned int) offset == strlen (node->nodename)) &&
1235fbc94a17Sniklas (strncmp (node->nodename, nodedef, offset) == 0))
1236fbc94a17Sniklas {
1237fbc94a17Sniklas node->contents = nodestart;
12381cc83814Sespie return node_body.buffer + position;
1239fbc94a17Sniklas }
1240fbc94a17Sniklas }
1241fbc94a17Sniklas }
1242fbc94a17Sniklas }
1243fbc94a17Sniklas
1244fbc94a17Sniklas /* Oh well, I guess we have to try to find it in a larger area. */
1245fbc94a17Sniklas node_body.buffer = node->contents - min;
1246fbc94a17Sniklas node_body.start = 0;
1247fbc94a17Sniklas node_body.end = min + max;
1248fbc94a17Sniklas node_body.flags = 0;
1249fbc94a17Sniklas
1250fbc94a17Sniklas position = find_node_in_binding (node->nodename, &node_body);
1251fbc94a17Sniklas
1252fbc94a17Sniklas /* If the node couldn't be found, we lose big. */
1253fbc94a17Sniklas if (position == -1)
12541cc83814Sespie return NULL;
1255fbc94a17Sniklas
1256fbc94a17Sniklas /* Otherwise, the node was found, but the tags table could need updating
1257fbc94a17Sniklas (if we used a tag to get here, that is). Set the flag in NODE->flags. */
1258fbc94a17Sniklas node->contents = node_body.buffer + position;
1259fbc94a17Sniklas node->contents += skip_node_separator (node->contents);
1260fbc94a17Sniklas if (node->flags & N_HasTagsTable)
1261fbc94a17Sniklas node->flags |= N_UpdateTags;
12621cc83814Sespie return node_body.buffer + position;
1263fbc94a17Sniklas }
1264