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