392 lines
11 KiB
C
392 lines
11 KiB
C
/* footnote.c -- footnotes for Texinfo.
|
||
$Id: footnote.c,v 1.7 2004/04/11 17:56:47 karl Exp $
|
||
|
||
Copyright (C) 1998, 1999, 2002 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 "footnote.h"
|
||
#include "macro.h"
|
||
#include "makeinfo.h"
|
||
#include "node.h"
|
||
#include "xml.h"
|
||
#include "xref.h"
|
||
|
||
/* Nonzero means that the footnote style for this document was set on
|
||
the command line, which overrides any other settings. */
|
||
int footnote_style_preset = 0;
|
||
|
||
/* The current footnote number in this node. Each time a new node is
|
||
started this is reset to 1. */
|
||
int current_footnote_number = 1;
|
||
|
||
/* Nonzero means we automatically number footnotes with no specified marker. */
|
||
int number_footnotes = 1;
|
||
|
||
/* Nonzero means we are currently outputting footnotes. */
|
||
int already_outputting_pending_notes = 0;
|
||
|
||
|
||
/* Footnotes can be handled in one of two ways:
|
||
|
||
separate_node:
|
||
Make them look like followed references, with the reference
|
||
destinations in a makeinfo manufactured node or,
|
||
end_node:
|
||
Make them appear at the bottom of the node that they originally
|
||
appeared in. */
|
||
|
||
#define separate_node 0
|
||
#define end_node 1
|
||
|
||
int footnote_style = end_node;
|
||
int first_footnote_this_node = 1;
|
||
int footnote_count = 0;
|
||
|
||
/* Set the footnote style based on the style identifier in STRING. */
|
||
int
|
||
set_footnote_style (char *string)
|
||
{
|
||
if (strcasecmp (string, "separate") == 0)
|
||
footnote_style = separate_node;
|
||
else if (strcasecmp (string, "end") == 0)
|
||
footnote_style = end_node;
|
||
else
|
||
return -1;
|
||
|
||
return 0;
|
||
}
|
||
|
||
void
|
||
cm_footnotestyle (void)
|
||
{
|
||
char *arg;
|
||
|
||
get_rest_of_line (1, &arg);
|
||
|
||
/* If set on command line, do not change the footnote style. */
|
||
if (!footnote_style_preset && set_footnote_style (arg) != 0)
|
||
line_error (_("Bad argument to %c%s"), COMMAND_PREFIX, command);
|
||
|
||
free (arg);
|
||
}
|
||
|
||
typedef struct fn
|
||
{
|
||
struct fn *next;
|
||
char *marker;
|
||
char *note;
|
||
int number;
|
||
} FN;
|
||
|
||
FN *pending_notes = NULL;
|
||
|
||
/* A method for remembering footnotes. Note that this list gets output
|
||
at the end of the current node. */
|
||
static void
|
||
remember_note (char *marker, char *note)
|
||
{
|
||
FN *temp = xmalloc (sizeof (FN));
|
||
|
||
temp->marker = xstrdup (marker);
|
||
temp->note = xstrdup (note);
|
||
temp->next = pending_notes;
|
||
temp->number = current_footnote_number;
|
||
pending_notes = temp;
|
||
footnote_count++;
|
||
}
|
||
|
||
/* How to get rid of existing footnotes. */
|
||
static void
|
||
free_pending_notes (void)
|
||
{
|
||
FN *temp;
|
||
|
||
while ((temp = pending_notes))
|
||
{
|
||
free (temp->marker);
|
||
free (temp->note);
|
||
pending_notes = pending_notes->next;
|
||
free (temp);
|
||
}
|
||
first_footnote_this_node = 1;
|
||
footnote_count = 0;
|
||
current_footnote_number = 1; /* for html */
|
||
}
|
||
|
||
/* What to do when you see a @footnote construct. */
|
||
|
||
/* Handle a "footnote".
|
||
footnote *{this is a footnote}
|
||
where "*" is the (optional) marker character for this note. */
|
||
void
|
||
cm_footnote (void)
|
||
{
|
||
char *marker;
|
||
char *note;
|
||
|
||
get_until ("{", &marker);
|
||
canon_white (marker);
|
||
|
||
if (macro_expansion_output_stream && !executing_string)
|
||
append_to_expansion_output (input_text_offset + 1); /* include the { */
|
||
|
||
/* Read the argument in braces. */
|
||
if (curchar () != '{')
|
||
{
|
||
line_error (_("`%c%s' needs an argument `{...}', not just `%s'"),
|
||
COMMAND_PREFIX, command, marker);
|
||
free (marker);
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
int len;
|
||
int braces = 1;
|
||
int loc = ++input_text_offset;
|
||
|
||
while (braces)
|
||
{
|
||
if (loc == input_text_length)
|
||
{
|
||
line_error (_("No closing brace for footnote `%s'"), marker);
|
||
return;
|
||
}
|
||
|
||
if (input_text[loc] == '{')
|
||
braces++;
|
||
else if (input_text[loc] == '}')
|
||
braces--;
|
||
else if (input_text[loc] == '\n')
|
||
line_number++;
|
||
|
||
loc++;
|
||
}
|
||
|
||
len = (loc - input_text_offset) - 1;
|
||
note = xmalloc (len + 1);
|
||
memcpy (note, &input_text[input_text_offset], len);
|
||
note[len] = 0;
|
||
input_text_offset = loc;
|
||
}
|
||
|
||
/* Must write the macro-expanded argument to the macro expansion
|
||
output stream. This is like the case in index_add_arg. */
|
||
if (macro_expansion_output_stream && !executing_string)
|
||
{
|
||
/* Calling me_execute_string on a lone } provokes an error, since
|
||
as far as the reader knows there is no matching {. We wrote
|
||
the { above in the call to append_to_expansion_output. */
|
||
me_execute_string_keep_state (note, "}");
|
||
}
|
||
|
||
if (!current_node || !*current_node)
|
||
{
|
||
line_error (_("Footnote defined without parent node"));
|
||
free (marker);
|
||
free (note);
|
||
return;
|
||
}
|
||
|
||
/* output_pending_notes is non-reentrant (it uses a global data
|
||
structure pending_notes, which it frees before it returns), and
|
||
TeX doesn't grok footnotes inside footnotes anyway. Disallow
|
||
that. */
|
||
if (already_outputting_pending_notes)
|
||
{
|
||
line_error (_("Footnotes inside footnotes are not allowed"));
|
||
free (marker);
|
||
free (note);
|
||
return;
|
||
}
|
||
|
||
if (!*marker)
|
||
{
|
||
free (marker);
|
||
|
||
if (number_footnotes)
|
||
{
|
||
marker = xmalloc (10);
|
||
sprintf (marker, "%d", current_footnote_number);
|
||
}
|
||
else
|
||
marker = xstrdup ("*");
|
||
}
|
||
|
||
if (xml)
|
||
xml_insert_footnote (note);
|
||
else
|
||
{
|
||
remember_note (marker, note);
|
||
|
||
/* fixme: html: footnote processing needs work; we currently ignore
|
||
the style requested; we could clash with a node name of the form
|
||
`fn-<n>', though that's unlikely. */
|
||
if (html)
|
||
{
|
||
/* Hyperlink also serves as an anchor (mnemonic: fnd is footnote
|
||
definition.) */
|
||
add_html_elt ("<a rel=\"footnote\" href=");
|
||
add_word_args ("\"#fn-%d\" name=\"fnd-%d\"><sup>%s</sup></a>",
|
||
current_footnote_number, current_footnote_number,
|
||
marker);
|
||
}
|
||
else
|
||
/* Your method should at least insert MARKER. */
|
||
switch (footnote_style)
|
||
{
|
||
case separate_node:
|
||
add_word_args ("(%s)", marker);
|
||
execute_string (" (*note %s-Footnote-%d::)",
|
||
current_node, current_footnote_number);
|
||
if (first_footnote_this_node)
|
||
{
|
||
char *temp_string, *expanded_ref;
|
||
|
||
temp_string = xmalloc (strlen (current_node)
|
||
+ strlen ("-Footnotes") + 1);
|
||
|
||
strcpy (temp_string, current_node);
|
||
strcat (temp_string, "-Footnotes");
|
||
expanded_ref = expansion (temp_string, 0);
|
||
remember_node_reference (expanded_ref, line_number,
|
||
followed_reference);
|
||
free (temp_string);
|
||
free (expanded_ref);
|
||
first_footnote_this_node = 0;
|
||
}
|
||
break;
|
||
|
||
case end_node:
|
||
add_word_args ("(%s)", marker);
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
current_footnote_number++;
|
||
}
|
||
free (marker);
|
||
free (note);
|
||
}
|
||
|
||
/* Output the footnotes. We are at the end of the current node. */
|
||
void
|
||
output_pending_notes (void)
|
||
{
|
||
FN *footnote = pending_notes;
|
||
|
||
if (!pending_notes)
|
||
return;
|
||
|
||
if (html)
|
||
{
|
||
add_html_block_elt ("<div class=\"footnote\">\n<hr>\n");
|
||
/* We add an anchor here so @printindex can refer to this point
|
||
(as the node name) for entries defined in footnotes. */
|
||
if (!splitting)
|
||
add_word ("<a name=\"texinfo-footnotes-in-document\"></a>");
|
||
add_word_args ("<h4>%s</h4>", (char *) _("Footnotes"));
|
||
}
|
||
else
|
||
switch (footnote_style)
|
||
{
|
||
case separate_node:
|
||
{
|
||
char *old_current_node = current_node;
|
||
char *old_command = xstrdup (command);
|
||
|
||
already_outputting_pending_notes++;
|
||
execute_string ("%cnode %s-Footnotes,,,%s\n",
|
||
COMMAND_PREFIX, current_node, current_node);
|
||
already_outputting_pending_notes--;
|
||
current_node = old_current_node;
|
||
free (command);
|
||
command = old_command;
|
||
}
|
||
break;
|
||
|
||
case end_node:
|
||
close_paragraph ();
|
||
in_fixed_width_font++;
|
||
/* This string should be translated according to the
|
||
@documentlanguage, not the current LANG. We can't do that
|
||
yet, so leave it in English. */
|
||
execute_string ("---------- Footnotes ----------\n\n");
|
||
in_fixed_width_font--;
|
||
break;
|
||
}
|
||
|
||
/* Handle the footnotes in reverse order. */
|
||
{
|
||
int save_in_fixed_width_font = in_fixed_width_font;
|
||
FN **array = xmalloc ((footnote_count + 1) * sizeof (FN *));
|
||
array[footnote_count] = NULL;
|
||
|
||
while (--footnote_count > -1)
|
||
{
|
||
array[footnote_count] = footnote;
|
||
footnote = footnote->next;
|
||
}
|
||
|
||
filling_enabled = 1;
|
||
indented_fill = 1;
|
||
in_fixed_width_font = 0;
|
||
|
||
while ((footnote = array[++footnote_count]))
|
||
{
|
||
if (html)
|
||
{
|
||
/* Make the text of every footnote begin a separate paragraph. */
|
||
add_html_block_elt ("<p class=\"footnote\"><small>");
|
||
/* Make footnote number a link to its definition. */
|
||
add_word_args ("[<a name=\"fn-%d\" href=\"#fnd-%d\">%d</a>]",
|
||
footnote->number, footnote->number, footnote->number);
|
||
add_word ("</small> ");
|
||
already_outputting_pending_notes++;
|
||
execute_string ("%s", footnote->note);
|
||
already_outputting_pending_notes--;
|
||
add_word ("</p>\n");
|
||
}
|
||
else
|
||
{
|
||
char *old_current_node = current_node;
|
||
char *old_command = xstrdup (command);
|
||
|
||
already_outputting_pending_notes++;
|
||
execute_string ("%canchor{%s-Footnote-%d}(%s) %s",
|
||
COMMAND_PREFIX, current_node, footnote->number,
|
||
footnote->marker, footnote->note);
|
||
already_outputting_pending_notes--;
|
||
current_node = old_current_node;
|
||
free (command);
|
||
command = old_command;
|
||
}
|
||
|
||
close_paragraph ();
|
||
}
|
||
|
||
if (html)
|
||
add_word ("<hr></div>");
|
||
close_paragraph ();
|
||
free (array);
|
||
|
||
in_fixed_width_font = save_in_fixed_width_font;
|
||
}
|
||
|
||
free_pending_notes ();
|
||
}
|