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