footnote.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. /* footnote.c -- footnotes for Texinfo.
  2. $Id: footnote.c,v 1.12 2007-12-03 01:38:43 karl Exp $
  3. Copyright (C) 1998, 1999, 2002, 2007 Free Software Foundation, Inc.
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  14. #include "system.h"
  15. #include "footnote.h"
  16. #include "macro.h"
  17. #include "makeinfo.h"
  18. #include "node.h"
  19. #include "xml.h"
  20. #include "xref.h"
  21. /* Nonzero means that the footnote style for this document was set on
  22. the command line, which overrides any other settings. */
  23. int footnote_style_preset = 0;
  24. /* The current footnote number in this node. Each time a new node is
  25. started this is reset to 1. */
  26. int current_footnote_number = 1;
  27. /* Nonzero means we automatically number footnotes with no specified marker. */
  28. int number_footnotes = 1;
  29. /* Nonzero means we are currently outputting footnotes. */
  30. int already_outputting_pending_notes = 0;
  31. /* Footnotes can be handled in one of two ways:
  32. separate_node:
  33. Make them look like followed references, with the reference
  34. destinations in a makeinfo manufactured node or,
  35. end_node:
  36. Make them appear at the bottom of the node that they originally
  37. appeared in. */
  38. #define separate_node 0
  39. #define end_node 1
  40. int footnote_style = end_node;
  41. int first_footnote_this_node = 1;
  42. int footnote_count = 0;
  43. /* Set the footnote style based on the style identifier in STRING. */
  44. int
  45. set_footnote_style (char *string)
  46. {
  47. if (mbscasecmp (string, "separate") == 0)
  48. footnote_style = separate_node;
  49. else if (mbscasecmp (string, "end") == 0)
  50. footnote_style = end_node;
  51. else
  52. return -1;
  53. return 0;
  54. }
  55. void
  56. cm_footnotestyle (void)
  57. {
  58. char *arg;
  59. get_rest_of_line (1, &arg);
  60. /* If set on command line, do not change the footnote style. */
  61. if (!footnote_style_preset && set_footnote_style (arg) != 0)
  62. line_error (_("Bad argument to %c%s"), COMMAND_PREFIX, command);
  63. free (arg);
  64. }
  65. typedef struct fn
  66. {
  67. struct fn *next;
  68. char *marker;
  69. char *note;
  70. int number;
  71. } FN;
  72. FN *pending_notes = NULL;
  73. /* A method for remembering footnotes. Note that this list gets output
  74. at the end of the current node. */
  75. static void
  76. remember_note (char *marker, char *note)
  77. {
  78. FN *temp = xmalloc (sizeof (FN));
  79. temp->marker = xstrdup (marker);
  80. temp->note = xstrdup (note);
  81. temp->next = pending_notes;
  82. temp->number = current_footnote_number;
  83. pending_notes = temp;
  84. footnote_count++;
  85. }
  86. /* How to get rid of existing footnotes. */
  87. static void
  88. free_pending_notes (void)
  89. {
  90. FN *temp;
  91. while ((temp = pending_notes))
  92. {
  93. free (temp->marker);
  94. free (temp->note);
  95. pending_notes = pending_notes->next;
  96. free (temp);
  97. }
  98. first_footnote_this_node = 1;
  99. footnote_count = 0;
  100. current_footnote_number = 1; /* for html */
  101. }
  102. /* What to do when you see a @footnote construct. */
  103. /* Handle a "footnote".
  104. footnote *{this is a footnote}
  105. where "*" is the (optional) marker character for this note. */
  106. void
  107. cm_footnote (void)
  108. {
  109. char *marker;
  110. char *note;
  111. get_until ("{", &marker);
  112. canon_white (marker);
  113. if (macro_expansion_output_stream && !executing_string)
  114. append_to_expansion_output (input_text_offset + 1); /* include the { */
  115. /* Read the argument in braces. */
  116. if (curchar () != '{')
  117. {
  118. line_error (_("`%c%s' needs an argument `{...}', not just `%s'"),
  119. COMMAND_PREFIX, command, marker);
  120. free (marker);
  121. return;
  122. }
  123. else
  124. {
  125. int len;
  126. int braces = 1;
  127. int loc = ++input_text_offset;
  128. while (braces)
  129. {
  130. if (loc == input_text_length)
  131. {
  132. line_error (_("No closing brace for footnote `%s'"), marker);
  133. return;
  134. }
  135. if (input_text[loc] == '{')
  136. braces++;
  137. else if (input_text[loc] == '}')
  138. braces--;
  139. else if (input_text[loc] == '\n')
  140. line_number++;
  141. loc++;
  142. }
  143. len = (loc - input_text_offset) - 1;
  144. note = xmalloc (len + 1);
  145. memcpy (note, &input_text[input_text_offset], len);
  146. note[len] = 0;
  147. input_text_offset = loc;
  148. }
  149. /* Must write the macro-expanded argument to the macro expansion
  150. output stream. This is like the case in index_add_arg. */
  151. if (macro_expansion_output_stream && !executing_string)
  152. {
  153. /* Calling me_execute_string on a lone } provokes an error, since
  154. as far as the reader knows there is no matching {. We wrote
  155. the { above in the call to append_to_expansion_output. */
  156. me_execute_string_keep_state (note, "}");
  157. }
  158. if (!current_node || !*current_node)
  159. {
  160. line_error (_("Footnote defined without parent node"));
  161. free (marker);
  162. free (note);
  163. return;
  164. }
  165. /* output_pending_notes is non-reentrant (it uses a global data
  166. structure pending_notes, which it frees before it returns), and
  167. TeX doesn't grok footnotes inside footnotes anyway. Disallow
  168. that. */
  169. if (already_outputting_pending_notes)
  170. {
  171. line_error (_("Footnotes inside footnotes are not allowed"));
  172. free (marker);
  173. free (note);
  174. return;
  175. }
  176. if (!*marker)
  177. {
  178. free (marker);
  179. if (number_footnotes)
  180. {
  181. marker = xmalloc (10);
  182. sprintf (marker, "%d", current_footnote_number);
  183. }
  184. else
  185. marker = xstrdup ("*");
  186. }
  187. if (xml)
  188. xml_insert_footnote (note);
  189. else
  190. {
  191. remember_note (marker, note);
  192. /* fixme: html: footnote processing needs work; we currently ignore
  193. the style requested; we could clash with a node name of the form
  194. `fn-<n>', though that's unlikely. */
  195. if (html)
  196. {
  197. /* Hyperlink also serves as an anchor (mnemonic: fnd is footnote
  198. definition.) */
  199. add_html_elt ("<a rel=\"footnote\" href=");
  200. add_word_args ("\"#fn-%d\" name=\"fnd-%d\"><sup>%s</sup></a>",
  201. current_footnote_number, current_footnote_number,
  202. marker);
  203. }
  204. else
  205. /* Your method should at least insert MARKER. */
  206. switch (footnote_style)
  207. {
  208. case separate_node:
  209. add_word_args ("(%s)", marker);
  210. execute_string (" (*note %s-Footnote-%d::)",
  211. current_node, current_footnote_number);
  212. if (first_footnote_this_node)
  213. {
  214. char *temp_string, *expanded_ref;
  215. temp_string = xmalloc (strlen (current_node)
  216. + strlen ("-Footnotes") + 1);
  217. strcpy (temp_string, current_node);
  218. strcat (temp_string, "-Footnotes");
  219. expanded_ref = expansion (temp_string, 0);
  220. remember_node_reference (expanded_ref, line_number,
  221. followed_reference);
  222. free (temp_string);
  223. free (expanded_ref);
  224. first_footnote_this_node = 0;
  225. }
  226. break;
  227. case end_node:
  228. add_word_args ("(%s)", marker);
  229. break;
  230. default:
  231. break;
  232. }
  233. current_footnote_number++;
  234. }
  235. free (marker);
  236. free (note);
  237. }
  238. /* Output the footnotes. We are at the end of the current node. */
  239. void
  240. output_pending_notes (void)
  241. {
  242. FN *footnote = pending_notes;
  243. if (!pending_notes)
  244. return;
  245. if (html)
  246. {
  247. add_html_block_elt ("<div class=\"footnote\">\n<hr>\n");
  248. /* We add an anchor here so @printindex can refer to this point
  249. (as the node name) for entries defined in footnotes. */
  250. if (!splitting)
  251. add_word ("<a name=\"texinfo-footnotes-in-document\"></a>");
  252. add_word_args ("<h4>%s</h4>", (char *) _("Footnotes"));
  253. }
  254. else
  255. switch (footnote_style)
  256. {
  257. case separate_node:
  258. {
  259. char *old_current_node = current_node;
  260. char *old_command = xstrdup (command);
  261. already_outputting_pending_notes++;
  262. execute_string ("%cnode %s-Footnotes,,,%s\n",
  263. COMMAND_PREFIX, current_node, current_node);
  264. already_outputting_pending_notes--;
  265. current_node = old_current_node;
  266. free (command);
  267. command = old_command;
  268. }
  269. break;
  270. case end_node:
  271. close_paragraph ();
  272. in_fixed_width_font++;
  273. /* This string should be translated according to the
  274. @documentlanguage, not the current LANG. We can't do that
  275. yet, so leave it in English. */
  276. execute_string ("---------- Footnotes ----------\n\n");
  277. in_fixed_width_font--;
  278. break;
  279. }
  280. /* Handle the footnotes in reverse order. */
  281. {
  282. int save_in_fixed_width_font = in_fixed_width_font;
  283. FN **array = xmalloc ((footnote_count + 1) * sizeof (FN *));
  284. array[footnote_count] = NULL;
  285. while (--footnote_count > -1)
  286. {
  287. array[footnote_count] = footnote;
  288. footnote = footnote->next;
  289. }
  290. filling_enabled = 1;
  291. indented_fill = 1;
  292. in_fixed_width_font = 0;
  293. while ((footnote = array[++footnote_count]))
  294. {
  295. if (html)
  296. {
  297. /* Make the text of every footnote begin a separate paragraph. */
  298. add_html_block_elt ("<p class=\"footnote\"><small>");
  299. /* Make footnote number a link to its definition. */
  300. add_word_args ("[<a name=\"fn-%d\" href=\"#fnd-%d\">%d</a>]",
  301. footnote->number, footnote->number, footnote->number);
  302. add_word ("</small> ");
  303. already_outputting_pending_notes++;
  304. execute_string ("%s", footnote->note);
  305. already_outputting_pending_notes--;
  306. add_word ("</p>\n");
  307. }
  308. else
  309. {
  310. char *old_current_node = current_node;
  311. char *old_command = xstrdup (command);
  312. already_outputting_pending_notes++;
  313. execute_string ("%canchor{%s-Footnote-%d}(%s) %s",
  314. COMMAND_PREFIX, current_node, footnote->number,
  315. footnote->marker, footnote->note);
  316. already_outputting_pending_notes--;
  317. current_node = old_current_node;
  318. free (command);
  319. command = old_command;
  320. }
  321. close_paragraph ();
  322. }
  323. if (html)
  324. add_html_block_elt ("<hr></div>");
  325. close_paragraph ();
  326. free (array);
  327. in_fixed_width_font = save_in_fixed_width_font;
  328. }
  329. free_pending_notes ();
  330. }