xref.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. /* xref.c -- cross references for Texinfo.
  2. $Id: xref.c,v 1.14 2007-09-26 20:53:40 karl Exp $
  3. Copyright (C) 2004, 2005, 2007 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 3 of the License, or
  7. (at your option) 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, see <http://www.gnu.org/licenses/>. */
  14. #include "system.h"
  15. #include "cmds.h"
  16. #include "float.h"
  17. #include "html.h"
  18. #include "index.h"
  19. #include "macro.h"
  20. #include "makeinfo.h"
  21. #include "node.h"
  22. #include "xml.h"
  23. #include "xref.h"
  24. /* Flags which control initial output string for xrefs. */
  25. int px_ref_flag = 0;
  26. int ref_flag = 0;
  27. /* Called in the multiple-argument case to make sure we generate a valid
  28. Info reference. In the single-argument case, the :: we output
  29. suffices for the Info readers to find the end of the reference. */
  30. static void
  31. add_xref_punctuation (void)
  32. {
  33. if (px_ref_flag || ref_flag) /* user inserts punct after @xref */
  34. {
  35. /* Check if there's already punctuation. */
  36. int next_char = next_nonwhitespace_character ();
  37. if (next_char == -1)
  38. /* EOF while looking for punctuation, let's
  39. insert a period instead of crying. */
  40. add_char ('.');
  41. else if (next_char != ',' && next_char != '.')
  42. /* period and comma terminate xrefs, and nothing else. Instead
  43. of generating an Info reference that can't be followed,
  44. though, just insert a period. Not pretty, but functional. */
  45. add_char ('.');
  46. }
  47. }
  48. /* Return next comma-delimited argument, but do not cross a close-brace
  49. boundary. Clean up whitespace, too. If EXPAND is nonzero, replace
  50. the entire brace-delimited argument list with its expansion before
  51. looking for the next comma. */
  52. char *
  53. get_xref_token (int expand)
  54. {
  55. char *string = 0;
  56. if (docbook)
  57. xml_in_xref_token = 1;
  58. if (expand)
  59. {
  60. int old_offset = input_text_offset;
  61. int old_lineno = line_number;
  62. get_until_in_braces ("}", &string);
  63. if (curchar () == '}') /* as opposed to end of text */
  64. input_text_offset++;
  65. if (input_text_offset > old_offset)
  66. {
  67. int limit = input_text_offset;
  68. input_text_offset = old_offset;
  69. line_number = old_lineno;
  70. only_macro_expansion++;
  71. replace_with_expansion (input_text_offset, &limit);
  72. only_macro_expansion--;
  73. }
  74. free (string);
  75. }
  76. get_until_in_braces (",", &string);
  77. if (curchar () == ',')
  78. input_text_offset++;
  79. fix_whitespace (string);
  80. if (docbook)
  81. xml_in_xref_token = 0;
  82. return string;
  83. }
  84. /* NOTE: If you wonder why the HTML output is produced with such a
  85. peculiar mix of calls to add_word and execute_string, here's the
  86. reason. get_xref_token (1) expands all macros in a reference, but
  87. any other commands, like @value, @@, etc., are left intact. To
  88. expand them, we need to run the arguments through execute_string.
  89. However, characters like <, &, > and others cannot be let into
  90. execute_string, because they will be escaped. See the mess? */
  91. /* Make a cross reference. */
  92. void
  93. cm_xref (int arg)
  94. {
  95. if (arg == START)
  96. {
  97. char *arg1 = get_xref_token (1); /* expands all macros in xref */
  98. char *arg2 = get_xref_token (0);
  99. char *arg3 = get_xref_token (0);
  100. char *arg4 = get_xref_token (0);
  101. char *arg5 = get_xref_token (0);
  102. char *tem;
  103. /* "@xref{,Foo,, Bar, Baz} is not valid usage of @xref. The
  104. first argument must never be blank." --rms.
  105. We hereby comply by disallowing such constructs. */
  106. if (!*arg1)
  107. line_error (_("First argument to cross-reference may not be empty"));
  108. if (docbook)
  109. {
  110. if (!ref_flag)
  111. add_word (px_ref_flag || printing_index
  112. ? (char *) gdt("see ") : (char *) gdt("See "));
  113. if (!*arg4 && !*arg5)
  114. {
  115. char *arg1_id = xml_id (arg1);
  116. if (*arg2 || *arg3)
  117. {
  118. xml_insert_element_with_attribute (XREFNODENAME, START,
  119. "linkend=\"%s\"", arg1_id);
  120. free (arg1_id);
  121. execute_string ("%s", *arg3 ? arg3 : arg2);
  122. xml_insert_element (XREFNODENAME, END);
  123. }
  124. else
  125. {
  126. xml_insert_element_with_attribute (XREF, START,
  127. "linkend=\"%s\"", arg1_id);
  128. xml_insert_element (XREF, END);
  129. free (arg1_id);
  130. }
  131. }
  132. else if (*arg5)
  133. {
  134. add_word_args (gdt("See section ``%s'' in "), *arg3 ? arg3 : arg1);
  135. xml_insert_element (CITE, START);
  136. add_word (arg5);
  137. xml_insert_element (CITE, END);
  138. }
  139. else if (*arg4)
  140. {
  141. /* Very sad, we are losing xrefs made to ``info only'' books. */
  142. }
  143. }
  144. else if (xml)
  145. {
  146. if (!ref_flag)
  147. add_word_args ("%s", px_ref_flag ? gdt("see ") : gdt("See "));
  148. xml_insert_element (XREF, START);
  149. xml_insert_element (XREFNODENAME, START);
  150. execute_string ("%s", arg1);
  151. xml_insert_element (XREFNODENAME, END);
  152. if (*arg2)
  153. {
  154. xml_insert_element (XREFINFONAME, START);
  155. execute_string ("%s", arg2);
  156. xml_insert_element (XREFINFONAME, END);
  157. }
  158. if (*arg3)
  159. {
  160. xml_insert_element (XREFPRINTEDDESC, START);
  161. execute_string ("%s", arg3);
  162. xml_insert_element (XREFPRINTEDDESC, END);
  163. }
  164. if (*arg4)
  165. {
  166. xml_insert_element (XREFINFOFILE, START);
  167. execute_string ("%s", arg4);
  168. xml_insert_element (XREFINFOFILE, END);
  169. }
  170. if (*arg5)
  171. {
  172. xml_insert_element (XREFPRINTEDNAME, START);
  173. execute_string ("%s", arg5);
  174. xml_insert_element (XREFPRINTEDNAME, END);
  175. }
  176. xml_insert_element (XREF, END);
  177. }
  178. else if (html)
  179. {
  180. if (!ref_flag)
  181. add_word_args ("%s", px_ref_flag ? gdt("see ") : gdt("See "));
  182. }
  183. else
  184. add_word_args ("%s", px_ref_flag || ref_flag ? "*note " : "*Note ");
  185. if (!xml)
  186. {
  187. if (*arg5 || *arg4)
  188. {
  189. /* arg1 - node name
  190. arg2 - reference name
  191. arg3 - title or topic (and reference name if arg2 is NULL)
  192. arg4 - info file name
  193. arg5 - printed manual title */
  194. char *ref_name;
  195. if (!*arg2)
  196. {
  197. if (*arg3)
  198. ref_name = arg3;
  199. else
  200. ref_name = arg1;
  201. }
  202. else
  203. ref_name = arg2;
  204. if (html)
  205. { /* More to do eventually, down to Unicode
  206. Normalization Form C. See the HTML Xref nodes in
  207. the manual. */
  208. char *file_arg = arg4;
  209. add_html_elt ("<a href=");
  210. {
  211. /* If there's a directory part, ignore it. */
  212. char *p = strrchr (file_arg, '/');
  213. if (p)
  214. file_arg = p + 1;
  215. /* If there's a dot, make it a NULL terminator, so the
  216. extension does not get into the way. */
  217. p = strrchr (file_arg , '.');
  218. if (p != NULL)
  219. *p = 0;
  220. }
  221. if (! *file_arg)
  222. warning (_("Empty file name for HTML cross reference in `%s'"),
  223. arg4);
  224. /* Note that if we are splitting, and the referenced
  225. tag is an anchor rather than a node, we will
  226. produce a reference to a file whose name is
  227. derived from the anchor name. However, only
  228. nodes create files, so we are referencing a
  229. non-existent file. cm_anchor, which see, deals
  230. with that problem. */
  231. if (splitting)
  232. execute_string ("\"../%s/", file_arg);
  233. else
  234. execute_string ("\"%s.html", file_arg);
  235. /* Do not collapse -- to -, etc., in references. */
  236. in_fixed_width_font++;
  237. tem = expansion (arg1, 0); /* expand @-commands in node */
  238. in_fixed_width_font--;
  239. add_anchor_name (tem, 1);
  240. free (tem);
  241. add_word ("\">");
  242. execute_string ("%s",ref_name);
  243. add_word ("</a>");
  244. }
  245. else
  246. {
  247. execute_string ("%s:", ref_name);
  248. in_fixed_width_font++;
  249. execute_string (" (%s)%s", arg4, arg1);
  250. add_xref_punctuation ();
  251. in_fixed_width_font--;
  252. }
  253. /* Free all of the arguments found. */
  254. if (arg1) free (arg1);
  255. if (arg2) free (arg2);
  256. if (arg3) free (arg3);
  257. if (arg4) free (arg4);
  258. if (arg5) free (arg5);
  259. return;
  260. }
  261. else
  262. remember_node_reference (arg1, line_number, followed_reference);
  263. if (*arg3)
  264. {
  265. if (html)
  266. {
  267. add_html_elt ("<a href=\"");
  268. in_fixed_width_font++;
  269. tem = expansion (arg1, 0);
  270. in_fixed_width_font--;
  271. add_anchor_name (tem, 1);
  272. free (tem);
  273. add_word ("\">");
  274. execute_string ("%s", *arg2 ? arg2 : arg3);
  275. add_word ("</a>");
  276. }
  277. else
  278. {
  279. execute_string ("%s:", *arg2 ? arg2 : arg3);
  280. in_fixed_width_font++;
  281. execute_string (" %s", arg1);
  282. add_xref_punctuation ();
  283. in_fixed_width_font--;
  284. }
  285. }
  286. else
  287. {
  288. if (html)
  289. {
  290. add_html_elt ("<a href=\"");
  291. in_fixed_width_font++;
  292. tem = expansion (arg1, 0);
  293. in_fixed_width_font--;
  294. add_anchor_name (tem, 1);
  295. free (tem);
  296. add_word ("\">");
  297. if (*arg2)
  298. execute_string ("%s", arg2);
  299. else
  300. {
  301. char *fref = get_float_ref (arg1);
  302. execute_string ("%s", fref ? fref : arg1);
  303. free (fref);
  304. }
  305. add_word ("</a>");
  306. }
  307. else
  308. {
  309. if (*arg2)
  310. {
  311. execute_string ("%s:", arg2);
  312. in_fixed_width_font++;
  313. execute_string (" %s", arg1);
  314. add_xref_punctuation ();
  315. in_fixed_width_font--;
  316. }
  317. else
  318. {
  319. char *fref = get_float_ref (arg1);
  320. if (fref)
  321. { /* Reference is being made to a float. */
  322. execute_string ("%s:", fref);
  323. in_fixed_width_font++;
  324. execute_string (" %s", arg1);
  325. add_xref_punctuation ();
  326. in_fixed_width_font--;
  327. }
  328. else
  329. {
  330. in_fixed_width_font++;
  331. execute_string ("%s::", arg1);
  332. in_fixed_width_font--;
  333. }
  334. }
  335. }
  336. }
  337. }
  338. /* Free all of the arguments found. */
  339. if (arg1) free (arg1);
  340. if (arg2) free (arg2);
  341. if (arg3) free (arg3);
  342. if (arg4) free (arg4);
  343. if (arg5) free (arg5);
  344. }
  345. else
  346. { /* Check that the next non-whitespace character is valid to follow
  347. an xref (so Info readers can find the node names).
  348. `input_text_offset' is pointing at the "}" which ended the xref
  349. command. This is not used for @pxref or @ref, since we insert
  350. the necessary punctuation above, if needed. */
  351. int temp = next_nonwhitespace_character ();
  352. if (temp == -1)
  353. warning (_("End of file reached while looking for `.' or `,'"));
  354. else if (temp != '.' && temp != ',')
  355. {
  356. warning (_("`.' or `,' must follow @%s, not `%c'"), command, temp);
  357. if (temp == ')')
  358. warning (_("for cross-references in parentheses, use @pxref"));
  359. }
  360. }
  361. }
  362. void
  363. cm_pxref (int arg)
  364. {
  365. if (arg == START)
  366. {
  367. px_ref_flag++;
  368. cm_xref (arg);
  369. px_ref_flag--;
  370. }
  371. /* cm_xref isn't called with arg == END, which disables the code near
  372. the end of cm_xref that checks for `.' or `,' after the
  373. cross-reference. This is because cm_xref generates the required
  374. character itself (when needed) if px_ref_flag is set. */
  375. }
  376. void
  377. cm_ref (int arg)
  378. {
  379. /* See the comments in cm_pxref about the checks for punctuation. */
  380. if (arg == START)
  381. {
  382. ref_flag++;
  383. cm_xref (arg);
  384. ref_flag--;
  385. }
  386. }
  387. void
  388. cm_inforef (int arg)
  389. {
  390. if (arg == START)
  391. {
  392. char *node = get_xref_token (1); /* expands all macros in inforef */
  393. char *pname = get_xref_token (0);
  394. char *file = get_xref_token (0);
  395. /* (see comments at cm_xref). */
  396. if (!*node)
  397. line_error (_("First argument to @inforef may not be empty"));
  398. if (xml && !docbook)
  399. {
  400. xml_insert_element (INFOREF, START);
  401. xml_insert_element (INFOREFNODENAME, START);
  402. execute_string ("%s", node);
  403. xml_insert_element (INFOREFNODENAME, END);
  404. if (*pname)
  405. {
  406. xml_insert_element (INFOREFREFNAME, START);
  407. execute_string ("%s", pname);
  408. xml_insert_element (INFOREFREFNAME, END);
  409. }
  410. xml_insert_element (INFOREFINFONAME, START);
  411. execute_string ("%s", file);
  412. xml_insert_element (INFOREFINFONAME, END);
  413. xml_insert_element (INFOREF, END);
  414. }
  415. else if (html)
  416. {
  417. char *tem;
  418. add_word ((char *) gdt("see "));
  419. /* html fixxme: revisit this */
  420. add_html_elt ("<a href=");
  421. if (splitting)
  422. execute_string ("\"../%s/", file);
  423. else
  424. execute_string ("\"%s.html", file);
  425. tem = expansion (node, 0);
  426. add_anchor_name (tem, 1);
  427. add_word ("\">");
  428. execute_string ("%s", *pname ? pname : tem);
  429. add_word ("</a>");
  430. free (tem);
  431. }
  432. else
  433. {
  434. if (*pname)
  435. execute_string ("*note %s: (%s)%s", pname, file, node);
  436. else
  437. execute_string ("*note (%s)%s::", file, node);
  438. }
  439. free (node);
  440. free (pname);
  441. free (file);
  442. }
  443. }
  444. /* A URL reference. */
  445. void
  446. cm_uref (int arg)
  447. {
  448. if (arg == START)
  449. {
  450. char *url = get_xref_token (1); /* expands all macros in uref */
  451. char *desc = get_xref_token (0);
  452. char *replacement = get_xref_token (0);
  453. if (docbook)
  454. {
  455. xml_insert_element_with_attribute (UREF, START, "url=\"%s\"",
  456. maybe_escaped_expansion (url, 0, 1));
  457. if (*replacement)
  458. execute_string ("%s", replacement);
  459. else if (*desc)
  460. execute_string ("%s", desc);
  461. else
  462. execute_string ("%s", url);
  463. xml_insert_element (UREF, END);
  464. }
  465. else if (xml)
  466. {
  467. xml_insert_element (UREF, START);
  468. xml_insert_element (UREFURL, START);
  469. execute_string ("%s", url);
  470. xml_insert_element (UREFURL, END);
  471. if (*desc)
  472. {
  473. xml_insert_element (UREFDESC, START);
  474. execute_string ("%s", desc);
  475. xml_insert_element (UREFDESC, END);
  476. }
  477. if (*replacement)
  478. {
  479. xml_insert_element (UREFREPLACEMENT, START);
  480. execute_string ("%s", replacement);
  481. xml_insert_element (UREFREPLACEMENT, END);
  482. }
  483. xml_insert_element (UREF, END);
  484. }
  485. else if (html)
  486. { /* never need to show the url */
  487. add_html_elt ("<a href=");
  488. /* don't collapse `--' etc. in the url */
  489. in_fixed_width_font++;
  490. execute_string ("\"%s\"", url);
  491. in_fixed_width_font--;
  492. add_word (">");
  493. execute_string ("%s", *replacement ? replacement
  494. : (*desc ? desc : url));
  495. add_word ("</a>");
  496. }
  497. else if (*replacement) /* do not show the url */
  498. execute_string ("%s", replacement);
  499. else if (*desc) /* show both text and url */
  500. {
  501. execute_string ("%s ", desc);
  502. in_fixed_width_font++;
  503. execute_string ("(%s)", url);
  504. in_fixed_width_font--;
  505. }
  506. else /* no text at all, so have the url to show */
  507. {
  508. in_fixed_width_font++;
  509. execute_string ("%s%s%s",
  510. printing_index ? "" : "`",
  511. url,
  512. printing_index ? "" : "'");
  513. in_fixed_width_font--;
  514. }
  515. if (url)
  516. free (url);
  517. if (desc)
  518. free (desc);
  519. if (replacement)
  520. free (replacement);
  521. }
  522. }
  523. /* An email reference. */
  524. void
  525. cm_email (int arg)
  526. {
  527. if (arg == START)
  528. {
  529. char *addr = get_xref_token (1); /* expands all macros in email */
  530. char *name = get_xref_token (0);
  531. if (xml && docbook)
  532. {
  533. if (*name)
  534. {
  535. xml_insert_element_with_attribute (EMAIL, START,
  536. "url=\"mailto:%s\"",
  537. maybe_escaped_expansion (addr, 0, 1));
  538. execute_string ("%s", name);
  539. xml_insert_element (EMAIL, END);
  540. }
  541. else
  542. {
  543. xml_insert_element (EMAILADDRESS, START);
  544. execute_string ("%s", addr);
  545. xml_insert_element (EMAILADDRESS, END);
  546. }
  547. }
  548. else if (xml)
  549. {
  550. xml_insert_element (EMAIL, START);
  551. xml_insert_element (EMAILADDRESS, START);
  552. execute_string ("%s", addr);
  553. xml_insert_element (EMAILADDRESS, END);
  554. if (*name)
  555. {
  556. xml_insert_element (EMAILNAME, START);
  557. execute_string ("%s", name);
  558. xml_insert_element (EMAILNAME, END);
  559. }
  560. xml_insert_element (EMAIL, END);
  561. }
  562. else if (html)
  563. {
  564. add_html_elt ("<a href=");
  565. /* don't collapse `--' etc. in the address */
  566. in_fixed_width_font++;
  567. execute_string ("\"mailto:%s\"", addr);
  568. in_fixed_width_font--;
  569. add_word (">");
  570. execute_string ("%s", *name ? name : addr);
  571. add_word ("</a>");
  572. }
  573. else
  574. {
  575. execute_string ("%s%s", name, *name ? " " : "");
  576. in_fixed_width_font++;
  577. execute_string ("<%s>", addr);
  578. in_fixed_width_font--;
  579. }
  580. if (addr)
  581. free (addr);
  582. if (name)
  583. free (name);
  584. }
  585. }