Back to index

tetex-bin  3.0
xref.c
Go to the documentation of this file.
00001 /* xref.c -- cross references for Texinfo.
00002    $Id: xref.c,v 1.4 2004/12/21 17:28:35 karl Exp $
00003 
00004    Copyright (C) 2004 Free Software Foundation, Inc.
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2, or (at your option)
00009    any later version.
00010 
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014    GNU General Public License for more details.
00015 
00016    You should have received a copy of the GNU General Public License
00017    along with this program; if not, write to the Free Software Foundation,
00018    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
00019 
00020 #include "system.h"
00021 #include "cmds.h"
00022 #include "float.h"
00023 #include "html.h"
00024 #include "index.h"
00025 #include "macro.h"
00026 #include "makeinfo.h"
00027 #include "node.h"
00028 #include "xml.h"
00029 #include "xref.h"
00030 
00031 /* Flags which control initial output string for xrefs. */
00032 int px_ref_flag = 0;
00033 int ref_flag = 0;
00034 
00035 /* Called in the multiple-argument case to make sure we generate a valid
00036    Info reference.  In the single-argument case, the :: we output
00037    suffices for the Info readers to find the end of the reference.  */
00038 static void
00039 add_xref_punctuation (void)
00040 {
00041   if (px_ref_flag || ref_flag)  /* user inserts punct after @xref */
00042     {
00043       /* Check if there's already punctuation.  */
00044       int next_char = next_nonwhitespace_character ();
00045 
00046       if (next_char == -1)
00047         /* EOF while looking for punctuation, let's
00048            insert a period instead of crying.  */
00049         add_char ('.');
00050       else if (next_char != ',' && next_char != '.')
00051         /* period and comma terminate xrefs, and nothing else.  Instead
00052            of generating an Info reference that can't be followed,
00053            though, just insert a period.  Not pretty, but functional.  */
00054         add_char ('.');
00055     }
00056 }
00057 
00058 /* Return next comma-delimited argument, but do not cross a close-brace
00059    boundary.  Clean up whitespace, too.  If EXPAND is nonzero, replace
00060    the entire brace-delimited argument list with its expansion before
00061    looking for the next comma.  */
00062 char *
00063 get_xref_token (int expand)
00064 {
00065   char *string = 0;
00066 
00067   if (docbook)
00068     xml_in_xref_token = 1;
00069 
00070   if (expand)
00071     {
00072       int old_offset = input_text_offset;
00073       int old_lineno = line_number;
00074 
00075       get_until_in_braces ("}", &string);
00076       if (curchar () == '}')    /* as opposed to end of text */
00077         input_text_offset++;
00078       if (input_text_offset > old_offset)
00079         {
00080           int limit = input_text_offset;
00081 
00082           input_text_offset = old_offset;
00083           line_number = old_lineno;
00084           only_macro_expansion++;
00085           replace_with_expansion (input_text_offset, &limit);
00086           only_macro_expansion--;
00087         }
00088       free (string);
00089     }
00090 
00091   get_until_in_braces (",", &string);
00092   if (curchar () == ',')
00093     input_text_offset++;
00094   fix_whitespace (string);
00095 
00096   if (docbook)
00097     xml_in_xref_token = 0;
00098 
00099   return string;
00100 }
00101 
00102 
00103 /* NOTE: If you wonder why the HTML output is produced with such a
00104    peculiar mix of calls to add_word and execute_string, here's the
00105    reason.  get_xref_token (1) expands all macros in a reference, but
00106    any other commands, like @value, @@, etc., are left intact.  To
00107    expand them, we need to run the arguments through execute_string.
00108    However, characters like <, &, > and others cannot be let into
00109    execute_string, because they will be escaped.  See the mess?  */
00110 
00111 /* Make a cross reference. */
00112 void
00113 cm_xref (int arg)
00114 {
00115   if (arg == START)
00116     {
00117       char *arg1 = get_xref_token (1); /* expands all macros in xref */
00118       char *arg2 = get_xref_token (0);
00119       char *arg3 = get_xref_token (0);
00120       char *arg4 = get_xref_token (0);
00121       char *arg5 = get_xref_token (0);
00122       char *tem;
00123 
00124       /* "@xref{,Foo,, Bar, Baz} is not valid usage of @xref.  The
00125          first argument must never be blank." --rms.
00126          We hereby comply by disallowing such constructs.  */
00127       if (!*arg1)
00128         line_error (_("First argument to cross-reference may not be empty"));
00129 
00130       if (docbook)
00131         {
00132           if (!ref_flag)
00133             add_word (px_ref_flag || printing_index
00134                 ? (char *) _("see ") : (char *) _("See "));
00135 
00136           if (!*arg4 && !*arg5)
00137             {
00138               char *arg1_id = xml_id (arg1);
00139 
00140               if (*arg2 || *arg3)
00141                 {
00142                   xml_insert_element_with_attribute (XREFNODENAME, START,
00143                                                      "linkend=\"%s\"", arg1_id);
00144                   free (arg1_id);
00145                   execute_string ("%s", *arg3 ? arg3 : arg2);
00146                   xml_insert_element (XREFNODENAME, END);
00147                 }
00148               else
00149                 {
00150                   xml_insert_element_with_attribute (XREF, START,
00151                                                      "linkend=\"%s\"", arg1_id);
00152                   xml_insert_element (XREF, END);
00153                   free (arg1_id);
00154                 }
00155             }
00156           else if (*arg5)
00157             {
00158               add_word_args (_("See section ``%s'' in "), *arg3 ? arg3 : arg1);
00159               xml_insert_element (CITE, START);
00160               add_word (arg5);
00161               xml_insert_element (CITE, END);
00162             }
00163           else if (*arg4)
00164             {
00165               /* Very sad, we are losing xrefs made to ``info only'' books.  */
00166             }
00167         }
00168       else if (xml)
00169         {
00170           if (!ref_flag)
00171             add_word_args ("%s", px_ref_flag ? _("see ") : _("See "));
00172 
00173           xml_insert_element (XREF, START);
00174           xml_insert_element (XREFNODENAME, START);
00175           execute_string ("%s", arg1);
00176           xml_insert_element (XREFNODENAME, END);
00177           if (*arg2)
00178             {
00179               xml_insert_element (XREFINFONAME, START);
00180               execute_string ("%s", arg2);
00181               xml_insert_element (XREFINFONAME, END);
00182             }
00183           if (*arg3)
00184             {
00185               xml_insert_element (XREFPRINTEDDESC, START);
00186               execute_string ("%s", arg3);
00187               xml_insert_element (XREFPRINTEDDESC, END);
00188             }
00189           if (*arg4)
00190             {
00191               xml_insert_element (XREFINFOFILE, START);
00192               execute_string ("%s", arg4);
00193               xml_insert_element (XREFINFOFILE, END);
00194             }
00195           if (*arg5)
00196             {
00197               xml_insert_element (XREFPRINTEDNAME, START);
00198               execute_string ("%s", arg5);
00199               xml_insert_element (XREFPRINTEDNAME, END);
00200             }
00201           xml_insert_element (XREF, END);
00202         }
00203       else if (html)
00204         {
00205           if (!ref_flag)
00206             add_word_args ("%s", px_ref_flag ? _("see ") : _("See "));
00207         }
00208       else
00209         add_word_args ("%s", px_ref_flag ? "*note " : "*Note ");
00210 
00211       if (!xml)
00212         {
00213           if (*arg5 || *arg4)
00214             {
00215               /* arg1 - node name
00216                  arg2 - reference name
00217                  arg3 - title or topic (and reference name if arg2 is NULL)
00218                  arg4 - info file name
00219                  arg5 - printed manual title  */
00220               char *ref_name;
00221 
00222               if (!*arg2)
00223                 {
00224                   if (*arg3)
00225                     ref_name = arg3;
00226                   else
00227                     ref_name = arg1;
00228                 }
00229               else
00230                 ref_name = arg2;
00231 
00232               if (html)
00233                 { /* More to do eventually, down to Unicode
00234                      Normalization Form C.  See the HTML Xref nodes in
00235                      the manual.  */
00236                   char *file_arg = arg4;
00237                   add_html_elt ("<a href=");
00238 
00239                   {
00240                     /* If there's a directory part, ignore it.  */
00241                     char *p = strrchr (file_arg, '/');
00242                     if (p)
00243                       file_arg = p + 1;
00244 
00245                   /* If there's a dot, make it a NULL terminator, so the
00246                      extension does not get into the way.  */
00247                     p = strrchr (file_arg , '.');
00248                     if (p != NULL)
00249                       *p = 0;
00250                   }
00251                   
00252                   if (! *file_arg)
00253                 warning (_("Empty file name for HTML cross reference in `%s'"),
00254                            arg4);
00255 
00256                   /* Note that if we are splitting, and the referenced
00257                      tag is an anchor rather than a node, we will
00258                      produce a reference to a file whose name is
00259                      derived from the anchor name.  However, only
00260                      nodes create files, so we are referencing a
00261                      non-existent file.  cm_anchor, which see, deals
00262                      with that problem.  */
00263                   if (splitting)
00264                     execute_string ("\"../%s/", file_arg);
00265                   else
00266                     execute_string ("\"%s.html", file_arg);
00267                   /* Do not collapse -- to -, etc., in references.  */
00268                   in_fixed_width_font++;
00269                   tem = expansion (arg1, 0); /* expand @-commands in node */
00270                   in_fixed_width_font--;
00271                   add_anchor_name (tem, 1);
00272                   free (tem);
00273                   add_word ("\">");
00274                   execute_string ("%s",ref_name);
00275                   add_word ("</a>");
00276                 }
00277               else
00278                 {
00279                   execute_string ("%s:", ref_name);
00280                   in_fixed_width_font++;
00281                   execute_string (" (%s)%s", arg4, arg1);
00282                   add_xref_punctuation ();
00283                   in_fixed_width_font--;
00284                 }
00285 
00286               /* Free all of the arguments found. */
00287               if (arg1) free (arg1);
00288               if (arg2) free (arg2);
00289               if (arg3) free (arg3);
00290               if (arg4) free (arg4);
00291               if (arg5) free (arg5);
00292               return;
00293             }
00294           else
00295             remember_node_reference (arg1, line_number, followed_reference);
00296 
00297           if (*arg3)
00298             {
00299               if (html)
00300                 {
00301                   add_html_elt ("<a href=\"");
00302                   in_fixed_width_font++;
00303                   tem = expansion (arg1, 0);
00304                   in_fixed_width_font--;
00305                   add_anchor_name (tem, 1);
00306                   free (tem);
00307                   add_word ("\">");
00308                   execute_string ("%s", *arg2 ? arg2 : arg3);
00309                   add_word ("</a>");
00310                 }
00311               else
00312                 {
00313                   execute_string ("%s:", *arg2 ? arg2 : arg3);
00314                   in_fixed_width_font++;
00315                   execute_string (" %s", arg1);
00316                   add_xref_punctuation ();
00317                   in_fixed_width_font--;
00318                 }
00319             }
00320           else
00321             {
00322               if (html)
00323                 {
00324                   add_html_elt ("<a href=\"");
00325                   in_fixed_width_font++;
00326                   tem = expansion (arg1, 0);
00327                   in_fixed_width_font--;
00328                   add_anchor_name (tem, 1);
00329                   free (tem);
00330                   add_word ("\">");
00331                   if (*arg2)
00332                     execute_string ("%s", arg2);
00333                   else
00334                     {
00335                       char *fref = get_float_ref (arg1);
00336                       execute_string ("%s", fref ? fref : arg1);
00337                       free (fref);
00338                     }
00339                   add_word ("</a>");
00340                 }
00341               else
00342                 {
00343                   if (*arg2)
00344                     {
00345                       execute_string ("%s:", arg2);
00346                       in_fixed_width_font++;
00347                       execute_string (" %s", arg1);
00348                       add_xref_punctuation ();
00349                       in_fixed_width_font--;
00350                     }
00351                   else
00352                     {
00353                       char *fref = get_float_ref (arg1);
00354                       if (fref)
00355                         { /* Reference is being made to a float.  */
00356                           execute_string ("%s:", fref);
00357                           in_fixed_width_font++;
00358                           execute_string (" %s", arg1);
00359                           add_xref_punctuation ();
00360                           in_fixed_width_font--;
00361                         }
00362                       else
00363                         {
00364                           in_fixed_width_font++;
00365                           execute_string ("%s::", arg1);
00366                           in_fixed_width_font--;
00367                         }
00368                     }
00369                 }
00370             }
00371         }
00372       /* Free all of the arguments found. */
00373       if (arg1) free (arg1);
00374       if (arg2) free (arg2);
00375       if (arg3) free (arg3);
00376       if (arg4) free (arg4);
00377       if (arg5) free (arg5);
00378     }
00379   else
00380     { /* Check that the next non-whitespace character is valid to follow
00381          an xref (so Info readers can find the node names).
00382          `input_text_offset' is pointing at the "}" which ended the xref
00383          command.  This is not used for @pxref or @ref, since we insert
00384          the necessary punctuation above, if needed.  */
00385       int temp = next_nonwhitespace_character ();
00386 
00387       if (temp == -1)
00388         warning (_("End of file reached while looking for `.' or `,'"));
00389       else if (temp != '.' && temp != ',')
00390         warning (_("`.' or `,' must follow @%s, not `%c'"), command, temp);
00391     }
00392 }
00393 
00394 void
00395 cm_pxref (int arg)
00396 {
00397   if (arg == START)
00398     {
00399       px_ref_flag++;
00400       cm_xref (arg);
00401       px_ref_flag--;
00402     }
00403   /* cm_xref isn't called with arg == END, which disables the code near
00404      the end of cm_xref that checks for `.' or `,' after the
00405      cross-reference.  This is because cm_xref generates the required
00406      character itself (when needed) if px_ref_flag is set.  */
00407 }
00408 
00409 void
00410 cm_ref (int arg)
00411 {
00412   /* See the comments in cm_pxref about the checks for punctuation.  */
00413   if (arg == START)
00414     {
00415       ref_flag++;
00416       cm_xref (arg);
00417       ref_flag--;
00418     }
00419 }
00420 
00421 void
00422 cm_inforef (int arg)
00423 {
00424   if (arg == START)
00425     {
00426       char *node = get_xref_token (1); /* expands all macros in inforef */
00427       char *pname = get_xref_token (0);
00428       char *file = get_xref_token (0);
00429 
00430       /* (see comments at cm_xref).  */
00431       if (!*node)
00432         line_error (_("First argument to @inforef may not be empty"));
00433 
00434       if (xml && !docbook)
00435         {
00436           xml_insert_element (INFOREF, START);
00437           xml_insert_element (INFOREFNODENAME, START);
00438           execute_string ("%s", node);
00439           xml_insert_element (INFOREFNODENAME, END);
00440           if (*pname)
00441             {
00442               xml_insert_element (INFOREFREFNAME, START);
00443               execute_string ("%s", pname);
00444               xml_insert_element (INFOREFREFNAME, END);
00445             }
00446           xml_insert_element (INFOREFINFONAME, START);
00447           execute_string ("%s", file);
00448           xml_insert_element (INFOREFINFONAME, END);
00449 
00450           xml_insert_element (INFOREF, END);
00451         }
00452       else if (html)
00453         {
00454           char *tem;
00455 
00456           add_word ((char *) _("see "));
00457           /* html fixxme: revisit this */
00458           add_html_elt ("<a href=");
00459           if (splitting)
00460             execute_string ("\"../%s/", file);
00461           else
00462             execute_string ("\"%s.html", file);
00463           tem = expansion (node, 0);
00464           add_anchor_name (tem, 1);
00465           add_word ("\">");
00466           execute_string ("%s", *pname ? pname : tem);
00467           add_word ("</a>");
00468           free (tem);
00469         }
00470       else
00471         {
00472           if (*pname)
00473             execute_string ("*note %s: (%s)%s", pname, file, node);
00474           else
00475             execute_string ("*note (%s)%s::", file, node);
00476         }
00477 
00478       free (node);
00479       free (pname);
00480       free (file);
00481     }
00482 }
00483 
00484 /* A URL reference.  */
00485 void
00486 cm_uref (int arg)
00487 {
00488   if (arg == START)
00489     {
00490       extern int printing_index;
00491       char *url  = get_xref_token (1); /* expands all macros in uref */
00492       char *desc = get_xref_token (0);
00493       char *replacement = get_xref_token (0);
00494 
00495       if (docbook)
00496         {
00497           xml_insert_element_with_attribute (UREF, START, "url=\"%s\"",
00498               text_expansion (url));
00499           if (*replacement)
00500             execute_string ("%s", replacement);
00501           else if (*desc)
00502             execute_string ("%s", desc);
00503           else
00504             execute_string ("%s", url);
00505           xml_insert_element (UREF, END);
00506         }
00507       else if (xml)
00508         {
00509           xml_insert_element (UREF, START);
00510           xml_insert_element (UREFURL, START);
00511           execute_string ("%s", url);
00512           xml_insert_element (UREFURL, END);
00513           if (*desc)
00514             {
00515               xml_insert_element (UREFDESC, START);
00516               execute_string ("%s", desc);
00517               xml_insert_element (UREFDESC, END);
00518             }
00519           if (*replacement)
00520             {
00521               xml_insert_element (UREFREPLACEMENT, START);
00522               execute_string ("%s", replacement);
00523               xml_insert_element (UREFREPLACEMENT, END);
00524             }
00525           xml_insert_element (UREF, END);
00526         }
00527       else if (html)
00528         { /* never need to show the url */
00529           add_html_elt ("<a href=");
00530           /* don't collapse `--' etc. in the url */
00531           in_fixed_width_font++;
00532           execute_string ("\"%s\"", url);
00533           in_fixed_width_font--;
00534           add_word (">");
00535           execute_string ("%s", *replacement ? replacement
00536                                 : (*desc ? desc : url));
00537           add_word ("</a>");
00538         }
00539       else if (*replacement) /* do not show the url */
00540         execute_string ("%s", replacement);
00541       else if (*desc)        /* show both text and url */
00542         {
00543           execute_string ("%s ", desc);
00544           in_fixed_width_font++;
00545           execute_string ("(%s)", url);
00546           in_fixed_width_font--;
00547         }
00548       else /* no text at all, so have the url to show */
00549         {
00550           in_fixed_width_font++;
00551           execute_string ("%s%s%s",
00552                           printing_index ? "" : "`",
00553                           url,
00554                           printing_index ? "" : "'");
00555           in_fixed_width_font--;
00556         }
00557       if (url)
00558         free (url);
00559       if (desc)
00560         free (desc);
00561       if (replacement)
00562         free (replacement);
00563     }
00564 }
00565 
00566 /* An email reference.  */
00567 void
00568 cm_email (int arg)
00569 {
00570   if (arg == START)
00571     {
00572       char *addr = get_xref_token (1); /* expands all macros in email */
00573       char *name = get_xref_token (0);
00574 
00575       if (xml && docbook)
00576         {
00577           xml_insert_element_with_attribute (EMAIL, START, "url=\"mailto:%s\"", addr);
00578           if (*name)
00579               execute_string ("%s", name);
00580           xml_insert_element (EMAIL, END);
00581         }
00582       else if (xml)
00583         {
00584           xml_insert_element (EMAIL, START);
00585           xml_insert_element (EMAILADDRESS, START);
00586           execute_string ("%s", addr);
00587           xml_insert_element (EMAILADDRESS, END);
00588           if (*name)
00589             {
00590               xml_insert_element (EMAILNAME, START);
00591               execute_string ("%s", name);
00592               xml_insert_element (EMAILNAME, END);
00593             }
00594           xml_insert_element (EMAIL, END);
00595         }
00596       else if (html)
00597         {
00598           add_html_elt ("<a href=");
00599           /* don't collapse `--' etc. in the address */
00600           in_fixed_width_font++;
00601           execute_string ("\"mailto:%s\"", addr);
00602           in_fixed_width_font--;
00603           add_word (">");
00604           execute_string ("%s", *name ? name : addr);
00605           add_word ("</a>");
00606         }
00607       else
00608         {
00609           execute_string ("%s%s", name, *name ? " "  : "");
00610           in_fixed_width_font++;
00611           execute_string ("<%s>", addr);
00612           in_fixed_width_font--;
00613         }
00614 
00615       if (addr)
00616         free (addr);
00617       if (name)
00618         free (name);
00619     }
00620 }