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