Add missing "return(error)".
[dragonfly.git] / contrib / texinfo / makeinfo / footnote.c
1 /* footnote.c -- footnotes for Texinfo.
2    $Id: footnote.c,v 1.13 2002/03/02 15:05:21 karl Exp $
3
4    Copyright (C) 1998, 99, 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 "xml.h"
25
26 /* Nonzero means that the footnote style for this document was set on
27    the command line, which overrides any other settings. */
28 int footnote_style_preset = 0;
29
30 /* The current footnote number in this node.  Each time a new node is
31    started this is reset to 1. */
32 int current_footnote_number = 1;
33
34 /* Nonzero means we automatically number footnotes with no specified marker. */
35 int number_footnotes = 1;
36
37 /* Nonzero means we are currently outputting footnotes. */
38 int already_outputting_pending_notes = 0;
39
40 \f
41 /* Footnotes can be handled in one of two ways:
42
43    separate_node:
44         Make them look like followed references, with the reference
45         destinations in a makeinfo manufactured node or,
46    end_node:
47         Make them appear at the bottom of the node that they originally
48         appeared in. */
49
50 #define separate_node 0
51 #define end_node 1
52
53 int footnote_style = end_node;
54 int first_footnote_this_node = 1;
55 int footnote_count = 0;
56
57 /* Set the footnote style based on the style identifier in STRING. */
58 int
59 set_footnote_style (string)
60      char *string;
61 {
62   if (strcasecmp (string, "separate") == 0)
63     footnote_style = separate_node;
64   else if (strcasecmp (string, "end") == 0)
65     footnote_style = end_node;
66   else
67     return -1;
68
69  return 0;
70 }
71
72 void
73 cm_footnotestyle ()
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 void
99 remember_note (marker, note)
100      char *marker, *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 ()
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 ()
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       add_html_elt ("<a rel=footnote href=");
241       add_word_args ("\"#fn-%d\"><sup>%s</sup></a>",
242                      current_footnote_number, marker);
243     }
244   else
245     /* Your method should at least insert MARKER. */
246     switch (footnote_style)
247       {
248       case separate_node:
249         add_word_args ("(%s)", marker);
250         execute_string (" (*note %s-Footnote-%d::)",
251                         current_node, current_footnote_number);
252         if (first_footnote_this_node)
253           {
254             char *temp_string, *expanded_ref;
255
256             temp_string = xmalloc (strlen (current_node)
257                                    + strlen ("-Footnotes") + 1);
258
259             strcpy (temp_string, current_node);
260             strcat (temp_string, "-Footnotes");
261             expanded_ref = expansion (temp_string, 0);
262             remember_node_reference (expanded_ref, line_number,
263                                      followed_reference);
264             free (temp_string);
265             free (expanded_ref);
266             first_footnote_this_node = 0;
267           }
268         break;
269
270       case end_node:
271         add_word_args ("(%s)", marker);
272         break;
273
274       default:
275         break;
276       }
277   current_footnote_number++;
278     }
279   free (marker);
280   free (note);
281 }
282
283 /* Output the footnotes.  We are at the end of the current node. */
284 void
285 output_pending_notes ()
286 {
287   FN *footnote = pending_notes;
288
289   if (!pending_notes)
290     return;
291
292   if (html)
293     { /* The type= attribute is used just in case some weirdo browser
294          out there doesn't use numbers by default.  Since we rely on the
295          browser to produce the footnote numbers, we need to make sure
296          they ARE indeed numbers.  Pre-HTML4 browsers seem to not care.  */
297       add_word ("<hr><h4>");
298       add_word (_("Footnotes"));
299       add_word ("</h4>\n<ol type=\"1\">\n");
300     }
301   else
302     switch (footnote_style)
303       {
304       case separate_node:
305         {
306           char *old_current_node = current_node;
307           char *old_command = xstrdup (command);
308
309           already_outputting_pending_notes++;
310           execute_string ("%cnode %s-Footnotes,,,%s\n",
311                           COMMAND_PREFIX, current_node, current_node);
312           already_outputting_pending_notes--;
313           current_node = old_current_node;
314           free (command);
315           command = old_command;
316         }
317       break;
318
319       case end_node:
320         close_paragraph ();
321         in_fixed_width_font++;
322         /* This string should be translated according to the
323            @documentlanguage, not the current LANG.  We can't do that
324            yet, so leave it in English.  */
325         execute_string ("---------- Footnotes ----------\n\n");
326         in_fixed_width_font--;
327         break;
328       }
329
330   /* Handle the footnotes in reverse order. */
331   {
332     FN **array = xmalloc ((footnote_count + 1) * sizeof (FN *));
333     array[footnote_count] = NULL;
334
335     while (--footnote_count > -1)
336       {
337         array[footnote_count] = footnote;
338         footnote = footnote->next;
339       }
340
341     filling_enabled = 1;
342     indented_fill = 1;
343
344     while ((footnote = array[++footnote_count]))
345       {
346         if (html)
347           {
348             /* Make the text of every footnote begin a separate paragraph.  */
349             add_word_args ("<li><a name=\"fn-%d\"></a>\n<p>",
350                            footnote->number);
351             already_outputting_pending_notes++;
352             execute_string ("%s", footnote->note);
353             already_outputting_pending_notes--;
354             add_word ("</p>\n");
355           }
356         else
357           {
358             char *old_current_node = current_node;
359             char *old_command = xstrdup (command);
360
361             already_outputting_pending_notes++;
362             execute_string ("%canchor{%s-Footnote-%d}(%s) %s",
363                             COMMAND_PREFIX, current_node, footnote->number,
364                             footnote->marker, footnote->note);
365             already_outputting_pending_notes--;
366             current_node = old_current_node;
367             free (command);
368             command = old_command;
369           }
370
371         close_paragraph ();
372       }
373
374     if (html)
375       add_word ("</ol><hr>");
376     close_paragraph ();
377     free (array);
378   }
379   
380   free_pending_notes ();
381 }