Sync usage with man page.
[netbsd-mini2440.git] / gnu / dist / texinfo / info / nodes.c
blob56fc0cf80a23dfc32ab77fd8179b9835ffc63f6e
1 /* $NetBSD: nodes.c,v 1.1.1.6 2008/09/02 07:49:50 christos Exp $ */
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
6 Copyright (C) 1993, 1998, 1999, 2000, 2002, 2003, 2004 Free Software
7 Foundation, Inc.
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.
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.
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.
23 Originally written by Brian Fox (bfox@ai.mit.edu). */
25 #include "info.h"
27 #include "nodes.h"
28 #include "search.h"
29 #include "filesys.h"
30 #include "info-utils.h"
32 #if defined (HANDLE_MAN_PAGES)
33 # include "man.h"
34 #endif /* HANDLE_MAN_PAGES */
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);
52 static long get_node_length (SEARCH_BINDING *binding);
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
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
63 /* Global variables. */
65 /* When non-zero, this is a string describing the recent file error. */
66 char *info_recent_file_error = NULL;
68 /* The list of already loaded nodes. */
69 FILE_BUFFER **info_loaded_files = NULL;
71 /* The number of slots currently allocated to LOADED_FILES. */
72 int info_loaded_files_slots = 0;
74 /* Public functions for node manipulation. */
76 /* Used to build `dir' menu from `localdir' files found in INFOPATH. */
77 extern void maybe_build_dir_node (char *dirname);
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 *
84 info_get_node (char *filename, char *nodename)
86 NODE *node;
87 FILE_BUFFER *file_buffer = NULL;
89 info_recent_file_error = NULL;
90 info_parse_node (nodename, DONT_SKIP_NEWLINES);
91 nodename = NULL;
93 if (info_parsed_filename)
94 filename = info_parsed_filename;
96 if (info_parsed_nodename)
97 nodename = info_parsed_nodename;
99 /* If FILENAME is not specified, it defaults to "dir". */
100 if (!filename)
101 filename = "dir";
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);
108 /* Find the correct info file, or give up. */
109 file_buffer = info_find_file (filename);
110 if (!file_buffer)
112 if (filesys_error_number)
113 info_recent_file_error =
114 filesys_error_string (filename, filesys_error_number);
115 return NULL;
118 /* Look for the node. */
119 node = info_get_node_of_file_buffer (nodename, file_buffer);
121 /* If the node not found was "Top", try again with different case. */
122 if (!node && (nodename == NULL || strcasecmp (nodename, "Top") == 0))
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);
131 return node;
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 *
139 info_get_node_of_file_buffer (char *nodename, FILE_BUFFER *file_buffer)
141 NODE *node = NULL;
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;
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);
152 /* If NODENAME is not specified, it defaults to "Top". */
153 if (!nodename)
154 nodename = "Top";
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)
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;
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)
175 node = get_manpage_node (file_buffer, nodename);
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)
184 node = info_node_of_file_buffer_tags (file_buffer, nodename);
187 /* Return the results of our node search. */
188 return node;
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 *
197 info_find_file (char *filename)
199 return info_find_file_internal (filename, INFO_GET_TAGS);
202 /* Load the info file FILENAME, remembering information about it in a
203 file buffer. */
204 FILE_BUFFER *
205 info_load_file (char *filename)
207 return info_load_file_internal (filename, INFO_GET_TAGS);
211 /* Private functions implementation. */
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 *
219 info_find_file_internal (char *filename, int get_tags)
221 int i;
222 FILE_BUFFER *file_buffer;
224 /* First try to find the file in our list of already loaded files. */
225 if (info_loaded_files)
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))
235 struct stat new_info, *old_info;
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;
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 */
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)
252 filesys_error_number = errno;
253 return NULL;
256 old_info = &file_buffer->finfo;
258 if (new_info.st_size != old_info->st_size
259 || new_info.st_mtime != old_info->st_mtime)
261 /* The file has changed. Forget that we ever had loaded it
262 in the first place. */
263 forget_info_file (filename);
264 break;
266 else
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)
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;
281 if (get_tags && !file_buffer->tags)
282 build_tags_and_nodes (file_buffer);
284 return file_buffer;
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);
299 /* If the file was loaded, remember the name under which it was found. */
300 if (file_buffer)
301 remember_info_file (file_buffer);
303 return file_buffer;
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 *
311 info_load_file_internal (char *filename, int get_tags)
313 char *fullpath, *contents;
314 long filesize;
315 struct stat finfo;
316 int retcode, compressed;
317 FILE_BUFFER *file_buffer = NULL;
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);
323 /* Did we actually find the file? */
324 retcode = stat (fullpath, &finfo);
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)
334 char *lowered_name;
335 char *tmp_basename;
337 lowered_name = xstrdup (filename);
338 tmp_basename = filename_non_directory (lowered_name);
340 while (*tmp_basename)
342 if (isupper (*tmp_basename))
343 *tmp_basename = tolower (*tmp_basename);
345 tmp_basename++;
348 fullpath = info_find_fullpath (lowered_name);
350 retcode = stat (fullpath, &finfo);
351 free (lowered_name);
354 /* If the file wasn't found, give up, returning a NULL pointer. */
355 if (retcode < 0)
357 filesys_error_number = errno;
358 return NULL;
361 /* Otherwise, try to load the file. */
362 contents = filesys_read_info_file (fullpath, &filesize, &finfo, &compressed);
364 if (!contents)
365 return NULL;
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;
378 /* If requested, build the tags and nodes for this file buffer. */
379 if (get_tags)
380 build_tags_and_nodes (file_buffer);
382 return file_buffer;
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
388 build_tags_and_nodes (FILE_BUFFER *file_buffer)
390 SEARCH_BINDING binding;
391 long position;
393 free_file_buffer_tags (file_buffer);
394 file_buffer->flags &= ~N_HasTagsTable;
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;
404 position = search_backward (TAGS_TABLE_END_LABEL, &binding);
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)
411 long tags_table_begin, tags_table_end;
413 binding.end = position;
414 binding.start = binding.end - 5 - strlen (TAGS_TABLE_END_LABEL);
415 if (binding.start < 0)
416 binding.start = 0;
418 position = find_node_separator (&binding);
420 /* For this test, (and all others here) failure indicates a bogus
421 tags table. Grovel the file. */
422 if (position == -1)
423 break;
425 /* Remember the end of the tags table. */
426 binding.start = position;
427 tags_table_end = binding.start;
428 binding.end = 0;
430 /* Locate the start of the tags table. */
431 position = search_backward (TAGS_TABLE_BEG_LABEL, &binding);
433 if (position == -1)
434 break;
436 binding.end = position;
437 binding.start = binding.end - 5 - strlen (TAGS_TABLE_BEG_LABEL);
438 position = find_node_separator (&binding);
440 if (position == -1)
441 break;
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;
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;
454 if (!looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, &binding))
456 binding.start = tags_table_begin;
457 binding.end = tags_table_end;
458 get_nodes_of_tags_table (file_buffer, &binding);
459 return;
461 else
463 /* This is an indirect tags table. Build TAGS member. */
464 SEARCH_BINDING indirect;
466 indirect.start = tags_table_begin;
467 indirect.end = 0;
468 indirect.buffer = binding.buffer;
469 indirect.flags = S_FoldCase;
471 position = search_backward (INDIRECT_TAGS_TABLE_LABEL, &indirect);
473 if (position == -1)
475 /* This file is malformed. Give up. */
476 return;
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;
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);
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
498 get_nodes_of_info_file (FILE_BUFFER *file_buffer)
500 long nodestart;
501 int tags_index = 0;
502 SEARCH_BINDING binding;
504 binding.buffer = file_buffer->contents;
505 binding.start = 0;
506 binding.end = file_buffer->filesize;
507 binding.flags = S_FoldCase;
509 while ((nodestart = find_node_separator (&binding)) != -1)
511 int start, end;
512 char *nodeline;
513 TAG *entry;
514 int anchor = 0;
516 /* Skip past the characters just found. */
517 binding.start = nodestart;
518 binding.start += skip_node_separator (binding.buffer + binding.start);
520 /* Move to the start of the line defining the node. */
521 nodeline = binding.buffer + binding.start;
523 /* Find "Node:" */
524 start = string_in_line (INFO_NODE_LABEL, nodeline);
525 /* No Node:. Maybe it's a Ref:. */
526 if (start == -1)
528 start = string_in_line (INFO_REF_LABEL, nodeline);
529 if (start != -1)
530 anchor = 1;
533 /* If not there, this is not the start of a node. */
534 if (start == -1)
535 continue;
537 /* Find the start of the nodename. */
538 start += skip_whitespace (nodeline + start);
540 /* Find the end of the nodename. */
541 end = start +
542 skip_node_characters (nodeline + start, DONT_SKIP_NEWLINES);
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
555 SEARCH_BINDING node_body;
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);
564 entry->filename = file_buffer->fullpath;
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 *);
572 /* Return the length of the node which starts at BINDING. */
573 static long
574 get_node_length (SEARCH_BINDING *binding)
576 int i;
577 char *body;
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++)
582 if (body[i] == INFO_FF || body[i] == INFO_COOKIE)
583 break;
585 return i - binding->start;
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
591 get_nodes_of_tags_table (FILE_BUFFER *file_buffer,
592 SEARCH_BINDING *buffer_binding)
594 int name_offset;
595 SEARCH_BINDING *tmp_search;
596 long position;
597 int tags_index = 0;
599 tmp_search = copy_binding (buffer_binding);
601 /* Find the start of the tags table. */
602 position = find_tags_table (tmp_search);
604 /* If none, we're all done. */
605 if (position == -1)
606 return;
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--;
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)
619 TAG *entry;
620 char *nodedef;
621 unsigned p;
622 int anchor = 0;
624 /* Prepare to skip this line. */
625 tmp_search->start = position;
626 tmp_search->start++;
628 /* Skip past informative "(Indirect)" tags table line. */
629 if (!tags_index && looking_at (TAGS_TABLE_IS_INDIRECT_LABEL, tmp_search))
630 continue;
632 /* Find the label preceding the node name. */
633 name_offset =
634 string_in_line (INFO_NODE_LABEL, tmp_search->buffer + tmp_search->start);
636 /* If no node label, maybe it's an anchor. */
637 if (name_offset == -1)
639 name_offset = string_in_line (INFO_REF_LABEL,
640 tmp_search->buffer + tmp_search->start);
641 if (name_offset != -1)
642 anchor = 1;
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;
650 entry = xmalloc (sizeof (TAG));
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);
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++)
660 if (nodedef[p] != INFO_TAGSEP)
661 continue;
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);
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;
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;
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 *);
682 free (tmp_search);
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;
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
696 get_tags_of_indirect_tags_table (FILE_BUFFER *file_buffer,
697 SEARCH_BINDING *indirect_binding, SEARCH_BINDING *tags_binding)
699 int i;
700 SUBFILE **subfiles = NULL;
701 int subfiles_index = 0, subfiles_slots = 0;
702 TAG *entry;
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);
708 /* We have the list of tags in file_buffer->tags. Get the list of
709 subfiles from the indirect table. */
711 char *start, *end, *line;
712 SUBFILE *subfile;
714 start = indirect_binding->buffer + indirect_binding->start;
715 end = indirect_binding->buffer + indirect_binding->end;
716 line = start;
718 while (line < end)
720 int colon;
722 colon = string_in_line (":", line);
724 if (colon == -1)
725 break;
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);
733 add_pointer_to_array
734 (subfile, subfiles_index, subfiles, subfiles_slots, 10, SUBFILE *);
736 while (*line++ != '\n');
740 /* If we have successfully built the indirect files table, then
741 merge the information in the two tables. */
742 if (!subfiles)
744 free_file_buffer_tags (file_buffer);
745 return;
747 else
749 int tags_index;
750 long header_length;
751 SEARCH_BINDING binding;
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;
763 header_length = find_node_separator (&binding);
764 if (header_length == -1)
765 header_length = 0;
767 /* Build the file buffer's list of subfiles. */
769 char *containing_dir = xstrdup (file_buffer->fullpath);
770 char *temp = filename_non_directory (containing_dir);
771 int len_containing_dir;
773 if (temp > containing_dir)
775 if (HAVE_DRIVE (file_buffer->fullpath) &&
776 temp == containing_dir + 2)
778 /* Avoid converting "d:foo" into "d:/foo" below. */
779 *temp = '.';
780 temp += 2;
782 temp[-1] = 0;
785 len_containing_dir = strlen (containing_dir);
787 for (i = 0; subfiles[i]; i++);
789 file_buffer->subfiles = (char **) xmalloc ((1 + i) * sizeof (char *));
791 for (i = 0; subfiles[i]; i++)
793 char *fullpath;
795 fullpath = (char *) xmalloc
796 (2 + strlen (subfiles[i]->filename) + len_containing_dir);
798 sprintf (fullpath, "%s/%s",
799 containing_dir, subfiles[i]->filename);
801 file_buffer->subfiles[i] = fullpath;
803 file_buffer->subfiles[i] = NULL;
804 free (containing_dir);
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++)
812 for (i = 0;
813 subfiles[i] && entry->nodestart >= subfiles[i]->first_byte;
814 i++);
816 /* If the Info file containing the indirect tags table is
817 malformed, then give up. */
818 if (!i)
820 /* The Info file containing the indirect tags table is
821 malformed. Give up. */
822 for (i = 0; subfiles[i]; i++)
824 free (subfiles[i]->filename);
825 free (subfiles[i]);
826 free (file_buffer->subfiles[i]);
828 file_buffer->subfiles = NULL;
829 free_file_buffer_tags (file_buffer);
830 return;
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. */
838 entry->filename = file_buffer->subfiles[i - 1];
839 entry->nodestart -= subfiles[i - 1]->first_byte;
840 entry->nodestart += header_length;
843 /* We have successfully built the tags table. Remember that it
844 was indirect. */
845 file_buffer->flags |= N_TagsIndirect;
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++)
852 free (subfiles[i]->filename);
853 free (subfiles[i]);
855 free (subfiles);
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 *
862 find_node_of_anchor (FILE_BUFFER *file_buffer, TAG *tag)
864 int anchor_pos, node_pos;
865 TAG *node_tag;
866 NODE *node;
868 /* Look through the tag list for the anchor. */
869 for (anchor_pos = 0; file_buffer->tags[anchor_pos]; anchor_pos++)
871 TAG *t = file_buffer->tags[anchor_pos];
872 if (t->nodestart == tag->nodestart)
873 break;
876 /* Should not happen, because we should always find the anchor. */
877 if (!file_buffer->tags[anchor_pos])
878 return NULL;
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--)
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;
893 /* We have the tag for the node that contained the anchor tag. */
894 node_tag = file_buffer->tags[node_pos];
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);
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);
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;
920 /* Don't search in the node for the xref text, it's not there. */
921 node->flags |= N_FromAnchor;
924 return node;
928 /* Return the node from FILE_BUFFER which matches NODENAME by searching
929 the tags table in FILE_BUFFER, or NULL. */
930 static NODE *
931 info_node_of_file_buffer_tags (FILE_BUFFER *file_buffer, char *nodename)
933 TAG *tag;
934 int i;
936 /* If no tags at all (possibly a misformatted info file), quit. */
937 if (!file_buffer->tags) {
938 return NULL;
941 for (i = 0; (tag = file_buffer->tags[i]); i++)
942 if (strcmp (nodename, tag->nodename) == 0)
944 FILE_BUFFER *subfile = info_find_file_internal (tag->filename,
945 INFO_NO_TAGS);
946 if (!subfile)
947 return NULL;
949 if (!subfile->contents)
951 info_reload_file_buffer_contents (subfile);
952 if (!subfile->contents)
953 return NULL;
956 /* If we were able to find this file and load it, then return
957 the node within it. */
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;
967 if (file_buffer->flags & N_HasTagsTable)
969 node->flags |= N_HasTagsTable;
971 if (file_buffer->flags & N_TagsIndirect)
973 node->flags |= N_TagsIndirect;
974 node->parent = file_buffer->fullpath;
978 if (subfile->flags & N_IsCompressed)
979 node->flags |= N_IsCompressed;
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)
989 int min, max;
990 char *node_sep;
991 SEARCH_BINDING node_body;
992 char *buff_end;
994 min = max = DEFAULT_INFO_FUDGE;
996 if (tag->nodestart < DEFAULT_INFO_FUDGE)
997 min = tag->nodestart;
999 if (DEFAULT_INFO_FUDGE >
1000 (subfile->filesize - tag->nodestart))
1001 max = subfile->filesize - tag->nodestart;
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)
1010 free (node);
1011 return NULL;
1013 /* Readjust tag->nodestart. */
1014 tag->nodestart = node_sep - subfile->contents;
1016 /* Calculate the length of the current node. */
1017 buff_end = subfile->contents + subfile->filesize;
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;
1027 else if (tag->nodelen == 0) /* anchor, return containing node */
1029 free (node);
1030 node = find_node_of_anchor (file_buffer, tag);
1033 else
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;
1042 return node;
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;
1051 /* Managing file_buffers, nodes, and tags. */
1053 /* Create a new, empty file buffer. */
1054 FILE_BUFFER *
1055 make_file_buffer (void)
1057 FILE_BUFFER *file_buffer = xmalloc (sizeof (FILE_BUFFER));
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;
1066 return file_buffer;
1069 /* Add FILE_BUFFER to our list of already loaded info files. */
1070 static void
1071 remember_info_file (FILE_BUFFER *file_buffer)
1073 int i;
1075 for (i = 0; info_loaded_files && info_loaded_files[i]; i++)
1078 add_pointer_to_array (file_buffer, i, info_loaded_files,
1079 info_loaded_files_slots, 10, FILE_BUFFER *);
1082 /* Forget the contents, tags table, nodes list, and names of FILENAME. */
1083 static void
1084 forget_info_file (char *filename)
1086 int i;
1087 FILE_BUFFER *file_buffer;
1089 if (!info_loaded_files)
1090 return;
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)
1096 free (file_buffer->filename);
1097 free (file_buffer->fullpath);
1099 if (file_buffer->contents)
1100 free (file_buffer->contents);
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);
1106 /* Move rest of list down. */
1107 while (info_loaded_files[i + 1])
1109 info_loaded_files[i] = info_loaded_files[i + 1];
1110 i++;
1112 info_loaded_files[i] = 0;
1114 break;
1118 /* Free the tags (if any) associated with FILE_BUFFER. */
1119 static void
1120 free_file_buffer_tags (FILE_BUFFER *file_buffer)
1122 int i;
1124 if (file_buffer->tags)
1126 TAG *tag;
1128 for (i = 0; (tag = file_buffer->tags[i]); i++)
1129 free_info_tag (tag);
1131 free (file_buffer->tags);
1132 file_buffer->tags = NULL;
1133 file_buffer->tags_slots = 0;
1136 if (file_buffer->subfiles)
1138 for (i = 0; file_buffer->subfiles[i]; i++)
1139 free (file_buffer->subfiles[i]);
1141 free (file_buffer->subfiles);
1142 file_buffer->subfiles = NULL;
1146 /* Free the data associated with TAG, as well as TAG itself. */
1147 static void
1148 free_info_tag (TAG *tag)
1150 free (tag->nodename);
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. */
1156 free (tag);
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
1165 info_reload_file_buffer_contents (FILE_BUFFER *fb)
1167 int is_compressed;
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
1175 fb->flags &= ~N_IsCompressed;
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;
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 *
1195 adjust_nodestart (NODE *node, int min, int max)
1197 long position;
1198 SEARCH_BINDING node_body;
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;
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;
1211 position = find_node_separator (&node_body);
1213 /* If we found a node start, then check it out. */
1214 if (position != -1)
1216 int sep_len;
1218 sep_len = skip_node_separator (node->contents);
1220 /* If we managed to skip a node separator, then check for this node
1221 being the right one. */
1222 if (sep_len != 0)
1224 char *nodedef, *nodestart;
1225 int offset;
1227 nodestart = node_body.buffer + position + sep_len;
1228 nodedef = nodestart;
1229 offset = string_in_line (INFO_NODE_LABEL, nodedef);
1231 if (offset != -1)
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))
1239 node->contents = nodestart;
1240 return node_body.buffer + position;
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;
1252 position = find_node_in_binding (node->nodename, &node_body);
1254 /* If the node couldn't be found, we lose big. */
1255 if (position == -1)
1256 return NULL;
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;