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