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 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 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 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 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 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 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