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