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
add_xref_punctuation(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 *
get_xref_token(int expand)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
cm_xref(int arg)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
cm_pxref(int arg)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
cm_ref(int arg)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
cm_inforef(int arg)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
cm_uref(int arg)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
cm_email(int arg)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