xref: /netbsd-src/external/gpl2/texinfo/dist/makeinfo/footnote.c (revision 29619d2afe564e54d657b83e5a3ae89584f83720)
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