nodes.c 44 KB

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