xref: /openbsd/gnu/usr.bin/texinfo/makeinfo/xref.c (revision a1acfa9b)
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