source: trunk/texinfo/info/nodes.c@ 3003

Last change on this file since 3003 was 2617, checked in by bird, 19 years ago

GNU Texinfo 4.8

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