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