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