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