Merge from vendor branch SENDMAIL:
[dragonfly.git] / contrib / texinfo-4 / info / nodes.c
1 /* nodes.c -- how to get an Info file and node.
2    $Id: nodes.c,v 1.4 2004/04/11 17:56:46 karl 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 \f
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 \f
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 *
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 *
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 *
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 *
203 info_load_file (char *filename)
204 {
205   return info_load_file_internal (filename, INFO_GET_TAGS);
206 }
207
208 \f
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 *
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 *
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 \f
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
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
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
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
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
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 *
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 *
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 \f
1049 /* Managing file_buffers, nodes, and tags.  */
1050
1051 /* Create a new, empty file buffer. */
1052 FILE_BUFFER *
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
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
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
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
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
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 *
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 }