1 /* footnote.c -- footnotes for Texinfo.
2 $Id: footnote.c,v 1.1.1.3 2006/07/17 16:03:46 espie Exp $
3
4 Copyright (C) 1998, 1999, 2002 Free Software Foundation, Inc.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software Foundation,
18 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19
20 #include "system.h"
21 #include "footnote.h"
22 #include "macro.h"
23 #include "makeinfo.h"
24 #include "node.h"
25 #include "xml.h"
26 #include "xref.h"
27
28 /* Nonzero means that the footnote style for this document was set on
29 the command line, which overrides any other settings. */
30 int footnote_style_preset = 0;
31
32 /* The current footnote number in this node. Each time a new node is
33 started this is reset to 1. */
34 int current_footnote_number = 1;
35
36 /* Nonzero means we automatically number footnotes with no specified marker. */
37 int number_footnotes = 1;
38
39 /* Nonzero means we are currently outputting footnotes. */
40 int already_outputting_pending_notes = 0;
41
42
43 /* Footnotes can be handled in one of two ways:
44
45 separate_node:
46 Make them look like followed references, with the reference
47 destinations in a makeinfo manufactured node or,
48 end_node:
49 Make them appear at the bottom of the node that they originally
50 appeared in. */
51
52 #define separate_node 0
53 #define end_node 1
54
55 int footnote_style = end_node;
56 int first_footnote_this_node = 1;
57 int footnote_count = 0;
58
59 /* Set the footnote style based on the style identifier in STRING. */
60 int
set_footnote_style(char * string)61 set_footnote_style (char *string)
62 {
63 if (strcasecmp (string, "separate") == 0)
64 footnote_style = separate_node;
65 else if (strcasecmp (string, "end") == 0)
66 footnote_style = end_node;
67 else
68 return -1;
69
70 return 0;
71 }
72
73 void
cm_footnotestyle(void)74 cm_footnotestyle (void)
75 {
76 char *arg;
77
78 get_rest_of_line (1, &arg);
79
80 /* If set on command line, do not change the footnote style. */
81 if (!footnote_style_preset && set_footnote_style (arg) != 0)
82 line_error (_("Bad argument to %c%s"), COMMAND_PREFIX, command);
83
84 free (arg);
85 }
86
87 typedef struct fn
88 {
89 struct fn *next;
90 char *marker;
91 char *note;
92 int number;
93 } FN;
94
95 FN *pending_notes = NULL;
96
97 /* A method for remembering footnotes. Note that this list gets output
98 at the end of the current node. */
99 static void
remember_note(char * marker,char * note)100 remember_note (char *marker, char *note)
101 {
102 FN *temp = xmalloc (sizeof (FN));
103
104 temp->marker = xstrdup (marker);
105 temp->note = xstrdup (note);
106 temp->next = pending_notes;
107 temp->number = current_footnote_number;
108 pending_notes = temp;
109 footnote_count++;
110 }
111
112 /* How to get rid of existing footnotes. */
113 static void
free_pending_notes(void)114 free_pending_notes (void)
115 {
116 FN *temp;
117
118 while ((temp = pending_notes))
119 {
120 free (temp->marker);
121 free (temp->note);
122 pending_notes = pending_notes->next;
123 free (temp);
124 }
125 first_footnote_this_node = 1;
126 footnote_count = 0;
127 current_footnote_number = 1; /* for html */
128 }
129
130 /* What to do when you see a @footnote construct. */
131
132 /* Handle a "footnote".
133 footnote *{this is a footnote}
134 where "*" is the (optional) marker character for this note. */
135 void
cm_footnote(void)136 cm_footnote (void)
137 {
138 char *marker;
139 char *note;
140
141 get_until ("{", &marker);
142 canon_white (marker);
143
144 if (macro_expansion_output_stream && !executing_string)
145 append_to_expansion_output (input_text_offset + 1); /* include the { */
146
147 /* Read the argument in braces. */
148 if (curchar () != '{')
149 {
150 line_error (_("`%c%s' needs an argument `{...}', not just `%s'"),
151 COMMAND_PREFIX, command, marker);
152 free (marker);
153 return;
154 }
155 else
156 {
157 int len;
158 int braces = 1;
159 int loc = ++input_text_offset;
160
161 while (braces)
162 {
163 if (loc == input_text_length)
164 {
165 line_error (_("No closing brace for footnote `%s'"), marker);
166 return;
167 }
168
169 if (input_text[loc] == '{')
170 braces++;
171 else if (input_text[loc] == '}')
172 braces--;
173 else if (input_text[loc] == '\n')
174 line_number++;
175
176 loc++;
177 }
178
179 len = (loc - input_text_offset) - 1;
180 note = xmalloc (len + 1);
181 memcpy (note, &input_text[input_text_offset], len);
182 note[len] = 0;
183 input_text_offset = loc;
184 }
185
186 /* Must write the macro-expanded argument to the macro expansion
187 output stream. This is like the case in index_add_arg. */
188 if (macro_expansion_output_stream && !executing_string)
189 {
190 /* Calling me_execute_string on a lone } provokes an error, since
191 as far as the reader knows there is no matching {. We wrote
192 the { above in the call to append_to_expansion_output. */
193 me_execute_string_keep_state (note, "}");
194 }
195
196 if (!current_node || !*current_node)
197 {
198 line_error (_("Footnote defined without parent node"));
199 free (marker);
200 free (note);
201 return;
202 }
203
204 /* output_pending_notes is non-reentrant (it uses a global data
205 structure pending_notes, which it frees before it returns), and
206 TeX doesn't grok footnotes inside footnotes anyway. Disallow
207 that. */
208 if (already_outputting_pending_notes)
209 {
210 line_error (_("Footnotes inside footnotes are not allowed"));
211 free (marker);
212 free (note);
213 return;
214 }
215
216 if (!*marker)
217 {
218 free (marker);
219
220 if (number_footnotes)
221 {
222 marker = xmalloc (10);
223 sprintf (marker, "%d", current_footnote_number);
224 }
225 else
226 marker = xstrdup ("*");
227 }
228
229 if (xml)
230 xml_insert_footnote (note);
231 else
232 {
233 remember_note (marker, note);
234
235 /* fixme: html: footnote processing needs work; we currently ignore
236 the style requested; we could clash with a node name of the form
237 `fn-<n>', though that's unlikely. */
238 if (html)
239 {
240 /* Hyperlink also serves as an anchor (mnemonic: fnd is footnote
241 definition.) */
242 add_html_elt ("<a rel=\"footnote\" href=");
243 add_word_args ("\"#fn-%d\" name=\"fnd-%d\"><sup>%s</sup></a>",
244 current_footnote_number, current_footnote_number,
245 marker);
246 }
247 else
248 /* Your method should at least insert MARKER. */
249 switch (footnote_style)
250 {
251 case separate_node:
252 add_word_args ("(%s)", marker);
253 execute_string (" (*note %s-Footnote-%d::)",
254 current_node, current_footnote_number);
255 if (first_footnote_this_node)
256 {
257 char *temp_string, *expanded_ref;
258
259 temp_string = xmalloc (strlen (current_node)
260 + strlen ("-Footnotes") + 1);
261
262 strcpy (temp_string, current_node);
263 strcat (temp_string, "-Footnotes");
264 expanded_ref = expansion (temp_string, 0);
265 remember_node_reference (expanded_ref, line_number,
266 followed_reference);
267 free (temp_string);
268 free (expanded_ref);
269 first_footnote_this_node = 0;
270 }
271 break;
272
273 case end_node:
274 add_word_args ("(%s)", marker);
275 break;
276
277 default:
278 break;
279 }
280 current_footnote_number++;
281 }
282 free (marker);
283 free (note);
284 }
285
286 /* Output the footnotes. We are at the end of the current node. */
287 void
output_pending_notes(void)288 output_pending_notes (void)
289 {
290 FN *footnote = pending_notes;
291
292 if (!pending_notes)
293 return;
294
295 if (html)
296 {
297 add_html_block_elt ("<div class=\"footnote\">\n<hr>\n");
298 /* We add an anchor here so @printindex can refer to this point
299 (as the node name) for entries defined in footnotes. */
300 if (!splitting)
301 add_word ("<a name=\"texinfo-footnotes-in-document\"></a>");
302 add_word_args ("<h4>%s</h4>", (char *) _("Footnotes"));
303 }
304 else
305 switch (footnote_style)
306 {
307 case separate_node:
308 {
309 char *old_current_node = current_node;
310 char *old_command = xstrdup (command);
311
312 already_outputting_pending_notes++;
313 execute_string ("%cnode %s-Footnotes,,,%s\n",
314 COMMAND_PREFIX, current_node, current_node);
315 already_outputting_pending_notes--;
316 current_node = old_current_node;
317 free (command);
318 command = old_command;
319 }
320 break;
321
322 case end_node:
323 close_paragraph ();
324 in_fixed_width_font++;
325 /* This string should be translated according to the
326 @documentlanguage, not the current LANG. We can't do that
327 yet, so leave it in English. */
328 execute_string ("---------- Footnotes ----------\n\n");
329 in_fixed_width_font--;
330 break;
331 }
332
333 /* Handle the footnotes in reverse order. */
334 {
335 int save_in_fixed_width_font = in_fixed_width_font;
336 FN **array = xmalloc ((footnote_count + 1) * sizeof (FN *));
337 array[footnote_count] = NULL;
338
339 while (--footnote_count > -1)
340 {
341 array[footnote_count] = footnote;
342 footnote = footnote->next;
343 }
344
345 filling_enabled = 1;
346 indented_fill = 1;
347 in_fixed_width_font = 0;
348
349 while ((footnote = array[++footnote_count]))
350 {
351 if (html)
352 {
353 /* Make the text of every footnote begin a separate paragraph. */
354 add_html_block_elt ("<p class=\"footnote\"><small>");
355 /* Make footnote number a link to its definition. */
356 add_word_args ("[<a name=\"fn-%d\" href=\"#fnd-%d\">%d</a>]",
357 footnote->number, footnote->number, footnote->number);
358 add_word ("</small> ");
359 already_outputting_pending_notes++;
360 execute_string ("%s", footnote->note);
361 already_outputting_pending_notes--;
362 add_word ("</p>\n");
363 }
364 else
365 {
366 char *old_current_node = current_node;
367 char *old_command = xstrdup (command);
368
369 already_outputting_pending_notes++;
370 execute_string ("%canchor{%s-Footnote-%d}(%s) %s",
371 COMMAND_PREFIX, current_node, footnote->number,
372 footnote->marker, footnote->note);
373 already_outputting_pending_notes--;
374 current_node = old_current_node;
375 free (command);
376 command = old_command;
377 }
378
379 close_paragraph ();
380 }
381
382 if (html)
383 add_word ("<hr></div>");
384 close_paragraph ();
385 free (array);
386
387 in_fixed_width_font = save_in_fixed_width_font;
388 }
389
390 free_pending_notes ();
391 }
392