Home | History | Annotate | Line # | Download | only in makeinfo
      1 /*	$NetBSD: xref.c,v 1.4 2025/12/31 22:18:50 oster 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
     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 *
     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
    115 cm_xref (int arg, int arg2, int arg3)
    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
    397 cm_pxref (int arg, int arg2, int arg3)
    398 {
    399   if (arg == START)
    400     {
    401       px_ref_flag++;
    402       cm_xref (arg, 0, 0);
    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
    412 cm_ref (int arg, int arg2, int arg3)
    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, 0, 0);
    419       ref_flag--;
    420     }
    421 }
    422 
    423 void
    424 cm_inforef (int arg, int arg2, int arg3)
    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
    488 cm_uref (int arg, int arg2, int arg3)
    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
    570 cm_email (int arg, int arg2, int arg3)
    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