PageRenderTime 53ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/src/freebsd/contrib/texinfo/info/info-utils.c

https://bitbucket.org/killerpenguinassassins/open_distrib_devel
C | 701 lines | 457 code | 114 blank | 130 comment | 93 complexity | f2525976984898d8494de279d5c6c2f3 MD5 | raw file
Possible License(s): CC0-1.0, MIT, LGPL-2.0, LGPL-3.0, WTFPL, GPL-2.0, BSD-2-Clause, AGPL-3.0, CC-BY-SA-3.0, MPL-2.0, JSON, BSD-3-Clause-No-Nuclear-License-2014, LGPL-2.1, CPL-1.0, AGPL-1.0, 0BSD, ISC, Apache-2.0, GPL-3.0, IPL-1.0, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  1. /* info-utils.c -- miscellanous.
  2. $Id: info-utils.c,v 1.4 2004/04/11 17:56:45 karl Exp $
  3. Copyright (C) 1993, 1998, 2003, 2004 Free Software Foundation, Inc.
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  15. Originally written by Brian Fox (bfox@ai.mit.edu). */
  16. #include "info.h"
  17. #include "info-utils.h"
  18. #if defined (HANDLE_MAN_PAGES)
  19. # include "man.h"
  20. #endif /* HANDLE_MAN_PAGES */
  21. /* When non-zero, various display and input functions handle ISO Latin
  22. character sets correctly. */
  23. int ISO_Latin_p = 1;
  24. /* Variable which holds the most recent filename parsed as a result of
  25. calling info_parse_xxx (). */
  26. char *info_parsed_filename = (char *)NULL;
  27. /* Variable which holds the most recent nodename parsed as a result of
  28. calling info_parse_xxx (). */
  29. char *info_parsed_nodename = (char *)NULL;
  30. /* Variable which holds the most recent line number parsed as a result of
  31. calling info_parse_xxx (). */
  32. int info_parsed_line_number = 0;
  33. /* Functions to remember a filename or nodename for later return. */
  34. static void save_filename (char *filename);
  35. static void saven_filename (char *filename, int len);
  36. static void save_nodename (char *nodename);
  37. static void saven_nodename (char *nodename, int len);
  38. /* How to get a reference (either menu or cross). */
  39. static REFERENCE **info_references_internal (char *label,
  40. SEARCH_BINDING *binding);
  41. /* Parse the filename and nodename out of STRING. If STRING doesn't
  42. contain a filename (i.e., it is NOT (FILENAME)NODENAME) then set
  43. INFO_PARSED_FILENAME to NULL. If second argument NEWLINES_OKAY is
  44. non-zero, it says to allow the nodename specification to cross a
  45. newline boundary (i.e., only `,', `.', or `TAB' can end the spec). */
  46. void
  47. info_parse_node (char *string, int newlines_okay)
  48. {
  49. register int i = 0;
  50. /* Default the answer. */
  51. save_filename ((char *)NULL);
  52. save_nodename ((char *)NULL);
  53. /* Special case of nothing passed. Return nothing. */
  54. if (!string || !*string)
  55. return;
  56. string += skip_whitespace (string);
  57. /* Check for (FILENAME)NODENAME. */
  58. if (*string == '(')
  59. {
  60. i = 0;
  61. /* Advance past the opening paren. */
  62. string++;
  63. /* Find the closing paren. */
  64. while (string[i] && string[i] != ')')
  65. i++;
  66. /* Remember parsed filename. */
  67. saven_filename (string, i);
  68. /* Point directly at the nodename. */
  69. string += i;
  70. if (*string)
  71. string++;
  72. }
  73. /* Parse out nodename. */
  74. i = skip_node_characters (string, newlines_okay);
  75. saven_nodename (string, i);
  76. canonicalize_whitespace (info_parsed_nodename);
  77. if (info_parsed_nodename && !*info_parsed_nodename)
  78. {
  79. free (info_parsed_nodename);
  80. info_parsed_nodename = (char *)NULL;
  81. }
  82. /* Parse ``(line ...)'' part of menus, if any. */
  83. {
  84. char *rest = string + i;
  85. /* Advance only if it's not already at end of string. */
  86. if (*rest)
  87. rest++;
  88. /* Skip any whitespace first, and then a newline in case the item
  89. was so long to contain the ``(line ...)'' string in the same
  90. physical line. */
  91. while (whitespace(*rest))
  92. rest++;
  93. if (*rest == '\n')
  94. {
  95. rest++;
  96. while (whitespace(*rest))
  97. rest++;
  98. }
  99. /* Are we looking at an opening parenthesis? That can only mean
  100. we have a winner. :) */
  101. if (strncmp (rest, "(line ", strlen ("(line ")) == 0)
  102. {
  103. rest += strlen ("(line ");
  104. info_parsed_line_number = strtol (rest, NULL, 0);
  105. }
  106. else
  107. info_parsed_line_number = 0;
  108. }
  109. }
  110. /* Return the node addressed by LABEL in NODE (usually one of "Prev:",
  111. "Next:", "Up:", "File:", or "Node:". After a call to this function,
  112. the global INFO_PARSED_NODENAME and INFO_PARSED_FILENAME contain
  113. the information. */
  114. void
  115. info_parse_label (char *label, NODE *node)
  116. {
  117. register int i;
  118. char *nodeline;
  119. /* Default answer to failure. */
  120. save_nodename ((char *)NULL);
  121. save_filename ((char *)NULL);
  122. /* Find the label in the first line of this node. */
  123. nodeline = node->contents;
  124. i = string_in_line (label, nodeline);
  125. if (i == -1)
  126. return;
  127. nodeline += i;
  128. nodeline += skip_whitespace (nodeline);
  129. info_parse_node (nodeline, DONT_SKIP_NEWLINES);
  130. }
  131. /* **************************************************************** */
  132. /* */
  133. /* Finding and Building Menus */
  134. /* */
  135. /* **************************************************************** */
  136. /* Return a NULL terminated array of REFERENCE * which represents the menu
  137. found in NODE. If there is no menu in NODE, just return a NULL pointer. */
  138. REFERENCE **
  139. info_menu_of_node (NODE *node)
  140. {
  141. long position;
  142. SEARCH_BINDING tmp_search;
  143. REFERENCE **menu = (REFERENCE **)NULL;
  144. tmp_search.buffer = node->contents;
  145. tmp_search.start = 0;
  146. tmp_search.end = node->nodelen;
  147. tmp_search.flags = S_FoldCase;
  148. /* Find the start of the menu. */
  149. position = search_forward (INFO_MENU_LABEL, &tmp_search);
  150. if (position == -1)
  151. return ((REFERENCE **) NULL);
  152. /* We have the start of the menu now. Glean menu items from the rest
  153. of the node. */
  154. tmp_search.start = position + strlen (INFO_MENU_LABEL);
  155. tmp_search.start += skip_line (tmp_search.buffer + tmp_search.start);
  156. tmp_search.start--;
  157. menu = info_menu_items (&tmp_search);
  158. return (menu);
  159. }
  160. /* Return a NULL terminated array of REFERENCE * which represents the cross
  161. refrences found in NODE. If there are no cross references in NODE, just
  162. return a NULL pointer. */
  163. REFERENCE **
  164. info_xrefs_of_node (NODE *node)
  165. {
  166. SEARCH_BINDING tmp_search;
  167. #if defined (HANDLE_MAN_PAGES)
  168. if (node->flags & N_IsManPage)
  169. return (xrefs_of_manpage (node));
  170. #endif
  171. tmp_search.buffer = node->contents;
  172. tmp_search.start = 0;
  173. tmp_search.end = node->nodelen;
  174. tmp_search.flags = S_FoldCase;
  175. return (info_xrefs (&tmp_search));
  176. }
  177. /* Glean menu entries from BINDING->buffer + BINDING->start until we
  178. have looked at the entire contents of BINDING. Return an array
  179. of REFERENCE * that represents each menu item in this range. */
  180. REFERENCE **
  181. info_menu_items (SEARCH_BINDING *binding)
  182. {
  183. return (info_references_internal (INFO_MENU_ENTRY_LABEL, binding));
  184. }
  185. /* Glean cross references from BINDING->buffer + BINDING->start until
  186. BINDING->end. Return an array of REFERENCE * that represents each
  187. cross reference in this range. */
  188. REFERENCE **
  189. info_xrefs (SEARCH_BINDING *binding)
  190. {
  191. return (info_references_internal (INFO_XREF_LABEL, binding));
  192. }
  193. /* Glean cross references or menu items from BINDING. Return an array
  194. of REFERENCE * that represents the items found. */
  195. static REFERENCE **
  196. info_references_internal (char *label, SEARCH_BINDING *binding)
  197. {
  198. SEARCH_BINDING tmp_search;
  199. REFERENCE **refs = (REFERENCE **)NULL;
  200. int refs_index = 0, refs_slots = 0;
  201. int searching_for_menu_items = 0;
  202. long position;
  203. tmp_search.buffer = binding->buffer;
  204. tmp_search.start = binding->start;
  205. tmp_search.end = binding->end;
  206. tmp_search.flags = S_FoldCase | S_SkipDest;
  207. searching_for_menu_items = (strcasecmp (label, INFO_MENU_ENTRY_LABEL) == 0);
  208. while ((position = search_forward (label, &tmp_search)) != -1)
  209. {
  210. int offset, start;
  211. char *refdef;
  212. REFERENCE *entry;
  213. tmp_search.start = position;
  214. tmp_search.start += skip_whitespace (tmp_search.buffer + tmp_search.start);
  215. start = tmp_search.start - binding->start;
  216. refdef = tmp_search.buffer + tmp_search.start;
  217. offset = string_in_line (":", refdef);
  218. /* When searching for menu items, if no colon, there is no
  219. menu item on this line. */
  220. if (offset == -1)
  221. {
  222. if (searching_for_menu_items)
  223. continue;
  224. else
  225. {
  226. int temp;
  227. temp = skip_line (refdef);
  228. offset = string_in_line (":", refdef + temp);
  229. if (offset == -1)
  230. continue; /* Give up? */
  231. else
  232. offset += temp;
  233. }
  234. }
  235. entry = (REFERENCE *)xmalloc (sizeof (REFERENCE));
  236. entry->filename = (char *)NULL;
  237. entry->nodename = (char *)NULL;
  238. entry->label = (char *)xmalloc (offset);
  239. strncpy (entry->label, refdef, offset - 1);
  240. entry->label[offset - 1] = '\0';
  241. canonicalize_whitespace (entry->label);
  242. refdef += offset;
  243. entry->start = start;
  244. entry->end = refdef - binding->buffer;
  245. /* If this reference entry continues with another ':' then the
  246. nodename is the same as the label. */
  247. if (*refdef == ':')
  248. {
  249. entry->nodename = xstrdup (entry->label);
  250. }
  251. else
  252. {
  253. /* This entry continues with a specific nodename. Parse the
  254. nodename from the specification. */
  255. refdef += skip_whitespace_and_newlines (refdef);
  256. if (searching_for_menu_items)
  257. info_parse_node (refdef, DONT_SKIP_NEWLINES);
  258. else
  259. info_parse_node (refdef, SKIP_NEWLINES);
  260. if (info_parsed_filename)
  261. entry->filename = xstrdup (info_parsed_filename);
  262. if (info_parsed_nodename)
  263. entry->nodename = xstrdup (info_parsed_nodename);
  264. entry->line_number = info_parsed_line_number;
  265. }
  266. add_pointer_to_array
  267. (entry, refs_index, refs, refs_slots, 50, REFERENCE *);
  268. }
  269. return (refs);
  270. }
  271. /* Get the entry associated with LABEL in REFERENCES. Return a pointer
  272. to the ENTRY if found, or NULL. */
  273. REFERENCE *
  274. info_get_labeled_reference (char *label, REFERENCE **references)
  275. {
  276. register int i;
  277. REFERENCE *entry;
  278. for (i = 0; references && (entry = references[i]); i++)
  279. {
  280. if (strcmp (label, entry->label) == 0)
  281. return (entry);
  282. }
  283. return ((REFERENCE *)NULL);
  284. }
  285. /* A utility function for concatenating REFERENCE **. Returns a new
  286. REFERENCE ** which is the concatenation of REF1 and REF2. The REF1
  287. and REF2 arrays are freed, but their contents are not. */
  288. REFERENCE **
  289. info_concatenate_references (REFERENCE **ref1, REFERENCE **ref2)
  290. {
  291. register int i, j;
  292. REFERENCE **result;
  293. int size;
  294. /* With one argument passed as NULL, simply return the other arg. */
  295. if (!ref1)
  296. return (ref2);
  297. else if (!ref2)
  298. return (ref1);
  299. /* Get the total size of the slots that we will need. */
  300. for (i = 0; ref1[i]; i++);
  301. size = i;
  302. for (i = 0; ref2[i]; i++);
  303. size += i;
  304. result = (REFERENCE **)xmalloc ((1 + size) * sizeof (REFERENCE *));
  305. /* Copy the contents over. */
  306. for (i = 0; ref1[i]; i++)
  307. result[i] = ref1[i];
  308. j = i;
  309. for (i = 0; ref2[i]; i++)
  310. result[j++] = ref2[i];
  311. result[j] = (REFERENCE *)NULL;
  312. free (ref1);
  313. free (ref2);
  314. return (result);
  315. }
  316. /* Copy a reference structure. Since we tend to free everything at
  317. every opportunity, we don't share any points, but copy everything into
  318. new memory. */
  319. REFERENCE *
  320. info_copy_reference (REFERENCE *src)
  321. {
  322. REFERENCE *dest = xmalloc (sizeof (REFERENCE));
  323. dest->label = src->label ? xstrdup (src->label) : NULL;
  324. dest->filename = src->filename ? xstrdup (src->filename) : NULL;
  325. dest->nodename = src->nodename ? xstrdup (src->nodename) : NULL;
  326. dest->start = src->start;
  327. dest->end = src->end;
  328. return dest;
  329. }
  330. /* Free the data associated with REFERENCES. */
  331. void
  332. info_free_references (REFERENCE **references)
  333. {
  334. register int i;
  335. REFERENCE *entry;
  336. if (references)
  337. {
  338. for (i = 0; references && (entry = references[i]); i++)
  339. {
  340. maybe_free (entry->label);
  341. maybe_free (entry->filename);
  342. maybe_free (entry->nodename);
  343. free (entry);
  344. }
  345. free (references);
  346. }
  347. }
  348. /* Search for sequences of whitespace or newlines in STRING, replacing
  349. all such sequences with just a single space. Remove whitespace from
  350. start and end of string. */
  351. void
  352. canonicalize_whitespace (char *string)
  353. {
  354. register int i, j;
  355. int len, whitespace_found, whitespace_loc = 0;
  356. char *temp;
  357. if (!string)
  358. return;
  359. len = strlen (string);
  360. temp = (char *)xmalloc (1 + len);
  361. /* Search for sequences of whitespace or newlines. Replace all such
  362. sequences in the string with just a single space. */
  363. whitespace_found = 0;
  364. for (i = 0, j = 0; string[i]; i++)
  365. {
  366. if (whitespace_or_newline (string[i]))
  367. {
  368. whitespace_found++;
  369. whitespace_loc = i;
  370. continue;
  371. }
  372. else
  373. {
  374. if (whitespace_found && whitespace_loc)
  375. {
  376. whitespace_found = 0;
  377. /* Suppress whitespace at start of string. */
  378. if (j)
  379. temp[j++] = ' ';
  380. }
  381. temp[j++] = string[i];
  382. }
  383. }
  384. /* Kill trailing whitespace. */
  385. if (j && whitespace (temp[j - 1]))
  386. j--;
  387. temp[j] = '\0';
  388. strcpy (string, temp);
  389. free (temp);
  390. }
  391. /* String representation of a char returned by printed_representation (). */
  392. static char the_rep[10];
  393. /* Return a pointer to a string which is the printed representation
  394. of CHARACTER if it were printed at HPOS. */
  395. char *
  396. printed_representation (unsigned char character, int hpos)
  397. {
  398. register int i = 0;
  399. int printable_limit = ISO_Latin_p ? 255 : 127;
  400. if (raw_escapes_p && character == '\033')
  401. the_rep[i++] = character;
  402. /* Show CTRL-x as ^X. */
  403. else if (iscntrl (character) && character < 127)
  404. {
  405. switch (character)
  406. {
  407. case '\r':
  408. case '\n':
  409. the_rep[i++] = character;
  410. break;
  411. case '\t':
  412. {
  413. int tw;
  414. tw = ((hpos + 8) & 0xf8) - hpos;
  415. while (i < tw)
  416. the_rep[i++] = ' ';
  417. }
  418. break;
  419. default:
  420. the_rep[i++] = '^';
  421. the_rep[i++] = (character | 0x40);
  422. }
  423. }
  424. /* Show META-x as 0370. */
  425. else if (character > printable_limit)
  426. {
  427. sprintf (the_rep + i, "\\%0o", character);
  428. i = strlen (the_rep);
  429. }
  430. else if (character == DEL)
  431. {
  432. the_rep[i++] = '^';
  433. the_rep[i++] = '?';
  434. }
  435. else
  436. the_rep[i++] = character;
  437. the_rep[i] = 0;
  438. return the_rep;
  439. }
  440. /* **************************************************************** */
  441. /* */
  442. /* Functions Static To This File */
  443. /* */
  444. /* **************************************************************** */
  445. /* Amount of space allocated to INFO_PARSED_FILENAME via xmalloc (). */
  446. static int parsed_filename_size = 0;
  447. /* Amount of space allocated to INFO_PARSED_NODENAME via xmalloc (). */
  448. static int parsed_nodename_size = 0;
  449. static void save_string (char *string, char **string_p, int *string_size_p);
  450. static void saven_string (char *string, int len, char **string_p,
  451. int *string_size_p);
  452. /* Remember FILENAME in PARSED_FILENAME. An empty FILENAME is translated
  453. to a NULL pointer in PARSED_FILENAME. */
  454. static void
  455. save_filename (char *filename)
  456. {
  457. save_string (filename, &info_parsed_filename, &parsed_filename_size);
  458. }
  459. /* Just like save_filename (), but you pass the length of the string. */
  460. static void
  461. saven_filename (char *filename, int len)
  462. {
  463. saven_string (filename, len,
  464. &info_parsed_filename, &parsed_filename_size);
  465. }
  466. /* Remember NODENAME in PARSED_NODENAME. An empty NODENAME is translated
  467. to a NULL pointer in PARSED_NODENAME. */
  468. static void
  469. save_nodename (char *nodename)
  470. {
  471. save_string (nodename, &info_parsed_nodename, &parsed_nodename_size);
  472. }
  473. /* Just like save_nodename (), but you pass the length of the string. */
  474. static void
  475. saven_nodename (char *nodename, int len)
  476. {
  477. saven_string (nodename, len,
  478. &info_parsed_nodename, &parsed_nodename_size);
  479. }
  480. /* Remember STRING in STRING_P. STRING_P should currently have STRING_SIZE_P
  481. bytes allocated to it. An empty STRING is translated to a NULL pointer
  482. in STRING_P. */
  483. static void
  484. save_string (char *string, char **string_p, int *string_size_p)
  485. {
  486. if (!string || !*string)
  487. {
  488. if (*string_p)
  489. free (*string_p);
  490. *string_p = (char *)NULL;
  491. *string_size_p = 0;
  492. }
  493. else
  494. {
  495. if (strlen (string) >= (unsigned int) *string_size_p)
  496. *string_p = (char *)xrealloc
  497. (*string_p, (*string_size_p = 1 + strlen (string)));
  498. strcpy (*string_p, string);
  499. }
  500. }
  501. /* Just like save_string (), but you also pass the length of STRING. */
  502. static void
  503. saven_string (char *string, int len, char **string_p, int *string_size_p)
  504. {
  505. if (!string)
  506. {
  507. if (*string_p)
  508. free (*string_p);
  509. *string_p = (char *)NULL;
  510. *string_size_p = 0;
  511. }
  512. else
  513. {
  514. if (len >= *string_size_p)
  515. *string_p = (char *)xrealloc (*string_p, (*string_size_p = 1 + len));
  516. strncpy (*string_p, string, len);
  517. (*string_p)[len] = '\0';
  518. }
  519. }
  520. /* Return a pointer to the part of PATHNAME that simply defines the file. */
  521. char *
  522. filename_non_directory (char *pathname)
  523. {
  524. register char *filename = pathname + strlen (pathname);
  525. if (HAVE_DRIVE (pathname))
  526. pathname += 2;
  527. while (filename > pathname && !IS_SLASH (filename[-1]))
  528. filename--;
  529. return (filename);
  530. }
  531. /* Return non-zero if NODE is one especially created by Info. */
  532. int
  533. internal_info_node_p (NODE *node)
  534. {
  535. #if defined (NEVER)
  536. if (node &&
  537. (node->filename && !*node->filename) &&
  538. !node->parent && node->nodename)
  539. return (1);
  540. else
  541. return (0);
  542. #else
  543. return ((node != (NODE *)NULL) && ((node->flags & N_IsInternal) != 0));
  544. #endif /* !NEVER */
  545. }
  546. /* Make NODE appear to be one especially created by Info. */
  547. void
  548. name_internal_node (NODE *node, char *name)
  549. {
  550. if (!node)
  551. return;
  552. node->filename = "";
  553. node->parent = (char *)NULL;
  554. node->nodename = name;
  555. node->flags |= N_IsInternal;
  556. }
  557. /* Return the window displaying NAME, the name of an internally created
  558. Info window. */
  559. WINDOW *
  560. get_internal_info_window (char *name)
  561. {
  562. WINDOW *win;
  563. for (win = windows; win; win = win->next)
  564. if (internal_info_node_p (win->node) &&
  565. (strcmp (win->node->nodename, name) == 0))
  566. break;
  567. return (win);
  568. }
  569. /* Return a window displaying the node NODE. */
  570. WINDOW *
  571. get_window_of_node (NODE *node)
  572. {
  573. WINDOW *win = (WINDOW *)NULL;
  574. for (win = windows; win; win = win->next)
  575. if (win->node == node)
  576. break;
  577. return (win);
  578. }