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