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