Merge branch 'vendor/LESS'
[dragonfly.git] / contrib / texinfo-4 / makeinfo / footnote.c
1 /* footnote.c -- footnotes for Texinfo.
2    $Id: footnote.c,v 1.7 2004/04/11 17:56:47 karl 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 \f
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 \f
59 /* Set the footnote style based on the style identifier in STRING. */
60 int
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
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
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
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
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
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 }