xref: /openbsd-src/gnu/usr.bin/texinfo/info/nodes.c (revision a1acfa9b69ad64eb720639240c8438f11107dc85)
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