Home | History | Annotate | Line # | Download | only in makeinfo
      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