xref: /openbsd/gnu/usr.bin/texinfo/info/nodes.c (revision a1acfa9b)
1 /* nodes.c -- how to get an Info file and node.
2    $Id: nodes.c,v 1.1.1.5 2006/07/17 16:03:43 espie Exp $
3 
4    Copyright (C) 1993, 1998, 1999, 2000, 2002, 2003, 2004 Free Software
5    Foundation, Inc.
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2, or (at your option)
10    any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 
21    Originally written by Brian Fox (bfox@ai.mit.edu). */
22 
23 #include "info.h"
24 
25 #include "nodes.h"
26 #include "search.h"
27 #include "filesys.h"
28 #include "info-utils.h"
29 
30 #if defined (HANDLE_MAN_PAGES)
31 #  include "man.h"
32 #endif /* HANDLE_MAN_PAGES */
33 
34 static void forget_info_file (char *filename);
35 static void remember_info_file (FILE_BUFFER *file_buffer);
36 static void free_file_buffer_tags (FILE_BUFFER *file_buffer);
37 static void free_info_tag (TAG *tag);
38 static void get_nodes_of_tags_table (FILE_BUFFER *file_buffer,
39     SEARCH_BINDING *buffer_binding);
40 static void get_nodes_of_info_file (FILE_BUFFER *file_buffer);
41 static void get_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer,
42     SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding);
43 static void info_reload_file_buffer_contents (FILE_BUFFER *fb);
44 static char *adjust_nodestart (NODE *node, int min, int max);
45 static FILE_BUFFER *info_load_file_internal (char *filename, int get_tags);
46 static FILE_BUFFER *info_find_file_internal (char *filename, int get_tags);
47 static NODE *info_node_of_file_buffer_tags (FILE_BUFFER *file_buffer,
48     char *nodename);
49 
50 static long get_node_length (SEARCH_BINDING *binding);
51 
52 /* Magic number that RMS used to decide how much a tags table pointer could
53    be off by.  I feel that it should be much smaller, like 4.  */
54 #define DEFAULT_INFO_FUDGE 1000
55 
56 /* Passed to *_internal functions.  INFO_GET_TAGS says to do what is
57    neccessary to fill in the nodes or tags arrays in FILE_BUFFER. */
58 #define INFO_NO_TAGS  0
59 #define INFO_GET_TAGS 1
60 
61 /* Global variables.  */
62 
63 /* When non-zero, this is a string describing the recent file error. */
64 char *info_recent_file_error = NULL;
65 
66 /* The list of already loaded nodes. */
67 FILE_BUFFER **info_loaded_files = NULL;
68 
69 /* The number of slots currently allocated to LOADED_FILES. */
70 int info_loaded_files_slots = 0;
71 
72 /* Public functions for node manipulation.  */
73 
74 /* Used to build `dir' menu from `localdir' files found in INFOPATH. */
75 extern void maybe_build_dir_node (char *dirname);
76 
77 /* Return a pointer to a NODE structure for the Info node (FILENAME)NODENAME.
78    If FILENAME is NULL, `dir' is used.
79    IF NODENAME is NULL, `Top' is used.
80    If the node cannot be found, return NULL. */
81 NODE *
info_get_node(char * filename,char * nodename)82 info_get_node (char *filename, char *nodename)
83 {
84   NODE *node;
85   FILE_BUFFER *file_buffer = NULL;
86 
87   info_recent_file_error = NULL;
88   info_parse_node (nodename, DONT_SKIP_NEWLINES);
89   nodename = NULL;
90 
91   if (info_parsed_filename)
92     filename = info_parsed_filename;
93 
94   if (info_parsed_nodename)
95     nodename = info_parsed_nodename;
96 
97   /* If FILENAME is not specified, it defaults to "dir". */
98   if (!filename)
99     filename = "dir";
100 
101   /* If the file to be looked up is "dir", build the contents from all of
102      the "dir"s and "localdir"s found in INFOPATH. */
103   if (is_dir_name (filename))
104     maybe_build_dir_node (filename);
105 
106   /* Find the correct info file, or give up.  */
107   file_buffer = info_find_file (filename);
108   if (!file_buffer)
109     {
110       if (filesys_error_number)
111         info_recent_file_error =
112           filesys_error_string (filename, filesys_error_number);
113       return NULL;
114     }
115 
116   /* Look for the node.  */
117   node = info_get_node_of_file_buffer (nodename, file_buffer);
118 
119   /* If the node not found was "Top", try again with different case.  */
120   if (!node && (nodename == NULL || strcasecmp (nodename, "Top") == 0))
121     {
122       node = info_get_node_of_file_buffer ("Top", file_buffer);
123       if (!node)
124         node = info_get_node_of_file_buffer ("top", file_buffer);
125       if (!node)
126         node = info_get_node_of_file_buffer ("TOP", file_buffer);
127     }
128 
129   return node;
130 }
131 
132 /* Return a pointer to a NODE structure for the Info node NODENAME in
133    FILE_BUFFER.  NODENAME can be passed as NULL, in which case the
134    nodename of "Top" is used.  If the node cannot be found, return a
135    NULL pointer. */
136 NODE *
info_get_node_of_file_buffer(char * nodename,FILE_BUFFER * file_buffer)137 info_get_node_of_file_buffer (char *nodename, FILE_BUFFER *file_buffer)
138 {
139   NODE *node = NULL;
140 
141   /* If we are unable to find the file, we have to give up.  There isn't
142      anything else we can do. */
143   if (!file_buffer)
144     return NULL;
145 
146   /* If the file buffer was gc'ed, reload the contents now. */
147   if (!file_buffer->contents)
148     info_reload_file_buffer_contents (file_buffer);
149 
150   /* If NODENAME is not specified, it defaults to "Top". */
151   if (!nodename)
152     nodename = "Top";
153 
154   /* If the name of the node that we wish to find is exactly "*", then the
155      node body is the contents of the entire file.  Create and return such
156      a node. */
157   if (strcmp (nodename, "*") == 0)
158     {
159       node = (NODE *)xmalloc (sizeof (NODE));
160       node->filename = file_buffer->fullpath;
161       node->parent   = NULL;
162       node->nodename = xstrdup ("*");
163       node->contents = file_buffer->contents;
164       node->nodelen = file_buffer->filesize;
165       node->flags = 0;
166       node->display_pos = 0;
167     }
168 #if defined (HANDLE_MAN_PAGES)
169   /* If the file buffer is the magic one associated with manpages, call
170      the manpage node finding function instead. */
171   else if (file_buffer->flags & N_IsManPage)
172     {
173         node = get_manpage_node (file_buffer, nodename);
174     }
175 #endif /* HANDLE_MAN_PAGES */
176   /* If this is the "main" info file, it might contain a tags table.  Search
177      the tags table for an entry which matches the node that we want.  If
178      there is a tags table, get the file which contains this node, but don't
179      bother building a node list for it. */
180   else if (file_buffer->tags)
181     {
182       node = info_node_of_file_buffer_tags (file_buffer, nodename);
183     }
184 
185   /* Return the results of our node search. */
186   return node;
187 }
188 
189 /* Locate the file named by FILENAME, and return the information structure
190    describing this file.  The file may appear in our list of loaded files
191    already, or it may not.  If it does not already appear, find the file,
192    and add it to the list of loaded files.  If the file cannot be found,
193    return a NULL FILE_BUFFER *. */
194 FILE_BUFFER *
info_find_file(char * filename)195 info_find_file (char *filename)
196 {
197   return info_find_file_internal (filename, INFO_GET_TAGS);
198 }
199 
200 /* Load the info file FILENAME, remembering information about it in a
201    file buffer. */
202 FILE_BUFFER *
info_load_file(char * filename)203 info_load_file (char *filename)
204 {
205   return info_load_file_internal (filename, INFO_GET_TAGS);
206 }
207 
208 
209 /* Private functions implementation.  */
210 
211 /* The workhorse for info_find_file ().  Non-zero 2nd argument says to
212    try to build a tags table (or otherwise glean the nodes) for this
213    file once found.  By default, we build the tags table, but when this
214    function is called by info_get_node () when we already have a valid
215    tags table describing the nodes, it is unnecessary. */
216 static FILE_BUFFER *
info_find_file_internal(char * filename,int get_tags)217 info_find_file_internal (char *filename, int get_tags)
218 {
219   int i;
220   FILE_BUFFER *file_buffer;
221 
222   /* First try to find the file in our list of already loaded files. */
223   if (info_loaded_files)
224     {
225       for (i = 0; (file_buffer = info_loaded_files[i]); i++)
226         if ((FILENAME_CMP (filename, file_buffer->filename) == 0)
227             || (FILENAME_CMP (filename, file_buffer->fullpath) == 0)
228             || (!IS_ABSOLUTE (filename)
229                 && FILENAME_CMP (filename,
230                                 filename_non_directory (file_buffer->fullpath))
231                     == 0))
232           {
233             struct stat new_info, *old_info;
234 
235             /* This file is loaded.  If the filename that we want is
236                specifically "dir", then simply return the file buffer. */
237             if (is_dir_name (filename_non_directory (filename)))
238               return file_buffer;
239 
240 #if defined (HANDLE_MAN_PAGES)
241             /* Do the same for the magic MANPAGE file. */
242             if (file_buffer->flags & N_IsManPage)
243               return file_buffer;
244 #endif /* HANDLE_MAN_PAGES */
245 
246             /* The file appears to be already loaded, and is not "dir".  Check
247                to see if it's changed since the last time it was loaded.  */
248             if (stat (file_buffer->fullpath, &new_info) == -1)
249               {
250                 filesys_error_number = errno;
251                 return NULL;
252               }
253 
254             old_info = &file_buffer->finfo;
255 
256             if (new_info.st_size != old_info->st_size
257                 || new_info.st_mtime != old_info->st_mtime)
258               {
259                 /* The file has changed.  Forget that we ever had loaded it
260                    in the first place. */
261                 forget_info_file (filename);
262                 break;
263               }
264             else
265               {
266                 /* The info file exists, and has not changed since the last
267                    time it was loaded.  If the caller requested a nodes list
268                    for this file, and there isn't one here, build the nodes
269                    for this file_buffer.  In any case, return the file_buffer
270                    object. */
271                 if (!file_buffer->contents)
272                   {
273                     /* The file's contents have been gc'ed.  Reload it.  */
274                     info_reload_file_buffer_contents (file_buffer);
275                     if (!file_buffer->contents)
276                       return NULL;
277                   }
278 
279                 if (get_tags && !file_buffer->tags)
280                   build_tags_and_nodes (file_buffer);
281 
282                 return file_buffer;
283               }
284           }
285     }
286 
287   /* The file wasn't loaded.  Try to load it now. */
288 #if defined (HANDLE_MAN_PAGES)
289   /* If the name of the file that we want is our special file buffer for
290      Unix manual pages, then create the file buffer, and return it now. */
291   if (strcasecmp (filename, MANPAGE_FILE_BUFFER_NAME) == 0)
292     file_buffer = create_manpage_file_buffer ();
293   else
294 #endif /* HANDLE_MAN_PAGES */
295     file_buffer = info_load_file_internal (filename, get_tags);
296 
297   /* If the file was loaded, remember the name under which it was found. */
298   if (file_buffer)
299     remember_info_file (file_buffer);
300 
301   return file_buffer;
302 }
303 
304 /* The workhorse function for info_load_file ().  Non-zero second argument
305    says to build a list of tags (or nodes) for this file.  This is the
306    default behaviour when info_load_file () is called, but it is not
307    necessary when loading a subfile for which we already have tags. */
308 static FILE_BUFFER *
info_load_file_internal(char * filename,int get_tags)309 info_load_file_internal (char *filename, int get_tags)
310 {
311   char *fullpath, *contents;
312   long filesize;
313   struct stat finfo;
314   int retcode, compressed;
315   FILE_BUFFER *file_buffer = NULL;
316 
317   /* Get the full pathname of this file, as known by the info system.
318      That is to say, search along INFOPATH and expand tildes, etc. */
319   fullpath = info_find_fullpath (filename);
320 
321   /* Did we actually find the file? */
322   retcode = stat (fullpath, &finfo);
323 
324   /* If the file referenced by the name returned from info_find_fullpath ()
325      doesn't exist, then try again with the last part of the filename
326      appearing in lowercase. */
327   /* This is probably not needed at all on those systems which define
328      FILENAME_CMP to be strcasecmp.  But let's do it anyway, lest some
329      network redirector supports case sensitivity.  */
330   if (retcode < 0)
331     {
332       char *lowered_name;
333       char *tmp_basename;
334 
335       lowered_name = xstrdup (filename);
336       tmp_basename = filename_non_directory (lowered_name);
337 
338       while (*tmp_basename)
339         {
340           if (isupper (*tmp_basename))
341             *tmp_basename = tolower (*tmp_basename);
342 
343           tmp_basename++;
344         }
345 
346       fullpath = info_find_fullpath (lowered_name);
347 
348       retcode = stat (fullpath, &finfo);
349       free (lowered_name);
350     }
351 
352   /* If the file wasn't found, give up, returning a NULL pointer. */
353   if (retcode < 0)
354     {
355       filesys_error_number = errno;
356       return NULL;
357     }
358 
359   /* Otherwise, try to load the file. */
360   contents = filesys_read_info_file (fullpath, &filesize, &finfo, &compressed);
361 
362   if (!contents)
363     return NULL;
364 
365   /* The file was found, and can be read.  Allocate FILE_BUFFER and fill
366      in the various members. */
367   file_buffer = make_file_buffer ();
368   file_buffer->filename = xstrdup (filename);
369   file_buffer->fullpath = xstrdup (fullpath);
370   file_buffer->finfo = finfo;
371   file_buffer->filesize = filesize;
372   file_buffer->contents = contents;
373   if (compressed)
374     file_buffer->flags |= N_IsCompressed;
375 
376   /* If requested, build the tags and nodes for this file buffer. */
377   if (get_tags)
378     build_tags_and_nodes (file_buffer);
379 
380   return file_buffer;
381 }
382 
383 /* Grovel FILE_BUFFER->contents finding tags and nodes, and filling in the
384    various slots.  This can also be used to rebuild a tag or node table. */
385 void
build_tags_and_nodes(FILE_BUFFER * file_buffer)386 build_tags_and_nodes (FILE_BUFFER *file_buffer)
387 {
388   SEARCH_BINDING binding;
389   long position;
390 
391   free_file_buffer_tags (file_buffer);
392   file_buffer->flags &= ~N_HasTagsTable;
393 
394   /* See if there is a tags table in this info file. */
395   binding.buffer = file_buffer->contents;
396   binding.start = file_buffer->filesize;
397   binding.end = binding.start - 1000;
398   if (binding.end < 0)
399     binding.end = 0;
400   binding.flags = S_FoldCase;
401 
402   position = search_backward (TAGS_TABLE_END_LABEL, &binding);
403 
404   /* If there is a tag table, find the start of it, and grovel over it
405      extracting tag information. */
406   if (position != -1)
407     while (1)
408       {
409         long tags_table_begin, tags_table_end;
410 
411         binding.end = position;
412         binding.start = binding.end - 5 - strlen (TAGS_TABLE_END_LABEL);
413         if (binding.start < 0)
414           binding.start = 0;
415 
416         position = find_node_separator (&binding);
417 
418         /* For this test, (and all others here) failure indicates a bogus
419            tags table.  Grovel the file. */
420         if (position == -1)
421           break;
422 
423         /* Remember the end of the tags table. */
424         binding.start = position;
425         tags_table_end = binding.start;
426         binding.end = 0;
427 
428         /* Locate the start of the tags table. */
429         position = search_backward (TAGS_TABLE_BEG_LABEL, &binding);
430 
431         if (position == -1)
432           break;
433 
434         binding.end = position;
435         binding.start = binding.end - 5 - strlen (TAGS_TABLE_BEG_LABEL);
436         position = find_node_separator (&binding);
437 
438         if (position == -1)
439           break;
440 
441         /* The file contains a valid tags table.  Fill the FILE_BUFFER's
442            tags member. */
443         file_buffer->flags |= N_HasTagsTable;
444         tags_table_begin = position;
445 
446         /* If this isn't an indirect tags table, just remember the nodes
447            described locally in this tags table.  Note that binding.end
448            is pointing to just after the beginning label. */
449         binding.start = binding.end;
450         binding.end = file_buffer->filesize;
451 
452         if (!looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, &binding))
453           {
454             binding.start = tags_table_begin;
455             binding.end = tags_table_end;
456             get_nodes_of_tags_table (file_buffer, &binding);
457             return;
458           }
459         else
460           {
461             /* This is an indirect tags table.  Build TAGS member. */
462             SEARCH_BINDING indirect;
463 
464             indirect.start = tags_table_begin;
465             indirect.end = 0;
466             indirect.buffer = binding.buffer;
467             indirect.flags = S_FoldCase;
468 
469             position = search_backward (INDIRECT_TAGS_TABLE_LABEL, &indirect);
470 
471             if (position == -1)
472               {
473                 /* This file is malformed.  Give up. */
474                 return;
475               }
476 
477             indirect.start = position;
478             indirect.end = tags_table_begin;
479             binding.start = tags_table_begin;
480             binding.end = tags_table_end;
481             get_tags_of_indirect_tags_table (file_buffer, &indirect, &binding);
482             return;
483           }
484       }
485 
486   /* This file doesn't contain any kind of tags table.  Grovel the
487      file and build node entries for it. */
488   get_nodes_of_info_file (file_buffer);
489 }
490 
491 /* Search through FILE_BUFFER->contents building an array of TAG *,
492    one entry per each node present in the file.  Store the tags in
493    FILE_BUFFER->tags, and the number of allocated slots in
494    FILE_BUFFER->tags_slots. */
495 static void
get_nodes_of_info_file(FILE_BUFFER * file_buffer)496 get_nodes_of_info_file (FILE_BUFFER *file_buffer)
497 {
498   long nodestart;
499   int tags_index = 0;
500   SEARCH_BINDING binding;
501 
502   binding.buffer = file_buffer->contents;
503   binding.start = 0;
504   binding.end = file_buffer->filesize;
505   binding.flags = S_FoldCase;
506 
507   while ((nodestart = find_node_separator (&binding)) != -1)
508     {
509       int start, end;
510       char *nodeline;
511       TAG *entry;
512       int anchor = 0;
513 
514       /* Skip past the characters just found. */
515       binding.start = nodestart;
516       binding.start += skip_node_separator (binding.buffer + binding.start);
517 
518       /* Move to the start of the line defining the node. */
519       nodeline = binding.buffer + binding.start;
520 
521       /* Find "Node:" */
522       start = string_in_line (INFO_NODE_LABEL, nodeline);
523       /* No Node:.  Maybe it's a Ref:.  */
524       if (start == -1)
525         {
526           start = string_in_line (INFO_REF_LABEL, nodeline);
527           if (start != -1)
528             anchor = 1;
529         }
530 
531       /* If not there, this is not the start of a node. */
532       if (start == -1)
533         continue;
534 
535       /* Find the start of the nodename. */
536       start += skip_whitespace (nodeline + start);
537 
538       /* Find the end of the nodename. */
539       end = start +
540         skip_node_characters (nodeline + start, DONT_SKIP_NEWLINES);
541 
542       /* Okay, we have isolated the node name, and we know where the
543          node starts.  Remember this information. */
544       entry = xmalloc (sizeof (TAG));
545       entry->nodename = xmalloc (1 + (end - start));
546       strncpy (entry->nodename, nodeline + start, end - start);
547       entry->nodename[end - start] = 0;
548       entry->nodestart = nodestart;
549       if (anchor)
550         entry->nodelen = 0;
551       else
552         {
553           SEARCH_BINDING node_body;
554 
555           node_body.buffer = binding.buffer + binding.start;
556           node_body.start = 0;
557           node_body.end = binding.end - binding.start;
558           node_body.flags = S_FoldCase;
559           entry->nodelen = get_node_length (&node_body);
560         }
561 
562       entry->filename = file_buffer->fullpath;
563 
564       /* Add this tag to the array of tag structures in this FILE_BUFFER. */
565       add_pointer_to_array (entry, tags_index, file_buffer->tags,
566                             file_buffer->tags_slots, 100, TAG *);
567     }
568 }
569 
570 /* Return the length of the node which starts at BINDING. */
571 static long
get_node_length(SEARCH_BINDING * binding)572 get_node_length (SEARCH_BINDING *binding)
573 {
574   int i;
575   char *body;
576 
577   /* [A node] ends with either a ^_, a ^L, or end of file.  */
578   for (i = binding->start, body = binding->buffer; i < binding->end; i++)
579     {
580       if (body[i] == INFO_FF || body[i] == INFO_COOKIE)
581         break;
582     }
583   return i - binding->start;
584 }
585 
586 /* Build and save the array of nodes in FILE_BUFFER by searching through the
587    contents of BUFFER_BINDING for a tags table, and groveling the contents. */
588 static void
get_nodes_of_tags_table(FILE_BUFFER * file_buffer,SEARCH_BINDING * buffer_binding)589 get_nodes_of_tags_table (FILE_BUFFER *file_buffer,
590     SEARCH_BINDING *buffer_binding)
591 {
592   int name_offset;
593   SEARCH_BINDING *tmp_search;
594   long position;
595   int tags_index = 0;
596 
597   tmp_search = copy_binding (buffer_binding);
598 
599   /* Find the start of the tags table. */
600   position = find_tags_table (tmp_search);
601 
602   /* If none, we're all done. */
603   if (position == -1)
604     return;
605 
606   /* Move to one character before the start of the actual table. */
607   tmp_search->start = position;
608   tmp_search->start += skip_node_separator
609     (tmp_search->buffer + tmp_search->start);
610   tmp_search->start += strlen (TAGS_TABLE_BEG_LABEL);
611   tmp_search->start--;
612 
613   /* The tag table consists of lines containing node names and positions.
614      Do each line until we find one that doesn't contain a node name. */
615   while ((position = search_forward ("\n", tmp_search)) != -1)
616     {
617       TAG *entry;
618       char *nodedef;
619       unsigned p;
620       int anchor = 0;
621 
622       /* Prepare to skip this line. */
623       tmp_search->start = position;
624       tmp_search->start++;
625 
626       /* Skip past informative "(Indirect)" tags table line. */
627       if (!tags_index && looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, tmp_search))
628         continue;
629 
630       /* Find the label preceding the node name. */
631       name_offset =
632         string_in_line (INFO_NODE_LABEL, tmp_search->buffer + tmp_search->start);
633 
634       /* If no node label, maybe it's an anchor.  */
635       if (name_offset == -1)
636         {
637           name_offset = string_in_line (INFO_REF_LABEL,
638               tmp_search->buffer + tmp_search->start);
639           if (name_offset != -1)
640             anchor = 1;
641         }
642 
643       /* If not there, not a defining line, so we must be out of the
644          tags table.  */
645       if (name_offset == -1)
646         break;
647 
648       entry = xmalloc (sizeof (TAG));
649 
650       /* Find the beginning of the node definition. */
651       tmp_search->start += name_offset;
652       nodedef = tmp_search->buffer + tmp_search->start;
653       nodedef += skip_whitespace (nodedef);
654 
655       /* Move past the node's name in this tag to the TAGSEP character. */
656       for (p = 0; nodedef[p] && nodedef[p] != INFO_TAGSEP; p++)
657         ;
658       if (nodedef[p] != INFO_TAGSEP)
659         continue;
660 
661       entry->nodename = xmalloc (p + 1);
662       strncpy (entry->nodename, nodedef, p);
663       entry->nodename[p] = 0;
664       p++;
665       entry->nodestart = atol (nodedef + p);
666 
667       /* If a node, we don't know the length yet, but if it's an
668          anchor, the length is 0. */
669       entry->nodelen = anchor ? 0 : -1;
670 
671       /* The filename of this node is currently known as the same as the
672          name of this file. */
673       entry->filename = file_buffer->fullpath;
674 
675       /* Add this node structure to the array of node structures in this
676          FILE_BUFFER. */
677       add_pointer_to_array (entry, tags_index, file_buffer->tags,
678                             file_buffer->tags_slots, 100, TAG *);
679     }
680   free (tmp_search);
681 }
682 
683 /* A structure used only in `get_tags_of_indirect_tags_table' to hold onto
684    an intermediate value. */
685 typedef struct {
686   char *filename;
687   long first_byte;
688 } SUBFILE;
689 
690 /* Remember in FILE_BUFFER the nodenames, subfilenames, and offsets within the
691    subfiles of every node which appears in TAGS_BINDING.  The 2nd argument is
692    a binding surrounding the indirect files list. */
693 static void
get_tags_of_indirect_tags_table(FILE_BUFFER * file_buffer,SEARCH_BINDING * indirect_binding,SEARCH_BINDING * tags_binding)694 get_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer,
695     SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding)
696 {
697   int i;
698   SUBFILE **subfiles = NULL;
699   int subfiles_index = 0, subfiles_slots = 0;
700   TAG *entry;
701 
702   /* First get the list of tags from the tags table.  Then lookup the
703      associated file in the indirect list for each tag, and update it. */
704   get_nodes_of_tags_table (file_buffer, tags_binding);
705 
706   /* We have the list of tags in file_buffer->tags.  Get the list of
707      subfiles from the indirect table. */
708   {
709     char *start, *end, *line;
710     SUBFILE *subfile;
711 
712     start = indirect_binding->buffer + indirect_binding->start;
713     end = indirect_binding->buffer + indirect_binding->end;
714     line = start;
715 
716     while (line < end)
717       {
718         int colon;
719 
720         colon = string_in_line (":", line);
721 
722         if (colon == -1)
723           break;
724 
725         subfile = (SUBFILE *)xmalloc (sizeof (SUBFILE));
726         subfile->filename = (char *)xmalloc (colon);
727         strncpy (subfile->filename, line, colon - 1);
728         subfile->filename[colon - 1] = 0;
729         subfile->first_byte = (long) atol (line + colon);
730 
731         add_pointer_to_array
732           (subfile, subfiles_index, subfiles, subfiles_slots, 10, SUBFILE *);
733 
734         while (*line++ != '\n');
735       }
736   }
737 
738   /* If we have successfully built the indirect files table, then
739      merge the information in the two tables. */
740   if (!subfiles)
741     {
742       free_file_buffer_tags (file_buffer);
743       return;
744     }
745   else
746     {
747       int tags_index;
748       long header_length;
749       SEARCH_BINDING binding;
750 
751       /* Find the length of the header of the file containing the indirect
752          tags table.  This header appears at the start of every file.  We
753          want the absolute position of each node within each subfile, so
754          we subtract the start of the containing subfile from the logical
755          position of the node, and then add the length of the header in. */
756       binding.buffer = file_buffer->contents;
757       binding.start = 0;
758       binding.end = file_buffer->filesize;
759       binding.flags = S_FoldCase;
760 
761       header_length = find_node_separator (&binding);
762       if (header_length == -1)
763         header_length = 0;
764 
765       /* Build the file buffer's list of subfiles. */
766       {
767         char *containing_dir = xstrdup (file_buffer->fullpath);
768         char *temp = filename_non_directory (containing_dir);
769         int len_containing_dir;
770 
771         if (temp > containing_dir)
772           {
773             if (HAVE_DRIVE (file_buffer->fullpath) &&
774                 temp == containing_dir + 2)
775               {
776                 /* Avoid converting "d:foo" into "d:/foo" below.  */
777                 *temp = '.';
778                 temp += 2;
779               }
780             temp[-1] = 0;
781           }
782 
783         len_containing_dir = strlen (containing_dir);
784 
785         for (i = 0; subfiles[i]; i++);
786 
787         file_buffer->subfiles = (char **) xmalloc ((1 + i) * sizeof (char *));
788 
789         for (i = 0; subfiles[i]; i++)
790           {
791             char *fullpath;
792 
793             fullpath = (char *) xmalloc
794               (2 + strlen (subfiles[i]->filename) + len_containing_dir);
795 
796             sprintf (fullpath, "%s/%s",
797                      containing_dir, subfiles[i]->filename);
798 
799             file_buffer->subfiles[i] = fullpath;
800           }
801         file_buffer->subfiles[i] = NULL;
802         free (containing_dir);
803       }
804 
805       /* For each node in the file's tags table, remember the starting
806          position. */
807       for (tags_index = 0; (entry = file_buffer->tags[tags_index]);
808            tags_index++)
809         {
810           for (i = 0;
811                subfiles[i] && entry->nodestart >= subfiles[i]->first_byte;
812                i++);
813 
814           /* If the Info file containing the indirect tags table is
815              malformed, then give up. */
816           if (!i)
817             {
818               /* The Info file containing the indirect tags table is
819                  malformed.  Give up. */
820               for (i = 0; subfiles[i]; i++)
821                 {
822                   free (subfiles[i]->filename);
823                   free (subfiles[i]);
824                   free (file_buffer->subfiles[i]);
825                 }
826               file_buffer->subfiles = NULL;
827               free_file_buffer_tags (file_buffer);
828               return;
829             }
830 
831           /* SUBFILES[i] is the index of the first subfile whose logical
832              first byte is greater than the logical offset of this node's
833              starting position.  This means that the subfile directly
834              preceding this one is the one containing the node. */
835 
836           entry->filename = file_buffer->subfiles[i - 1];
837           entry->nodestart -= subfiles[i - 1]->first_byte;
838           entry->nodestart += header_length;
839         }
840 
841       /* We have successfully built the tags table.  Remember that it
842          was indirect. */
843       file_buffer->flags |= N_TagsIndirect;
844     }
845 
846   /* Free the structures assigned to SUBFILES.  Free the names as well
847      as the structures themselves, then finally, the array. */
848   for (i = 0; subfiles[i]; i++)
849     {
850       free (subfiles[i]->filename);
851       free (subfiles[i]);
852     }
853   free (subfiles);
854 }
855 
856 
857 /* Return the node that contains TAG in FILE_BUFFER, else
858    (pathologically) NULL.  Called from info_node_of_file_buffer_tags.  */
859 static NODE *
find_node_of_anchor(FILE_BUFFER * file_buffer,TAG * tag)860 find_node_of_anchor (FILE_BUFFER *file_buffer, TAG *tag)
861 {
862   int anchor_pos, node_pos;
863   TAG *node_tag;
864   NODE *node;
865 
866   /* Look through the tag list for the anchor.  */
867   for (anchor_pos = 0; file_buffer->tags[anchor_pos]; anchor_pos++)
868     {
869       TAG *t = file_buffer->tags[anchor_pos];
870       if (t->nodestart == tag->nodestart)
871         break;
872     }
873 
874   /* Should not happen, because we should always find the anchor.  */
875   if (!file_buffer->tags[anchor_pos])
876     return NULL;
877 
878   /* We've found the anchor.  Look backwards in the tag table for the
879      preceding node (we're assuming the tags are given in order),
880      skipping over any preceding anchors.  */
881   for (node_pos = anchor_pos - 1;
882        node_pos >= 0 && file_buffer->tags[node_pos]->nodelen == 0;
883        node_pos--)
884     ;
885 
886   /* An info file with an anchor before any nodes is pathological, but
887      it's possible, so don't crash.  */
888   if (node_pos < 0)
889     return NULL;
890 
891   /* We have the tag for the node that contained the anchor tag.  */
892   node_tag = file_buffer->tags[node_pos];
893 
894   /* Look up the node name in the tag table to get the actual node.
895      This is a recursive call, but it can't recurse again, because we
896      call it with a real node.  */
897   node = info_node_of_file_buffer_tags (file_buffer, node_tag->nodename);
898 
899   /* Start displaying the node at the anchor position.  */
900   if (node)
901     { /* The nodestart for real nodes is three characters before the `F'
902          in the `File:' line (a newline, the CTRL-_, and another
903          newline).  The nodestart for anchors is the actual position.
904          But we offset by only 2, rather than 3, because if an anchor is
905          at the beginning of a paragraph, it's nicer for it to end up on
906          the beginning of the first line of the paragraph rather than
907          the blank line before it.  (makeinfo has no way of knowing that
908          a paragraph is going to start, so we can't fix it there.)  */
909       node->display_pos = file_buffer->tags[anchor_pos]->nodestart
910                           - (node_tag->nodestart + 2);
911 
912       /* Otherwise an anchor at the end of a node ends up displaying at
913          the end of the last line of the node (way over on the right of
914          the screen), which looks wrong.  */
915       if (node->display_pos >= (unsigned long) node->nodelen)
916         node->display_pos = node->nodelen - 1;
917 
918       /* Don't search in the node for the xref text, it's not there.  */
919       node->flags |= N_FromAnchor;
920     }
921 
922   return node;
923 }
924 
925 
926 /* Return the node from FILE_BUFFER which matches NODENAME by searching
927    the tags table in FILE_BUFFER, or NULL.  */
928 static NODE *
info_node_of_file_buffer_tags(FILE_BUFFER * file_buffer,char * nodename)929 info_node_of_file_buffer_tags (FILE_BUFFER *file_buffer, char *nodename)
930 {
931   TAG *tag;
932   int i;
933 
934   /* If no tags at all (possibly a misformatted info file), quit.  */
935   if (!file_buffer->tags) {
936     return NULL;
937   }
938 
939   for (i = 0; (tag = file_buffer->tags[i]); i++)
940     if (strcmp (nodename, tag->nodename) == 0)
941       {
942         FILE_BUFFER *subfile = info_find_file_internal (tag->filename,
943                                                         INFO_NO_TAGS);
944         if (!subfile)
945           return NULL;
946 
947         if (!subfile->contents)
948           {
949             info_reload_file_buffer_contents (subfile);
950             if (!subfile->contents)
951               return NULL;
952           }
953 
954         /* If we were able to find this file and load it, then return
955            the node within it. */
956         {
957           NODE *node = xmalloc (sizeof (NODE));
958           node->filename    = subfile->fullpath;
959           node->parent      = NULL;
960           node->nodename    = tag->nodename;
961           node->contents    = subfile->contents + tag->nodestart;
962           node->display_pos = 0;
963           node->flags       = 0;
964 
965           if (file_buffer->flags & N_HasTagsTable)
966             {
967               node->flags |= N_HasTagsTable;
968 
969               if (file_buffer->flags & N_TagsIndirect)
970                 {
971                   node->flags |= N_TagsIndirect;
972                   node->parent = file_buffer->fullpath;
973                 }
974             }
975 
976           if (subfile->flags & N_IsCompressed)
977             node->flags |= N_IsCompressed;
978 
979           /* If TAG->nodelen hasn't been calculated yet, then we aren't
980              in a position to trust the entry pointer.  Adjust things so
981              that ENTRY->nodestart gets the exact address of the start of
982              the node separator which starts this node, and NODE->contents
983              gets the address of the line defining this node.  If we cannot
984              do that, the node isn't really here. */
985           if (tag->nodelen == -1)
986             {
987               int min, max;
988               char *node_sep;
989               SEARCH_BINDING node_body;
990               char *buff_end;
991 
992               min = max = DEFAULT_INFO_FUDGE;
993 
994               if (tag->nodestart < DEFAULT_INFO_FUDGE)
995                 min = tag->nodestart;
996 
997               if (DEFAULT_INFO_FUDGE >
998                   (subfile->filesize - tag->nodestart))
999                 max = subfile->filesize - tag->nodestart;
1000 
1001               /* NODE_SEP gets the address of the separator which defines
1002                  this node, or NULL if the node wasn't found.
1003                  NODE->contents is side-effected to point to right after
1004                  the separator. */
1005               node_sep = adjust_nodestart (node, min, max);
1006               if (node_sep == NULL)
1007                 {
1008                   free (node);
1009                   return NULL;
1010                 }
1011               /* Readjust tag->nodestart. */
1012               tag->nodestart = node_sep - subfile->contents;
1013 
1014               /* Calculate the length of the current node. */
1015               buff_end = subfile->contents + subfile->filesize;
1016 
1017               node_body.buffer = node->contents;
1018               node_body.start = 0;
1019               node_body.end = buff_end - node_body.buffer;
1020               node_body.flags = 0;
1021               tag->nodelen = get_node_length (&node_body);
1022               node->nodelen = tag->nodelen;
1023             }
1024 
1025           else if (tag->nodelen == 0) /* anchor, return containing node */
1026             {
1027               free (node);
1028               node = find_node_of_anchor (file_buffer, tag);
1029             }
1030 
1031           else
1032             {
1033               /* Since we know the length of this node, we have already
1034                  adjusted tag->nodestart to point to the exact start of
1035                  it.  Simply skip the node separator. */
1036               node->contents += skip_node_separator (node->contents);
1037               node->nodelen = tag->nodelen;
1038             }
1039 
1040           return node;
1041         }
1042       }
1043 
1044   /* There was a tag table for this file, and the node wasn't found.
1045      Return NULL, since this file doesn't contain the desired node. */
1046   return NULL;
1047 }
1048 
1049 /* Managing file_buffers, nodes, and tags.  */
1050 
1051 /* Create a new, empty file buffer. */
1052 FILE_BUFFER *
make_file_buffer(void)1053 make_file_buffer (void)
1054 {
1055   FILE_BUFFER *file_buffer = xmalloc (sizeof (FILE_BUFFER));
1056 
1057   file_buffer->filename = file_buffer->fullpath = NULL;
1058   file_buffer->contents = NULL;
1059   file_buffer->tags = NULL;
1060   file_buffer->subfiles = NULL;
1061   file_buffer->tags_slots = 0;
1062   file_buffer->flags = 0;
1063 
1064   return file_buffer;
1065 }
1066 
1067 /* Add FILE_BUFFER to our list of already loaded info files. */
1068 static void
remember_info_file(FILE_BUFFER * file_buffer)1069 remember_info_file (FILE_BUFFER *file_buffer)
1070 {
1071   int i;
1072 
1073   for (i = 0; info_loaded_files && info_loaded_files[i]; i++)
1074     ;
1075 
1076   add_pointer_to_array (file_buffer, i, info_loaded_files,
1077                         info_loaded_files_slots, 10, FILE_BUFFER *);
1078 }
1079 
1080 /* Forget the contents, tags table, nodes list, and names of FILENAME. */
1081 static void
forget_info_file(char * filename)1082 forget_info_file (char *filename)
1083 {
1084   int i;
1085   FILE_BUFFER *file_buffer;
1086 
1087   if (!info_loaded_files)
1088     return;
1089 
1090   for (i = 0; (file_buffer = info_loaded_files[i]); i++)
1091     if (FILENAME_CMP (filename, file_buffer->filename) == 0
1092         || FILENAME_CMP (filename, file_buffer->fullpath) == 0)
1093       {
1094         free (file_buffer->filename);
1095         free (file_buffer->fullpath);
1096 
1097         if (file_buffer->contents)
1098           free (file_buffer->contents);
1099 
1100         /* free_file_buffer_tags () also kills the subfiles list, since
1101            the subfiles list is only of use in conjunction with tags. */
1102         free_file_buffer_tags (file_buffer);
1103 
1104         /* Move rest of list down.  */
1105         while (info_loaded_files[i + 1])
1106           {
1107             info_loaded_files[i] = info_loaded_files[i + 1];
1108             i++;
1109           }
1110         info_loaded_files[i] = 0;
1111 
1112         break;
1113       }
1114 }
1115 
1116 /* Free the tags (if any) associated with FILE_BUFFER. */
1117 static void
free_file_buffer_tags(FILE_BUFFER * file_buffer)1118 free_file_buffer_tags (FILE_BUFFER *file_buffer)
1119 {
1120   int i;
1121 
1122   if (file_buffer->tags)
1123     {
1124       TAG *tag;
1125 
1126       for (i = 0; (tag = file_buffer->tags[i]); i++)
1127         free_info_tag (tag);
1128 
1129       free (file_buffer->tags);
1130       file_buffer->tags = NULL;
1131       file_buffer->tags_slots = 0;
1132     }
1133 
1134   if (file_buffer->subfiles)
1135     {
1136       for (i = 0; file_buffer->subfiles[i]; i++)
1137         free (file_buffer->subfiles[i]);
1138 
1139       free (file_buffer->subfiles);
1140       file_buffer->subfiles = NULL;
1141     }
1142 }
1143 
1144 /* Free the data associated with TAG, as well as TAG itself. */
1145 static void
free_info_tag(TAG * tag)1146 free_info_tag (TAG *tag)
1147 {
1148   free (tag->nodename);
1149 
1150   /* We don't free tag->filename, because that filename is part of the
1151      subfiles list for the containing FILE_BUFFER.  free_info_tags ()
1152      will free the subfiles when it is appropriate. */
1153 
1154   free (tag);
1155 }
1156 
1157 /* Load the contents of FILE_BUFFER->contents.  This function is called
1158    when a file buffer was loaded, and then in order to conserve memory, the
1159    file buffer's contents were freed and the pointer was zero'ed.  Note that
1160    the file was already loaded at least once successfully, so the tags and/or
1161    nodes members are still correctly filled. */
1162 static void
info_reload_file_buffer_contents(FILE_BUFFER * fb)1163 info_reload_file_buffer_contents (FILE_BUFFER *fb)
1164 {
1165   int is_compressed;
1166 
1167 #if defined (HANDLE_MAN_PAGES)
1168   /* If this is the magic manpage node, don't try to reload, just give up. */
1169   if (fb->flags & N_IsManPage)
1170     return;
1171 #endif
1172 
1173   fb->flags &= ~N_IsCompressed;
1174 
1175   /* Let the filesystem do all the work for us. */
1176   fb->contents =
1177     filesys_read_info_file (fb->fullpath, &(fb->filesize), &(fb->finfo),
1178                             &is_compressed);
1179   if (is_compressed)
1180     fb->flags |= N_IsCompressed;
1181 }
1182 
1183 /* Return the actual starting memory location of NODE, side-effecting
1184    NODE->contents.  MIN and MAX are bounds for a search if one is necessary.
1185    Because of the way that tags are implemented, the physical nodestart may
1186    not actually be where the tag says it is.  If that is the case, but the
1187    node was found anyway, set N_UpdateTags in NODE->flags.  If the node is
1188    found, return non-zero.  NODE->contents is returned positioned right after
1189    the node separator that precedes this node, while the return value is
1190    position directly on the separator that precedes this node.  If the node
1191    could not be found, return a NULL pointer. */
1192 static char *
adjust_nodestart(NODE * node,int min,int max)1193 adjust_nodestart (NODE *node, int min, int max)
1194 {
1195   long position;
1196   SEARCH_BINDING node_body;
1197 
1198   /* Define the node body. */
1199   node_body.buffer = node->contents;
1200   node_body.start = 0;
1201   node_body.end = max;
1202   node_body.flags = 0;
1203 
1204   /* Try the optimal case first.  Who knows?  This file may actually be
1205      formatted (mostly) correctly. */
1206   if (node_body.buffer[0] != INFO_COOKIE && min > 2)
1207     node_body.buffer -= 3;
1208 
1209   position = find_node_separator (&node_body);
1210 
1211   /* If we found a node start, then check it out. */
1212   if (position != -1)
1213     {
1214       int sep_len;
1215 
1216       sep_len = skip_node_separator (node->contents);
1217 
1218       /* If we managed to skip a node separator, then check for this node
1219          being the right one. */
1220       if (sep_len != 0)
1221         {
1222           char *nodedef, *nodestart;
1223           int offset;
1224 
1225           nodestart = node_body.buffer + position + sep_len;
1226           nodedef = nodestart;
1227           offset = string_in_line (INFO_NODE_LABEL, nodedef);
1228 
1229           if (offset != -1)
1230             {
1231               nodedef += offset;
1232               nodedef += skip_whitespace (nodedef);
1233               offset = skip_node_characters (nodedef, DONT_SKIP_NEWLINES);
1234               if (((unsigned int) offset == strlen (node->nodename)) &&
1235                   (strncmp (node->nodename, nodedef, offset) == 0))
1236                 {
1237                   node->contents = nodestart;
1238                   return node_body.buffer + position;
1239                 }
1240             }
1241         }
1242     }
1243 
1244   /* Oh well, I guess we have to try to find it in a larger area. */
1245   node_body.buffer = node->contents - min;
1246   node_body.start = 0;
1247   node_body.end = min + max;
1248   node_body.flags = 0;
1249 
1250   position = find_node_in_binding (node->nodename, &node_body);
1251 
1252   /* If the node couldn't be found, we lose big. */
1253   if (position == -1)
1254     return NULL;
1255 
1256   /* Otherwise, the node was found, but the tags table could need updating
1257      (if we used a tag to get here, that is).  Set the flag in NODE->flags. */
1258   node->contents = node_body.buffer + position;
1259   node->contents += skip_node_separator (node->contents);
1260   if (node->flags & N_HasTagsTable)
1261     node->flags |= N_UpdateTags;
1262   return node_body.buffer + position;
1263 }
1264