1 /* footnote.c -- footnotes for Texinfo. 2 $Id: footnote.c,v 1.1.1.1 2000/02/09 01:25:11 espie Exp $ 3 4 Copyright (C) 1998, 99 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 25 /* Nonzero means that the footnote style for this document was set on 26 the command line, which overrides any other settings. */ 27 int footnote_style_preset = 0; 28 29 /* The current footnote number in this node. Each time a new node is 30 started this is reset to 1. */ 31 int current_footnote_number = 1; 32 33 /* Nonzero means we automatically number footnotes with no specified marker. */ 34 int number_footnotes = 1; 35 36 /* Nonzero means we are currently outputting footnotes. */ 37 int already_outputting_pending_notes = 0; 38 39 40 /* Footnotes can be handled in one of two ways: 41 42 separate_node: 43 Make them look like followed references, with the reference 44 destinations in a makeinfo manufactured node or, 45 end_node: 46 Make them appear at the bottom of the node that they originally 47 appeared in. */ 48 49 #define separate_node 0 50 #define end_node 1 51 52 int footnote_style = end_node; 53 int first_footnote_this_node = 1; 54 int footnote_count = 0; 55 56 /* Set the footnote style based on the style identifier in STRING. */ 57 int 58 set_footnote_style (string) 59 char *string; 60 { 61 if (strcasecmp (string, "separate") == 0) 62 footnote_style = separate_node; 63 else if (strcasecmp (string, "end") == 0) 64 footnote_style = end_node; 65 else 66 return -1; 67 68 return 0; 69 } 70 71 void 72 cm_footnotestyle () 73 { 74 char *arg; 75 76 get_rest_of_line (1, &arg); 77 78 /* If set on command line, do not change the footnote style. */ 79 if (!footnote_style_preset && set_footnote_style (arg) != 0) 80 line_error (_("Bad argument to %c%s"), COMMAND_PREFIX, command); 81 82 free (arg); 83 } 84 85 typedef struct fn 86 { 87 struct fn *next; 88 char *marker; 89 char *note; 90 int number; 91 } FN; 92 93 FN *pending_notes = NULL; 94 95 /* A method for remembering footnotes. Note that this list gets output 96 at the end of the current node. */ 97 void 98 remember_note (marker, note) 99 char *marker, *note; 100 { 101 FN *temp = xmalloc (sizeof (FN)); 102 103 temp->marker = xstrdup (marker); 104 temp->note = xstrdup (note); 105 temp->next = pending_notes; 106 temp->number = current_footnote_number; 107 pending_notes = temp; 108 footnote_count++; 109 } 110 111 /* How to get rid of existing footnotes. */ 112 static void 113 free_pending_notes () 114 { 115 FN *temp; 116 117 while ((temp = pending_notes)) 118 { 119 free (temp->marker); 120 free (temp->note); 121 pending_notes = pending_notes->next; 122 free (temp); 123 } 124 first_footnote_this_node = 1; 125 footnote_count = 0; 126 current_footnote_number = 1; /* for html */ 127 } 128 129 /* What to do when you see a @footnote construct. */ 130 131 /* Handle a "footnote". 132 footnote *{this is a footnote} 133 where "*" is the (optional) marker character for this note. */ 134 void 135 cm_footnote () 136 { 137 char *marker; 138 char *note; 139 140 get_until ("{", &marker); 141 canon_white (marker); 142 143 if (macro_expansion_output_stream && !executing_string) 144 append_to_expansion_output (input_text_offset + 1); /* include the { */ 145 146 /* Read the argument in braces. */ 147 if (curchar () != '{') 148 { 149 line_error (_("`%c%s' needs an argument `{...}', not just `%s'"), 150 COMMAND_PREFIX, command, marker); 151 free (marker); 152 return; 153 } 154 else 155 { 156 int len; 157 int braces = 1; 158 int loc = ++input_text_offset; 159 160 while (braces) 161 { 162 if (loc == input_text_length) 163 { 164 line_error (_("No closing brace for footnote `%s'"), marker); 165 return; 166 } 167 168 if (input_text[loc] == '{') 169 braces++; 170 else if (input_text[loc] == '}') 171 braces--; 172 else if (input_text[loc] == '\n') 173 line_number++; 174 175 loc++; 176 } 177 178 len = (loc - input_text_offset) - 1; 179 note = xmalloc (len + 1); 180 memcpy (note, &input_text[input_text_offset], len); 181 note[len] = 0; 182 input_text_offset = loc; 183 } 184 185 /* Must write the macro-expanded argument to the macro expansion 186 output stream. This is like the case in index_add_arg. */ 187 if (macro_expansion_output_stream && !executing_string) 188 { 189 /* Calling me_execute_string on a lone } provokes an error, since 190 as far as the reader knows there is no matching {. We wrote 191 the { above in the call to append_to_expansion_output. */ 192 me_execute_string_keep_state (note, "}"); 193 } 194 195 if (!current_node || !*current_node) 196 { 197 line_error (_("Footnote defined without parent node")); 198 free (marker); 199 free (note); 200 return; 201 } 202 203 if (!*marker) 204 { 205 free (marker); 206 207 if (number_footnotes) 208 { 209 marker = xmalloc (10); 210 sprintf (marker, "%d", current_footnote_number); 211 } 212 else 213 marker = xstrdup ("*"); 214 } 215 216 remember_note (marker, note); 217 218 /* fixme: html: footnote processing needs work; we currently ignore 219 the style requested; we could clash with a node name of the form 220 `fn-<n>', though that's unlikely. */ 221 if (html) 222 add_word_args ("<a rel=footnote href=\"#fn-%d\"><sup>%s</sup></a>", 223 current_footnote_number, marker); 224 else 225 /* Your method should at least insert MARKER. */ 226 switch (footnote_style) 227 { 228 case separate_node: 229 add_word_args ("(%s)", marker); 230 execute_string (" (*note %s-Footnote-%d::)", 231 current_node, current_footnote_number); 232 if (first_footnote_this_node) 233 { 234 char *temp_string, *expanded_ref; 235 236 temp_string = xmalloc (strlen (current_node) 237 + strlen ("-Footnotes") + 1); 238 239 strcpy (temp_string, current_node); 240 strcat (temp_string, "-Footnotes"); 241 expanded_ref = expansion (temp_string, 0); 242 remember_node_reference (expanded_ref, line_number, 243 followed_reference); 244 free (temp_string); 245 free (expanded_ref); 246 first_footnote_this_node = 0; 247 } 248 break; 249 250 case end_node: 251 add_word_args ("(%s)", marker); 252 break; 253 254 default: 255 break; 256 } 257 current_footnote_number++; 258 259 free (marker); 260 free (note); 261 } 262 263 /* Output the footnotes. We are at the end of the current node. */ 264 void 265 output_pending_notes () 266 { 267 FN *footnote = pending_notes; 268 269 if (!pending_notes) 270 return; 271 272 if (html) 273 { /* The type= attribute is used just in case some weirdo browser 274 out there doesn't use numbers by default. Since we rely on the 275 browser to produce the footnote numbers, we need to make sure 276 they ARE indeed numbers. Pre-HTML4 browsers seem to not care. */ 277 add_word ("<hr><h4>"); 278 add_word (_("Footnotes")); 279 add_word ("</h4>\n<ol type=\"1\">\n"); 280 } 281 else 282 switch (footnote_style) 283 { 284 case separate_node: 285 { 286 char *old_current_node = current_node; 287 char *old_command = xstrdup (command); 288 289 already_outputting_pending_notes++; 290 execute_string ("%cnode %s-Footnotes,,,%s\n", 291 COMMAND_PREFIX, current_node, current_node); 292 already_outputting_pending_notes--; 293 current_node = old_current_node; 294 free (command); 295 command = old_command; 296 } 297 break; 298 299 case end_node: 300 close_paragraph (); 301 in_fixed_width_font++; 302 /* This string should be translated according to the 303 @documentlanguage, not the current LANG. We can't do that 304 yet, so leave it in English. */ 305 execute_string ("---------- Footnotes ----------\n\n"); 306 in_fixed_width_font--; 307 break; 308 } 309 310 /* Handle the footnotes in reverse order. */ 311 { 312 FN **array = xmalloc ((footnote_count + 1) * sizeof (FN *)); 313 array[footnote_count] = NULL; 314 315 while (--footnote_count > -1) 316 { 317 array[footnote_count] = footnote; 318 footnote = footnote->next; 319 } 320 321 filling_enabled = 1; 322 indented_fill = 1; 323 324 while ((footnote = array[++footnote_count])) 325 { 326 if (html) 327 { 328 /* Make the text of every footnote begin a separate paragraph. */ 329 add_word_args ("<li><a name=\"fn-%d\"></a>\n<p>", 330 footnote->number); 331 execute_string ("%s", footnote->note); 332 add_word ("</p>\n"); 333 } 334 else 335 { 336 char *old_current_node = current_node; 337 char *old_command = xstrdup (command); 338 339 already_outputting_pending_notes++; 340 execute_string ("%canchor{%s-Footnote-%d}(%s) %s", 341 COMMAND_PREFIX, current_node, footnote->number, 342 footnote->marker, footnote->note); 343 already_outputting_pending_notes--; 344 current_node = old_current_node; 345 free (command); 346 command = old_command; 347 } 348 349 close_paragraph (); 350 } 351 352 if (html) 353 add_word ("</ol><hr>"); 354 close_paragraph (); 355 free (array); 356 } 357 358 free_pending_notes (); 359 } 360