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