gcc41: update README.DELETED
[dragonfly.git] / contrib / texinfo-4 / info / nodes.c
bloba3381a831451c42d811e93b692967999c02cb111
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 $
4 Copyright (C) 1993, 1998, 1999, 2000, 2002, 2003, 2004 Free Software
5 Foundation, Inc.
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.
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.
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.
21 Originally written by Brian Fox (bfox@ai.mit.edu). */
23 #include "info.h"
25 #include "nodes.h"
26 #include "search.h"
27 #include "filesys.h"
28 #include "info-utils.h"
30 #if defined (HANDLE_MAN_PAGES)
31 # include "man.h"
32 #endif /* HANDLE_MAN_PAGES */
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);
50 static long get_node_length (SEARCH_BINDING *binding);
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
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
61 /* Global variables. */
63 /* When non-zero, this is a string describing the recent file error. */
64 char *info_recent_file_error = NULL;
66 /* The list of already loaded nodes. */
67 FILE_BUFFER **info_loaded_files = NULL;
69 /* The number of slots currently allocated to LOADED_FILES. */
70 int info_loaded_files_slots = 0;
72 /* Public functions for node manipulation. */
74 /* Used to build `dir' menu from `localdir' files found in INFOPATH. */
75 extern void maybe_build_dir_node (char *dirname);
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)
84 NODE *node;
85 FILE_BUFFER *file_buffer = NULL;
87 info_recent_file_error = NULL;
88 info_parse_node (nodename, DONT_SKIP_NEWLINES);
89 nodename = NULL;
91 if (info_parsed_filename)
92 filename = info_parsed_filename;
94 if (info_parsed_nodename)
95 nodename = info_parsed_nodename;
97 /* If FILENAME is not specified, it defaults to "dir". */
98 if (!filename)
99 filename = "dir";
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);
106 /* Find the correct info file, or give up. */
107 file_buffer = info_find_file (filename);
108 if (!file_buffer)
110 if (filesys_error_number)
111 info_recent_file_error =
112 filesys_error_string (filename, filesys_error_number);
113 return NULL;
116 /* Look for the node. */
117 node = info_get_node_of_file_buffer (nodename, file_buffer);
119 /* If the node not found was "Top", try again with different case. */
120 if (!node && (nodename == NULL || strcasecmp (nodename, "Top") == 0))
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);
129 return node;
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)
139 NODE *node = NULL;
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;
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);
150 /* If NODENAME is not specified, it defaults to "Top". */
151 if (!nodename)
152 nodename = "Top";
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)
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;
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)
173 node = get_manpage_node (file_buffer, nodename);
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)
182 node = info_node_of_file_buffer_tags (file_buffer, nodename);
185 /* Return the results of our node search. */
186 return node;
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)
197 return info_find_file_internal (filename, INFO_GET_TAGS);
200 /* Load the info file FILENAME, remembering information about it in a
201 file buffer. */
202 FILE_BUFFER *
203 info_load_file (char *filename)
205 return info_load_file_internal (filename, INFO_GET_TAGS);
209 /* Private functions implementation. */
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)
219 int i;
220 FILE_BUFFER *file_buffer;
222 /* First try to find the file in our list of already loaded files. */
223 if (info_loaded_files)
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))
233 struct stat new_info, *old_info;
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;
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 */
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)
250 filesys_error_number = errno;
251 return NULL;
254 old_info = &file_buffer->finfo;
256 if (new_info.st_size != old_info->st_size
257 || new_info.st_mtime != old_info->st_mtime)
259 /* The file has changed. Forget that we ever had loaded it
260 in the first place. */
261 forget_info_file (filename);
262 break;
264 else
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)
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;
279 if (get_tags && !file_buffer->tags)
280 build_tags_and_nodes (file_buffer);
282 return file_buffer;
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);
297 /* If the file was loaded, remember the name under which it was found. */
298 if (file_buffer)
299 remember_info_file (file_buffer);
301 return file_buffer;
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)
311 char *fullpath, *contents;
312 long filesize;
313 struct stat finfo;
314 int retcode, compressed;
315 FILE_BUFFER *file_buffer = NULL;
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);
321 /* Did we actually find the file? */
322 retcode = stat (fullpath, &finfo);
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)
332 char *lowered_name;
333 char *tmp_basename;
335 lowered_name = xstrdup (filename);
336 tmp_basename = filename_non_directory (lowered_name);
338 while (*tmp_basename)
340 if (isupper (*tmp_basename))
341 *tmp_basename = tolower (*tmp_basename);
343 tmp_basename++;
346 fullpath = info_find_fullpath (lowered_name);
348 retcode = stat (fullpath, &finfo);
349 free (lowered_name);
352 /* If the file wasn't found, give up, returning a NULL pointer. */
353 if (retcode < 0)
355 filesys_error_number = errno;
356 return NULL;
359 /* Otherwise, try to load the file. */
360 contents = filesys_read_info_file (fullpath, &filesize, &finfo, &compressed);
362 if (!contents)
363 return NULL;
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;
376 /* If requested, build the tags and nodes for this file buffer. */
377 if (get_tags)
378 build_tags_and_nodes (file_buffer);
380 return file_buffer;
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)
388 SEARCH_BINDING binding;
389 long position;
391 free_file_buffer_tags (file_buffer);
392 file_buffer->flags &= ~N_HasTagsTable;
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;
402 position = search_backward (TAGS_TABLE_END_LABEL, &binding);
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)
409 long tags_table_begin, tags_table_end;
411 binding.end = position;
412 binding.start = binding.end - 5 - strlen (TAGS_TABLE_END_LABEL);
413 if (binding.start < 0)
414 binding.start = 0;
416 position = find_node_separator (&binding);
418 /* For this test, (and all others here) failure indicates a bogus
419 tags table. Grovel the file. */
420 if (position == -1)
421 break;
423 /* Remember the end of the tags table. */
424 binding.start = position;
425 tags_table_end = binding.start;
426 binding.end = 0;
428 /* Locate the start of the tags table. */
429 position = search_backward (TAGS_TABLE_BEG_LABEL, &binding);
431 if (position == -1)
432 break;
434 binding.end = position;
435 binding.start = binding.end - 5 - strlen (TAGS_TABLE_BEG_LABEL);
436 position = find_node_separator (&binding);
438 if (position == -1)
439 break;
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;
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;
452 if (!looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, &binding))
454 binding.start = tags_table_begin;
455 binding.end = tags_table_end;
456 get_nodes_of_tags_table (file_buffer, &binding);
457 return;
459 else
461 /* This is an indirect tags table. Build TAGS member. */
462 SEARCH_BINDING indirect;
464 indirect.start = tags_table_begin;
465 indirect.end = 0;
466 indirect.buffer = binding.buffer;
467 indirect.flags = S_FoldCase;
469 position = search_backward (INDIRECT_TAGS_TABLE_LABEL, &indirect);
471 if (position == -1)
473 /* This file is malformed. Give up. */
474 return;
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;
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);
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)
498 long nodestart;
499 int tags_index = 0;
500 SEARCH_BINDING binding;
502 binding.buffer = file_buffer->contents;
503 binding.start = 0;
504 binding.end = file_buffer->filesize;
505 binding.flags = S_FoldCase;
507 while ((nodestart = find_node_separator (&binding)) != -1)
509 int start, end;
510 char *nodeline;
511 TAG *entry;
512 int anchor = 0;
514 /* Skip past the characters just found. */
515 binding.start = nodestart;
516 binding.start += skip_node_separator (binding.buffer + binding.start);
518 /* Move to the start of the line defining the node. */
519 nodeline = binding.buffer + binding.start;
521 /* Find "Node:" */
522 start = string_in_line (INFO_NODE_LABEL, nodeline);
523 /* No Node:. Maybe it's a Ref:. */
524 if (start == -1)
526 start = string_in_line (INFO_REF_LABEL, nodeline);
527 if (start != -1)
528 anchor = 1;
531 /* If not there, this is not the start of a node. */
532 if (start == -1)
533 continue;
535 /* Find the start of the nodename. */
536 start += skip_whitespace (nodeline + start);
538 /* Find the end of the nodename. */
539 end = start +
540 skip_node_characters (nodeline + start, DONT_SKIP_NEWLINES);
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
553 SEARCH_BINDING node_body;
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);
562 entry->filename = file_buffer->fullpath;
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 *);
570 /* Return the length of the node which starts at BINDING. */
571 static long
572 get_node_length (SEARCH_BINDING *binding)
574 int i;
575 char *body;
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++)
580 if (body[i] == INFO_FF || body[i] == INFO_COOKIE)
581 break;
583 return i - binding->start;
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)
592 int name_offset;
593 SEARCH_BINDING *tmp_search;
594 long position;
595 int tags_index = 0;
597 tmp_search = copy_binding (buffer_binding);
599 /* Find the start of the tags table. */
600 position = find_tags_table (tmp_search);
602 /* If none, we're all done. */
603 if (position == -1)
604 return;
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--;
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)
617 TAG *entry;
618 char *nodedef;
619 unsigned p;
620 int anchor = 0;
622 /* Prepare to skip this line. */
623 tmp_search->start = position;
624 tmp_search->start++;
626 /* Skip past informative "(Indirect)" tags table line. */
627 if (!tags_index && looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, tmp_search))
628 continue;
630 /* Find the label preceding the node name. */
631 name_offset =
632 string_in_line (INFO_NODE_LABEL, tmp_search->buffer + tmp_search->start);
634 /* If no node label, maybe it's an anchor. */
635 if (name_offset == -1)
637 name_offset = string_in_line (INFO_REF_LABEL,
638 tmp_search->buffer + tmp_search->start);
639 if (name_offset != -1)
640 anchor = 1;
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;
648 entry = xmalloc (sizeof (TAG));
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);
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++)
658 if (nodedef[p] != INFO_TAGSEP)
659 continue;
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);
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;
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;
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 *);
680 free (tmp_search);
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;
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)
697 int i;
698 SUBFILE **subfiles = NULL;
699 int subfiles_index = 0, subfiles_slots = 0;
700 TAG *entry;
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);
706 /* We have the list of tags in file_buffer->tags. Get the list of
707 subfiles from the indirect table. */
709 char *start, *end, *line;
710 SUBFILE *subfile;
712 start = indirect_binding->buffer + indirect_binding->start;
713 end = indirect_binding->buffer + indirect_binding->end;
714 line = start;
716 while (line < end)
718 int colon;
720 colon = string_in_line (":", line);
722 if (colon == -1)
723 break;
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);
731 add_pointer_to_array
732 (subfile, subfiles_index, subfiles, subfiles_slots, 10, SUBFILE *);
734 while (*line++ != '\n');
738 /* If we have successfully built the indirect files table, then
739 merge the information in the two tables. */
740 if (!subfiles)
742 free_file_buffer_tags (file_buffer);
743 return;
745 else
747 int tags_index;
748 long header_length;
749 SEARCH_BINDING binding;
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;
761 header_length = find_node_separator (&binding);
762 if (header_length == -1)
763 header_length = 0;
765 /* Build the file buffer's list of subfiles. */
767 char *containing_dir = xstrdup (file_buffer->fullpath);
768 char *temp = filename_non_directory (containing_dir);
769 int len_containing_dir;
771 if (temp > containing_dir)
773 if (HAVE_DRIVE (file_buffer->fullpath) &&
774 temp == containing_dir + 2)
776 /* Avoid converting "d:foo" into "d:/foo" below. */
777 *temp = '.';
778 temp += 2;
780 temp[-1] = 0;
783 len_containing_dir = strlen (containing_dir);
785 for (i = 0; subfiles[i]; i++);
787 file_buffer->subfiles = (char **) xmalloc ((1 + i) * sizeof (char *));
789 for (i = 0; subfiles[i]; i++)
791 char *fullpath;
793 fullpath = (char *) xmalloc
794 (2 + strlen (subfiles[i]->filename) + len_containing_dir);
796 sprintf (fullpath, "%s/%s",
797 containing_dir, subfiles[i]->filename);
799 file_buffer->subfiles[i] = fullpath;
801 file_buffer->subfiles[i] = NULL;
802 free (containing_dir);
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++)
810 for (i = 0;
811 subfiles[i] && entry->nodestart >= subfiles[i]->first_byte;
812 i++);
814 /* If the Info file containing the indirect tags table is
815 malformed, then give up. */
816 if (!i)
818 /* The Info file containing the indirect tags table is
819 malformed. Give up. */
820 for (i = 0; subfiles[i]; i++)
822 free (subfiles[i]->filename);
823 free (subfiles[i]);
824 free (file_buffer->subfiles[i]);
826 file_buffer->subfiles = NULL;
827 free_file_buffer_tags (file_buffer);
828 return;
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. */
836 entry->filename = file_buffer->subfiles[i - 1];
837 entry->nodestart -= subfiles[i - 1]->first_byte;
838 entry->nodestart += header_length;
841 /* We have successfully built the tags table. Remember that it
842 was indirect. */
843 file_buffer->flags |= N_TagsIndirect;
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++)
850 free (subfiles[i]->filename);
851 free (subfiles[i]);
853 free (subfiles);
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)
862 int anchor_pos, node_pos;
863 TAG *node_tag;
864 NODE *node;
866 /* Look through the tag list for the anchor. */
867 for (anchor_pos = 0; file_buffer->tags[anchor_pos]; anchor_pos++)
869 TAG *t = file_buffer->tags[anchor_pos];
870 if (t->nodestart == tag->nodestart)
871 break;
874 /* Should not happen, because we should always find the anchor. */
875 if (!file_buffer->tags[anchor_pos])
876 return NULL;
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--)
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;
891 /* We have the tag for the node that contained the anchor tag. */
892 node_tag = file_buffer->tags[node_pos];
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);
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);
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;
918 /* Don't search in the node for the xref text, it's not there. */
919 node->flags |= N_FromAnchor;
922 return node;
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)
931 TAG *tag;
932 int i;
934 /* If no tags at all (possibly a misformatted info file), quit. */
935 if (!file_buffer->tags) {
936 return NULL;
939 for (i = 0; (tag = file_buffer->tags[i]); i++)
940 if (strcmp (nodename, tag->nodename) == 0)
942 FILE_BUFFER *subfile = info_find_file_internal (tag->filename,
943 INFO_NO_TAGS);
944 if (!subfile)
945 return NULL;
947 if (!subfile->contents)
949 info_reload_file_buffer_contents (subfile);
950 if (!subfile->contents)
951 return NULL;
954 /* If we were able to find this file and load it, then return
955 the node within it. */
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;
965 if (file_buffer->flags & N_HasTagsTable)
967 node->flags |= N_HasTagsTable;
969 if (file_buffer->flags & N_TagsIndirect)
971 node->flags |= N_TagsIndirect;
972 node->parent = file_buffer->fullpath;
976 if (subfile->flags & N_IsCompressed)
977 node->flags |= N_IsCompressed;
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)
987 int min, max;
988 char *node_sep;
989 SEARCH_BINDING node_body;
990 char *buff_end;
992 min = max = DEFAULT_INFO_FUDGE;
994 if (tag->nodestart < DEFAULT_INFO_FUDGE)
995 min = tag->nodestart;
997 if (DEFAULT_INFO_FUDGE >
998 (subfile->filesize - tag->nodestart))
999 max = subfile->filesize - tag->nodestart;
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)
1008 free (node);
1009 return NULL;
1011 /* Readjust tag->nodestart. */
1012 tag->nodestart = node_sep - subfile->contents;
1014 /* Calculate the length of the current node. */
1015 buff_end = subfile->contents + subfile->filesize;
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;
1025 else if (tag->nodelen == 0) /* anchor, return containing node */
1027 free (node);
1028 node = find_node_of_anchor (file_buffer, tag);
1031 else
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;
1040 return node;
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;
1049 /* Managing file_buffers, nodes, and tags. */
1051 /* Create a new, empty file buffer. */
1052 FILE_BUFFER *
1053 make_file_buffer (void)
1055 FILE_BUFFER *file_buffer = xmalloc (sizeof (FILE_BUFFER));
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;
1064 return file_buffer;
1067 /* Add FILE_BUFFER to our list of already loaded info files. */
1068 static void
1069 remember_info_file (FILE_BUFFER *file_buffer)
1071 int i;
1073 for (i = 0; info_loaded_files && info_loaded_files[i]; i++)
1076 add_pointer_to_array (file_buffer, i, info_loaded_files,
1077 info_loaded_files_slots, 10, FILE_BUFFER *);
1080 /* Forget the contents, tags table, nodes list, and names of FILENAME. */
1081 static void
1082 forget_info_file (char *filename)
1084 int i;
1085 FILE_BUFFER *file_buffer;
1087 if (!info_loaded_files)
1088 return;
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)
1094 free (file_buffer->filename);
1095 free (file_buffer->fullpath);
1097 if (file_buffer->contents)
1098 free (file_buffer->contents);
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);
1104 /* Move rest of list down. */
1105 while (info_loaded_files[i + 1])
1107 info_loaded_files[i] = info_loaded_files[i + 1];
1108 i++;
1110 info_loaded_files[i] = 0;
1112 break;
1116 /* Free the tags (if any) associated with FILE_BUFFER. */
1117 static void
1118 free_file_buffer_tags (FILE_BUFFER *file_buffer)
1120 int i;
1122 if (file_buffer->tags)
1124 TAG *tag;
1126 for (i = 0; (tag = file_buffer->tags[i]); i++)
1127 free_info_tag (tag);
1129 free (file_buffer->tags);
1130 file_buffer->tags = NULL;
1131 file_buffer->tags_slots = 0;
1134 if (file_buffer->subfiles)
1136 for (i = 0; file_buffer->subfiles[i]; i++)
1137 free (file_buffer->subfiles[i]);
1139 free (file_buffer->subfiles);
1140 file_buffer->subfiles = NULL;
1144 /* Free the data associated with TAG, as well as TAG itself. */
1145 static void
1146 free_info_tag (TAG *tag)
1148 free (tag->nodename);
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. */
1154 free (tag);
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)
1165 int is_compressed;
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
1173 fb->flags &= ~N_IsCompressed;
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;
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)
1195 long position;
1196 SEARCH_BINDING node_body;
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;
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;
1209 position = find_node_separator (&node_body);
1211 /* If we found a node start, then check it out. */
1212 if (position != -1)
1214 int sep_len;
1216 sep_len = skip_node_separator (node->contents);
1218 /* If we managed to skip a node separator, then check for this node
1219 being the right one. */
1220 if (sep_len != 0)
1222 char *nodedef, *nodestart;
1223 int offset;
1225 nodestart = node_body.buffer + position + sep_len;
1226 nodedef = nodestart;
1227 offset = string_in_line (INFO_NODE_LABEL, nodedef);
1229 if (offset != -1)
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))
1237 node->contents = nodestart;
1238 return node_body.buffer + position;
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;
1250 position = find_node_in_binding (node->nodename, &node_body);
1252 /* If the node couldn't be found, we lose big. */
1253 if (position == -1)
1254 return NULL;
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;