File: | src/gnu/usr.bin/texinfo/info/nodes.c |
Warning: | line 807, column 37 Array access (via field 'tags') results in a null pointer dereference |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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_FUDGE1000 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_TAGS0 0 | |||
59 | #define INFO_GET_TAGS1 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((void *)0); | |||
65 | ||||
66 | /* The list of already loaded nodes. */ | |||
67 | FILE_BUFFER **info_loaded_files = NULL((void *)0); | |||
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 * | |||
82 | info_get_node (char *filename, char *nodename) | |||
83 | { | |||
84 | NODE *node; | |||
85 | FILE_BUFFER *file_buffer = NULL((void *)0); | |||
86 | ||||
87 | info_recent_file_error = NULL((void *)0); | |||
88 | info_parse_node (nodename, DONT_SKIP_NEWLINES0); | |||
89 | nodename = NULL((void *)0); | |||
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((void *)0); | |||
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((void *)0) || 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 * | |||
137 | info_get_node_of_file_buffer (char *nodename, FILE_BUFFER *file_buffer) | |||
138 | { | |||
139 | NODE *node = NULL((void *)0); | |||
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((void *)0); | |||
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((void *)0); | |||
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_IsManPage0x40) | |||
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 * | |||
195 | info_find_file (char *filename) | |||
196 | { | |||
197 | return info_find_file_internal (filename, INFO_GET_TAGS1); | |||
198 | } | |||
199 | ||||
200 | /* Load the info file FILENAME, remembering information about it in a | |||
201 | file buffer. */ | |||
202 | FILE_BUFFER * | |||
203 | info_load_file (char *filename) | |||
204 | { | |||
205 | return info_load_file_internal (filename, INFO_GET_TAGS1); | |||
| ||||
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 * | |||
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_CMPstrcmp (filename, file_buffer->filename) == 0) | |||
227 | || (FILENAME_CMPstrcmp (filename, file_buffer->fullpath) == 0) | |||
228 | || (!IS_ABSOLUTE (filename)((filename)[0] == '/') | |||
229 | && FILENAME_CMPstrcmp (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_IsManPage0x40) | |||
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(*__errno()); | |||
251 | return NULL((void *)0); | |||
252 | } | |||
253 | ||||
254 | old_info = &file_buffer->finfo; | |||
255 | ||||
256 | if (new_info.st_size != old_info->st_size | |||
257 | || new_info.st_mtimest_mtim.tv_sec != old_info->st_mtimest_mtim.tv_sec) | |||
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((void *)0); | |||
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"*manpages*") == 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 * | |||
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((void *)0); | |||
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
| |||
354 | { | |||
355 | filesys_error_number = errno(*__errno()); | |||
356 | return NULL((void *)0); | |||
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((void *)0); | |||
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_IsCompressed0x08; | |||
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 | |||
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_HasTagsTable0x01; | |||
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_FoldCase0x01; | |||
401 | ||||
402 | position = search_backward (TAGS_TABLE_END_LABEL"\nEnd Tag Table", &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"\nEnd Tag Table"); | |||
413 | if (binding.start
| |||
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"Tag Table:\n", &binding); | |||
430 | ||||
431 | if (position == -1) | |||
432 | break; | |||
433 | ||||
434 | binding.end = position; | |||
435 | binding.start = binding.end - 5 - strlen (TAGS_TABLE_BEG_LABEL"Tag Table:\n"); | |||
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_HasTagsTable0x01; | |||
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"(Indirect)", &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_FoldCase0x01; | |||
468 | ||||
469 | position = search_backward (INDIRECT_TAGS_TABLE_LABEL"Indirect:\n", &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 | |||
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_FoldCase0x01; | |||
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"Node:", nodeline); | |||
523 | /* No Node:. Maybe it's a Ref:. */ | |||
524 | if (start == -1) | |||
525 | { | |||
526 | start = string_in_line (INFO_REF_LABEL"Ref:", 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_NEWLINES0); | |||
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_FoldCase0x01; | |||
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,do { if (tags_index + 2 >= file_buffer->tags_slots) file_buffer ->tags = (TAG * *)(xrealloc (file_buffer->tags, (file_buffer ->tags_slots += 100) * sizeof (TAG *))); file_buffer->tags [tags_index++] = (TAG *)entry; file_buffer->tags[tags_index ] = (TAG *)((void *)0); } while (0) | |||
566 | file_buffer->tags_slots, 100, TAG *)do { if (tags_index + 2 >= file_buffer->tags_slots) file_buffer ->tags = (TAG * *)(xrealloc (file_buffer->tags, (file_buffer ->tags_slots += 100) * sizeof (TAG *))); file_buffer->tags [tags_index++] = (TAG *)entry; file_buffer->tags[tags_index ] = (TAG *)((void *)0); } while (0); | |||
567 | } | |||
568 | } | |||
569 | ||||
570 | /* Return the length of the node which starts at BINDING. */ | |||
571 | static long | |||
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'\014' || body[i] == INFO_COOKIE'\037') | |||
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 | |||
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"Tag Table:\n"); | |||
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"(Indirect)", tmp_search)) | |||
628 | continue; | |||
629 | ||||
630 | /* Find the label preceding the node name. */ | |||
631 | name_offset = | |||
632 | string_in_line (INFO_NODE_LABEL"Node:", 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"Ref:", | |||
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'\177'; p++) | |||
657 | ; | |||
658 | if (nodedef[p] != INFO_TAGSEP'\177') | |||
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,do { if (tags_index + 2 >= file_buffer->tags_slots) file_buffer ->tags = (TAG * *)(xrealloc (file_buffer->tags, (file_buffer ->tags_slots += 100) * sizeof (TAG *))); file_buffer->tags [tags_index++] = (TAG *)entry; file_buffer->tags[tags_index ] = (TAG *)((void *)0); } while (0) | |||
678 | file_buffer->tags_slots, 100, TAG *)do { if (tags_index + 2 >= file_buffer->tags_slots) file_buffer ->tags = (TAG * *)(xrealloc (file_buffer->tags, (file_buffer ->tags_slots += 100) * sizeof (TAG *))); file_buffer->tags [tags_index++] = (TAG *)entry; file_buffer->tags[tags_index ] = (TAG *)((void *)0); } while (0); | |||
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 | |||
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((void *)0); | |||
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_arraydo { if (subfiles_index + 2 >= subfiles_slots) subfiles = ( SUBFILE * *)(xrealloc (subfiles, (subfiles_slots += 10) * sizeof (SUBFILE *))); subfiles[subfiles_index++] = (SUBFILE *)subfile ; subfiles[subfiles_index] = (SUBFILE *)((void *)0); } while ( 0) | |||
732 | (subfile, subfiles_index, subfiles, subfiles_slots, 10, SUBFILE *)do { if (subfiles_index + 2 >= subfiles_slots) subfiles = ( SUBFILE * *)(xrealloc (subfiles, (subfiles_slots += 10) * sizeof (SUBFILE *))); subfiles[subfiles_index++] = (SUBFILE *)subfile ; subfiles[subfiles_index] = (SUBFILE *)((void *)0); } while ( 0); | |||
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_FoldCase0x01; | |||
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)(0) && | |||
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((void *)0); | |||
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((void *)0); | |||
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_TagsIndirect0x02; | |||
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 * | |||
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((void *)0); | |||
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((void *)0); | |||
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_FromAnchor0x80; | |||
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 * | |||
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((void *)0); | |||
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_TAGS0); | |||
944 | if (!subfile) | |||
945 | return NULL((void *)0); | |||
946 | ||||
947 | if (!subfile->contents) | |||
948 | { | |||
949 | info_reload_file_buffer_contents (subfile); | |||
950 | if (!subfile->contents) | |||
951 | return NULL((void *)0); | |||
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((void *)0); | |||
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_HasTagsTable0x01) | |||
966 | { | |||
967 | node->flags |= N_HasTagsTable0x01; | |||
968 | ||||
969 | if (file_buffer->flags & N_TagsIndirect0x02) | |||
970 | { | |||
971 | node->flags |= N_TagsIndirect0x02; | |||
972 | node->parent = file_buffer->fullpath; | |||
973 | } | |||
974 | } | |||
975 | ||||
976 | if (subfile->flags & N_IsCompressed0x08) | |||
977 | node->flags |= N_IsCompressed0x08; | |||
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_FUDGE1000; | |||
993 | ||||
994 | if (tag->nodestart < DEFAULT_INFO_FUDGE1000) | |||
995 | min = tag->nodestart; | |||
996 | ||||
997 | if (DEFAULT_INFO_FUDGE1000 > | |||
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((void *)0)) | |||
1007 | { | |||
1008 | free (node); | |||
1009 | return NULL((void *)0); | |||
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((void *)0); | |||
1047 | } | |||
1048 | ||||
1049 | /* Managing file_buffers, nodes, and tags. */ | |||
1050 | ||||
1051 | /* Create a new, empty file buffer. */ | |||
1052 | FILE_BUFFER * | |||
1053 | make_file_buffer (void) | |||
1054 | { | |||
1055 | FILE_BUFFER *file_buffer = xmalloc (sizeof (FILE_BUFFER)); | |||
1056 | ||||
1057 | file_buffer->filename = file_buffer->fullpath = NULL((void *)0); | |||
1058 | file_buffer->contents = NULL((void *)0); | |||
1059 | file_buffer->tags = NULL((void *)0); | |||
1060 | file_buffer->subfiles = NULL((void *)0); | |||
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 | |||
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,do { if (i + 2 >= info_loaded_files_slots) info_loaded_files = (FILE_BUFFER * *)(xrealloc (info_loaded_files, (info_loaded_files_slots += 10) * sizeof (FILE_BUFFER *))); info_loaded_files[i++] = ( FILE_BUFFER *)file_buffer; info_loaded_files[i] = (FILE_BUFFER *)((void *)0); } while (0) | |||
1077 | info_loaded_files_slots, 10, FILE_BUFFER *)do { if (i + 2 >= info_loaded_files_slots) info_loaded_files = (FILE_BUFFER * *)(xrealloc (info_loaded_files, (info_loaded_files_slots += 10) * sizeof (FILE_BUFFER *))); info_loaded_files[i++] = ( FILE_BUFFER *)file_buffer; info_loaded_files[i] = (FILE_BUFFER *)((void *)0); } while (0); | |||
1078 | } | |||
1079 | ||||
1080 | /* Forget the contents, tags table, nodes list, and names of FILENAME. */ | |||
1081 | static void | |||
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_CMPstrcmp (filename, file_buffer->filename) == 0 | |||
1092 | || FILENAME_CMPstrcmp (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 | |||
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((void *)0); | |||
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((void *)0); | |||
1141 | } | |||
1142 | } | |||
1143 | ||||
1144 | /* Free the data associated with TAG, as well as TAG itself. */ | |||
1145 | static void | |||
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 | |||
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_IsManPage0x40) | |||
1170 | return; | |||
1171 | #endif | |||
1172 | ||||
1173 | fb->flags &= ~N_IsCompressed0x08; | |||
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_IsCompressed0x08; | |||
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 * | |||
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'\037' && 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"Node:", nodedef); | |||
1228 | ||||
1229 | if (offset != -1) | |||
1230 | { | |||
1231 | nodedef += offset; | |||
1232 | nodedef += skip_whitespace (nodedef); | |||
1233 | offset = skip_node_characters (nodedef, DONT_SKIP_NEWLINES0); | |||
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((void *)0); | |||
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_HasTagsTable0x01) | |||
1261 | node->flags |= N_UpdateTags0x04; | |||
1262 | return node_body.buffer + position; | |||
1263 | } |