1 /* footnote.c -- footnotes for Texinfo. 2 $Id: footnote.c,v 1.1.1.3 2006/07/17 16:03:46 espie 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 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 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 } 392