1 /* $NetBSD: footnote.c,v 1.4 2025/12/31 22:18:50 oster 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: 47 48 separate_node: 49 Make them look like followed references, with the reference 50 destinations in a makeinfo manufactured node or, 51 end_node: 52 Make them appear at the bottom of the node that they originally 53 appeared in. */ 54 55 #define separate_node 0 56 #define end_node 1 57 58 int footnote_style = end_node; 59 int first_footnote_this_node = 1; 60 int footnote_count = 0; 61 62 /* Set the footnote style based on the style identifier in STRING. */ 64 int 65 set_footnote_style (char *string) 66 { 67 if (strcasecmp (string, "separate") == 0) 68 footnote_style = separate_node; 69 else if (strcasecmp (string, "end") == 0) 70 footnote_style = end_node; 71 else 72 return -1; 73 74 return 0; 75 } 76 77 void 78 cm_footnotestyle (int arg1, int arg2, int arg3) 79 { 80 char *arg; 81 82 get_rest_of_line (1, &arg); 83 84 /* If set on command line, do not change the footnote style. */ 85 if (!footnote_style_preset && set_footnote_style (arg) != 0) 86 line_error (_("Bad argument to %c%s"), COMMAND_PREFIX, command); 87 88 free (arg); 89 } 90 91 typedef struct fn 92 { 93 struct fn *next; 94 char *marker; 95 char *note; 96 int number; 97 } FN; 98 99 FN *pending_notes = NULL; 100 101 /* A method for remembering footnotes. Note that this list gets output 102 at the end of the current node. */ 103 static void 104 remember_note (char *marker, char *note) 105 { 106 FN *temp = xmalloc (sizeof (FN)); 107 108 temp->marker = xstrdup (marker); 109 temp->note = xstrdup (note); 110 temp->next = pending_notes; 111 temp->number = current_footnote_number; 112 pending_notes = temp; 113 footnote_count++; 114 } 115 116 /* How to get rid of existing footnotes. */ 117 static void 118 free_pending_notes (void) 119 { 120 FN *temp; 121 122 while ((temp = pending_notes)) 123 { 124 free (temp->marker); 125 free (temp->note); 126 pending_notes = pending_notes->next; 127 free (temp); 128 } 129 first_footnote_this_node = 1; 130 footnote_count = 0; 131 current_footnote_number = 1; /* for html */ 132 } 133 134 /* What to do when you see a @footnote construct. */ 135 136 /* Handle a "footnote". 137 footnote *{this is a footnote} 138 where "*" is the (optional) marker character for this note. */ 139 void 140 cm_footnote (int arg, int arg2, int arg3) 141 { 142 char *marker; 143 char *note; 144 145 get_until ("{", &marker); 146 canon_white (marker); 147 148 if (macro_expansion_output_stream && !executing_string) 149 append_to_expansion_output (input_text_offset + 1); /* include the { */ 150 151 /* Read the argument in braces. */ 152 if (curchar () != '{') 153 { 154 line_error (_("`%c%s' needs an argument `{...}', not just `%s'"), 155 COMMAND_PREFIX, command, marker); 156 free (marker); 157 return; 158 } 159 else 160 { 161 int len; 162 int braces = 1; 163 int loc = ++input_text_offset; 164 165 while (braces) 166 { 167 if (loc == input_text_length) 168 { 169 line_error (_("No closing brace for footnote `%s'"), marker); 170 return; 171 } 172 173 if (input_text[loc] == '{') 174 braces++; 175 else if (input_text[loc] == '}') 176 braces--; 177 else if (input_text[loc] == '\n') 178 line_number++; 179 180 loc++; 181 } 182 183 len = (loc - input_text_offset) - 1; 184 note = xmalloc (len + 1); 185 memcpy (note, &input_text[input_text_offset], len); 186 note[len] = 0; 187 input_text_offset = loc; 188 } 189 190 /* Must write the macro-expanded argument to the macro expansion 191 output stream. This is like the case in index_add_arg. */ 192 if (macro_expansion_output_stream && !executing_string) 193 { 194 /* Calling me_execute_string on a lone } provokes an error, since 195 as far as the reader knows there is no matching {. We wrote 196 the { above in the call to append_to_expansion_output. */ 197 me_execute_string_keep_state (note, "}"); 198 } 199 200 if (!current_node || !*current_node) 201 { 202 line_error (_("Footnote defined without parent node")); 203 free (marker); 204 free (note); 205 return; 206 } 207 208 /* output_pending_notes is non-reentrant (it uses a global data 209 structure pending_notes, which it frees before it returns), and 210 TeX doesn't grok footnotes inside footnotes anyway. Disallow 211 that. */ 212 if (already_outputting_pending_notes) 213 { 214 line_error (_("Footnotes inside footnotes are not allowed")); 215 free (marker); 216 free (note); 217 return; 218 } 219 220 if (!*marker) 221 { 222 free (marker); 223 224 if (number_footnotes) 225 { 226 marker = xmalloc (10); 227 sprintf (marker, "%d", current_footnote_number); 228 } 229 else 230 marker = xstrdup ("*"); 231 } 232 233 if (xml) 234 xml_insert_footnote (note); 235 else 236 { 237 remember_note (marker, note); 238 239 /* fixme: html: footnote processing needs work; we currently ignore 240 the style requested; we could clash with a node name of the form 241 `fn-<n>', though that's unlikely. */ 242 if (html) 243 { 244 /* Hyperlink also serves as an anchor (mnemonic: fnd is footnote 245 definition.) */ 246 add_html_elt ("<a rel=\"footnote\" href="); 247 add_word_args ("\"#fn-%d\" name=\"fnd-%d\"><sup>%s</sup></a>", 248 current_footnote_number, current_footnote_number, 249 marker); 250 } 251 else 252 /* Your method should at least insert MARKER. */ 253 switch (footnote_style) 254 { 255 case separate_node: 256 add_word_args ("(%s)", marker); 257 execute_string (" (*note %s-Footnote-%d::)", 258 current_node, current_footnote_number); 259 if (first_footnote_this_node) 260 { 261 char *temp_string, *expanded_ref; 262 263 temp_string = xmalloc (strlen (current_node) 264 + strlen ("-Footnotes") + 1); 265 266 strcpy (temp_string, current_node); 267 strcat (temp_string, "-Footnotes"); 268 expanded_ref = expansion (temp_string, 0); 269 remember_node_reference (expanded_ref, line_number, 270 followed_reference); 271 free (temp_string); 272 free (expanded_ref); 273 first_footnote_this_node = 0; 274 } 275 break; 276 277 case end_node: 278 add_word_args ("(%s)", marker); 279 break; 280 281 default: 282 break; 283 } 284 current_footnote_number++; 285 } 286 free (marker); 287 free (note); 288 } 289 290 /* Output the footnotes. We are at the end of the current node. */ 291 void 292 output_pending_notes (void) 293 { 294 FN *footnote = pending_notes; 295 296 if (!pending_notes) 297 return; 298 299 if (html) 300 { 301 add_html_block_elt ("<div class=\"footnote\">\n<hr>\n"); 302 /* We add an anchor here so @printindex can refer to this point 303 (as the node name) for entries defined in footnotes. */ 304 if (!splitting) 305 add_word ("<a name=\"texinfo-footnotes-in-document\"></a>"); 306 add_word_args ("<h4>%s</h4>", (char *) _("Footnotes")); 307 } 308 else 309 switch (footnote_style) 310 { 311 case separate_node: 312 { 313 char *old_current_node = current_node; 314 char *old_command = xstrdup (command); 315 316 already_outputting_pending_notes++; 317 execute_string ("%cnode %s-Footnotes,,,%s\n", 318 COMMAND_PREFIX, current_node, current_node); 319 already_outputting_pending_notes--; 320 current_node = old_current_node; 321 free (command); 322 command = old_command; 323 } 324 break; 325 326 case end_node: 327 close_paragraph (); 328 in_fixed_width_font++; 329 /* This string should be translated according to the 330 @documentlanguage, not the current LANG. We can't do that 331 yet, so leave it in English. */ 332 execute_string ("---------- Footnotes ----------\n\n"); 333 in_fixed_width_font--; 334 break; 335 } 336 337 /* Handle the footnotes in reverse order. */ 338 { 339 int save_in_fixed_width_font = in_fixed_width_font; 340 FN **array = xmalloc ((footnote_count + 1) * sizeof (FN *)); 341 array[footnote_count] = NULL; 342 343 while (--footnote_count > -1) 344 { 345 array[footnote_count] = footnote; 346 footnote = footnote->next; 347 } 348 349 filling_enabled = 1; 350 indented_fill = 1; 351 in_fixed_width_font = 0; 352 353 while ((footnote = array[++footnote_count])) 354 { 355 if (html) 356 { 357 /* Make the text of every footnote begin a separate paragraph. */ 358 add_html_block_elt ("<p class=\"footnote\"><small>"); 359 /* Make footnote number a link to its definition. */ 360 add_word_args ("[<a name=\"fn-%d\" href=\"#fnd-%d\">%d</a>]", 361 footnote->number, footnote->number, footnote->number); 362 add_word ("</small> "); 363 already_outputting_pending_notes++; 364 execute_string ("%s", footnote->note); 365 already_outputting_pending_notes--; 366 add_word ("</p>\n"); 367 } 368 else 369 { 370 char *old_current_node = current_node; 371 char *old_command = xstrdup (command); 372 373 already_outputting_pending_notes++; 374 execute_string ("%canchor{%s-Footnote-%d}(%s) %s", 375 COMMAND_PREFIX, current_node, footnote->number, 376 footnote->marker, footnote->note); 377 already_outputting_pending_notes--; 378 current_node = old_current_node; 379 free (command); 380 command = old_command; 381 } 382 383 close_paragraph (); 384 } 385 386 if (html) 387 add_word ("<hr></div>"); 388 close_paragraph (); 389 free (array); 390 391 in_fixed_width_font = save_in_fixed_width_font; 392 } 393 394 free_pending_notes (); 395 } 396