/* xref.c -- cross references for Texinfo. $Id: xref.c,v 1.4 2004/12/21 17:28:35 karl Exp $ Copyright (C) 2004 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "system.h" #include "cmds.h" #include "float.h" #include "html.h" #include "index.h" #include "macro.h" #include "makeinfo.h" #include "node.h" #include "xml.h" #include "xref.h" /* Flags which control initial output string for xrefs. */ int px_ref_flag = 0; int ref_flag = 0; /* Called in the multiple-argument case to make sure we generate a valid Info reference. In the single-argument case, the :: we output suffices for the Info readers to find the end of the reference. */ static void add_xref_punctuation (void) { if (px_ref_flag || ref_flag) /* user inserts punct after @xref */ { /* Check if there's already punctuation. */ int next_char = next_nonwhitespace_character (); if (next_char == -1) /* EOF while looking for punctuation, let's insert a period instead of crying. */ add_char ('.'); else if (next_char != ',' && next_char != '.') /* period and comma terminate xrefs, and nothing else. Instead of generating an Info reference that can't be followed, though, just insert a period. Not pretty, but functional. */ add_char ('.'); } } /* Return next comma-delimited argument, but do not cross a close-brace boundary. Clean up whitespace, too. If EXPAND is nonzero, replace the entire brace-delimited argument list with its expansion before looking for the next comma. */ char * get_xref_token (int expand) { char *string = 0; if (docbook) xml_in_xref_token = 1; if (expand) { int old_offset = input_text_offset; int old_lineno = line_number; get_until_in_braces ("}", &string); if (curchar () == '}') /* as opposed to end of text */ input_text_offset++; if (input_text_offset > old_offset) { int limit = input_text_offset; input_text_offset = old_offset; line_number = old_lineno; only_macro_expansion++; replace_with_expansion (input_text_offset, &limit); only_macro_expansion--; } free (string); } get_until_in_braces (",", &string); if (curchar () == ',') input_text_offset++; fix_whitespace (string); if (docbook) xml_in_xref_token = 0; return string; } /* NOTE: If you wonder why the HTML output is produced with such a peculiar mix of calls to add_word and execute_string, here's the reason. get_xref_token (1) expands all macros in a reference, but any other commands, like @value, @@, etc., are left intact. To expand them, we need to run the arguments through execute_string. However, characters like <, &, > and others cannot be let into execute_string, because they will be escaped. See the mess? */ /* Make a cross reference. */ void cm_xref (int arg) { if (arg == START) { char *arg1 = get_xref_token (1); /* expands all macros in xref */ char *arg2 = get_xref_token (0); char *arg3 = get_xref_token (0); char *arg4 = get_xref_token (0); char *arg5 = get_xref_token (0); char *tem; /* "@xref{,Foo,, Bar, Baz} is not valid usage of @xref. The first argument must never be blank." --rms. We hereby comply by disallowing such constructs. */ if (!*arg1) line_error (_("First argument to cross-reference may not be empty")); if (docbook) { if (!ref_flag) add_word (px_ref_flag || printing_index ? (char *) _("see ") : (char *) _("See ")); if (!*arg4 && !*arg5) { char *arg1_id = xml_id (arg1); if (*arg2 || *arg3) { xml_insert_element_with_attribute (XREFNODENAME, START, "linkend=\"%s\"", arg1_id); free (arg1_id); execute_string ("%s", *arg3 ? arg3 : arg2); xml_insert_element (XREFNODENAME, END); } else { xml_insert_element_with_attribute (XREF, START, "linkend=\"%s\"", arg1_id); xml_insert_element (XREF, END); free (arg1_id); } } else if (*arg5) { add_word_args (_("See section ``%s'' in "), *arg3 ? arg3 : arg1); xml_insert_element (CITE, START); add_word (arg5); xml_insert_element (CITE, END); } else if (*arg4) { /* Very sad, we are losing xrefs made to ``info only'' books. */ } } else if (xml) { if (!ref_flag) add_word_args ("%s", px_ref_flag ? _("see ") : _("See ")); xml_insert_element (XREF, START); xml_insert_element (XREFNODENAME, START); execute_string ("%s", arg1); xml_insert_element (XREFNODENAME, END); if (*arg2) { xml_insert_element (XREFINFONAME, START); execute_string ("%s", arg2); xml_insert_element (XREFINFONAME, END); } if (*arg3) { xml_insert_element (XREFPRINTEDDESC, START); execute_string ("%s", arg3); xml_insert_element (XREFPRINTEDDESC, END); } if (*arg4) { xml_insert_element (XREFINFOFILE, START); execute_string ("%s", arg4); xml_insert_element (XREFINFOFILE, END); } if (*arg5) { xml_insert_element (XREFPRINTEDNAME, START); execute_string ("%s", arg5); xml_insert_element (XREFPRINTEDNAME, END); } xml_insert_element (XREF, END); } else if (html) { if (!ref_flag) add_word_args ("%s", px_ref_flag ? _("see ") : _("See ")); } else add_word_args ("%s", px_ref_flag ? "*note " : "*Note "); if (!xml) { if (*arg5 || *arg4) { /* arg1 - node name arg2 - reference name arg3 - title or topic (and reference name if arg2 is NULL) arg4 - info file name arg5 - printed manual title */ char *ref_name; if (!*arg2) { if (*arg3) ref_name = arg3; else ref_name = arg1; } else ref_name = arg2; if (html) { /* More to do eventually, down to Unicode Normalization Form C. See the HTML Xref nodes in the manual. */ char *file_arg = arg4; add_html_elt (""); execute_string ("%s",ref_name); add_word (""); } else { execute_string ("%s:", ref_name); in_fixed_width_font++; execute_string (" (%s)%s", arg4, arg1); add_xref_punctuation (); in_fixed_width_font--; } /* Free all of the arguments found. */ if (arg1) free (arg1); if (arg2) free (arg2); if (arg3) free (arg3); if (arg4) free (arg4); if (arg5) free (arg5); return; } else remember_node_reference (arg1, line_number, followed_reference); if (*arg3) { if (html) { add_html_elt (""); execute_string ("%s", *arg2 ? arg2 : arg3); add_word (""); } else { execute_string ("%s:", *arg2 ? arg2 : arg3); in_fixed_width_font++; execute_string (" %s", arg1); add_xref_punctuation (); in_fixed_width_font--; } } else { if (html) { add_html_elt (""); if (*arg2) execute_string ("%s", arg2); else { char *fref = get_float_ref (arg1); execute_string ("%s", fref ? fref : arg1); free (fref); } add_word (""); } else { if (*arg2) { execute_string ("%s:", arg2); in_fixed_width_font++; execute_string (" %s", arg1); add_xref_punctuation (); in_fixed_width_font--; } else { char *fref = get_float_ref (arg1); if (fref) { /* Reference is being made to a float. */ execute_string ("%s:", fref); in_fixed_width_font++; execute_string (" %s", arg1); add_xref_punctuation (); in_fixed_width_font--; } else { in_fixed_width_font++; execute_string ("%s::", arg1); in_fixed_width_font--; } } } } } /* Free all of the arguments found. */ if (arg1) free (arg1); if (arg2) free (arg2); if (arg3) free (arg3); if (arg4) free (arg4); if (arg5) free (arg5); } else { /* Check that the next non-whitespace character is valid to follow an xref (so Info readers can find the node names). `input_text_offset' is pointing at the "}" which ended the xref command. This is not used for @pxref or @ref, since we insert the necessary punctuation above, if needed. */ int temp = next_nonwhitespace_character (); if (temp == -1) warning (_("End of file reached while looking for `.' or `,'")); else if (temp != '.' && temp != ',') warning (_("`.' or `,' must follow @%s, not `%c'"), command, temp); } } void cm_pxref (int arg) { if (arg == START) { px_ref_flag++; cm_xref (arg); px_ref_flag--; } /* cm_xref isn't called with arg == END, which disables the code near the end of cm_xref that checks for `.' or `,' after the cross-reference. This is because cm_xref generates the required character itself (when needed) if px_ref_flag is set. */ } void cm_ref (int arg) { /* See the comments in cm_pxref about the checks for punctuation. */ if (arg == START) { ref_flag++; cm_xref (arg); ref_flag--; } } void cm_inforef (int arg) { if (arg == START) { char *node = get_xref_token (1); /* expands all macros in inforef */ char *pname = get_xref_token (0); char *file = get_xref_token (0); /* (see comments at cm_xref). */ if (!*node) line_error (_("First argument to @inforef may not be empty")); if (xml && !docbook) { xml_insert_element (INFOREF, START); xml_insert_element (INFOREFNODENAME, START); execute_string ("%s", node); xml_insert_element (INFOREFNODENAME, END); if (*pname) { xml_insert_element (INFOREFREFNAME, START); execute_string ("%s", pname); xml_insert_element (INFOREFREFNAME, END); } xml_insert_element (INFOREFINFONAME, START); execute_string ("%s", file); xml_insert_element (INFOREFINFONAME, END); xml_insert_element (INFOREF, END); } else if (html) { char *tem; add_word ((char *) _("see ")); /* html fixxme: revisit this */ add_html_elt (""); execute_string ("%s", *pname ? pname : tem); add_word (""); free (tem); } else { if (*pname) execute_string ("*note %s: (%s)%s", pname, file, node); else execute_string ("*note (%s)%s::", file, node); } free (node); free (pname); free (file); } } /* A URL reference. */ void cm_uref (int arg) { if (arg == START) { extern int printing_index; char *url = get_xref_token (1); /* expands all macros in uref */ char *desc = get_xref_token (0); char *replacement = get_xref_token (0); if (docbook) { xml_insert_element_with_attribute (UREF, START, "url=\"%s\"", text_expansion (url)); if (*replacement) execute_string ("%s", replacement); else if (*desc) execute_string ("%s", desc); else execute_string ("%s", url); xml_insert_element (UREF, END); } else if (xml) { xml_insert_element (UREF, START); xml_insert_element (UREFURL, START); execute_string ("%s", url); xml_insert_element (UREFURL, END); if (*desc) { xml_insert_element (UREFDESC, START); execute_string ("%s", desc); xml_insert_element (UREFDESC, END); } if (*replacement) { xml_insert_element (UREFREPLACEMENT, START); execute_string ("%s", replacement); xml_insert_element (UREFREPLACEMENT, END); } xml_insert_element (UREF, END); } else if (html) { /* never need to show the url */ add_html_elt (""); execute_string ("%s", *replacement ? replacement : (*desc ? desc : url)); add_word (""); } else if (*replacement) /* do not show the url */ execute_string ("%s", replacement); else if (*desc) /* show both text and url */ { execute_string ("%s ", desc); in_fixed_width_font++; execute_string ("(%s)", url); in_fixed_width_font--; } else /* no text at all, so have the url to show */ { in_fixed_width_font++; execute_string ("%s%s%s", printing_index ? "" : "`", url, printing_index ? "" : "'"); in_fixed_width_font--; } if (url) free (url); if (desc) free (desc); if (replacement) free (replacement); } } /* An email reference. */ void cm_email (int arg) { if (arg == START) { char *addr = get_xref_token (1); /* expands all macros in email */ char *name = get_xref_token (0); if (xml && docbook) { xml_insert_element_with_attribute (EMAIL, START, "url=\"mailto:%s\"", addr); if (*name) execute_string ("%s", name); xml_insert_element (EMAIL, END); } else if (xml) { xml_insert_element (EMAIL, START); xml_insert_element (EMAILADDRESS, START); execute_string ("%s", addr); xml_insert_element (EMAILADDRESS, END); if (*name) { xml_insert_element (EMAILNAME, START); execute_string ("%s", name); xml_insert_element (EMAILNAME, END); } xml_insert_element (EMAIL, END); } else if (html) { add_html_elt (""); execute_string ("%s", *name ? name : addr); add_word (""); } else { execute_string ("%s%s", name, *name ? " " : ""); in_fixed_width_font++; execute_string ("<%s>", addr); in_fixed_width_font--; } if (addr) free (addr); if (name) free (name); } }