(root)/
texinfo-7.1/
info/
footnotes.c
       1  /* footnotes.c -- Some functions for manipulating footnotes.
       2  
       3     Copyright 1993-2023 Free Software Foundation, Inc.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation, either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
      17  
      18     Originally written by Brian Fox. */
      19  
      20  #include "info.h"
      21  #include "session.h"
      22  #include "scan.h"
      23  #include "util.h"
      24  #include "footnotes.h"
      25  
      26  /* Nonzero means attempt to show footnotes when displaying a new window. */
      27  int auto_footnotes_p = 0;
      28  
      29  static char *footnote_nodename = "*Footnotes*";
      30  
      31  /* Find the window currently showing footnotes. */
      32  static WINDOW *
      33  find_footnotes_window (void)
      34  {
      35    WINDOW *win;
      36  
      37    /* Try to find an existing window first. */
      38    for (win = windows; win; win = win->next)
      39      if (internal_info_node_p (win->node) &&
      40          (strcmp (win->node->nodename, footnote_nodename) == 0))
      41        break;
      42  
      43    return win;
      44  }
      45  
      46  /* Manufacture a node containing the footnotes of this node, and
      47     return the manufactured node.  If NODE has no footnotes, return a 
      48     NULL pointer. */
      49  NODE *
      50  make_footnotes_node (NODE *node)
      51  {
      52    NODE *fn_node, *footnotes_node = NULL, *result = NULL;
      53    long fn_start = -1;
      54    char *fnptr;
      55  
      56    /* Make the initial assumption that the footnotes appear as simple
      57       text within this windows node. */
      58    fn_node = node;
      59  
      60    /* See if this node contains the magic footnote label. */
      61      {
      62        char saved = node->contents[node->nodelen];
      63        node->contents[node->nodelen] = '\0';
      64        fnptr = strstr (node->contents, FOOTNOTE_LABEL);
      65        node->contents[node->nodelen] = saved;
      66      }
      67    if (fnptr)
      68      fn_start = fnptr - node->contents;
      69  
      70    /* If it doesn't, check to see if it has an associated footnotes node. */
      71    if (!fnptr)
      72      {
      73        REFERENCE **refs;
      74  
      75        refs = node->references;
      76  
      77        if (refs)
      78          {
      79            register int i;
      80            char *refname;
      81            int reflen = strlen ("-Footnotes") + strlen (node->nodename);
      82  
      83            refname = xmalloc (reflen + 1);
      84  
      85            strcpy (refname, node->nodename);
      86            strcat (refname, "-Footnotes");
      87  
      88            for (i = 0; refs[i]; i++)
      89              if (refs[i]->type == REFERENCE_XREF
      90                  && (refs[i]->nodename != NULL)
      91                  /* Support both the older "foo-Footnotes" and the new
      92                     style "foo-Footnote-NN" references.  */
      93                  && (strcmp (refs[i]->nodename, refname) == 0 ||
      94                   (strncmp (refs[i]->nodename, refname, reflen - 1) == 0 &&
      95                    refs[i]->nodename[reflen - 1] == '-' &&
      96                    isdigit (refs[i]->nodename[reflen]))))
      97                {
      98                  footnotes_node = info_get_node (node->fullpath, refname);
      99                  if (footnotes_node)
     100                    {
     101                      fn_node = footnotes_node;
     102                      fn_start = 0;
     103                    }
     104                  break;
     105                }
     106  
     107            free (refname);
     108          }
     109      }
     110  
     111    /* If we never found the start of a footnotes area, quit now. */
     112    if (fn_start == -1)
     113      return NULL;
     114  
     115    /* Make the new node. */
     116    result = info_create_node ();
     117  
     118    /* Get the size of the footnotes appearing within this node. */
     119    {
     120      char *header;
     121      long text_start = fn_start;
     122  
     123      xasprintf (&header,
     124                "*** Footnotes appearing in the node '%s' ***\n",
     125                node->nodename);
     126  
     127      /* Move the start of the displayed text to right after the first line.
     128         This effectively skips either "---- footno...", or "File: foo...". */
     129      while (text_start < fn_node->nodelen)
     130        if (fn_node->contents[text_start++] == '\n')
     131          break;
     132    
     133      result->nodelen = strlen (header) + fn_node->nodelen - text_start;
     134  
     135      /* Set the contents of this node. */
     136      result->contents = xmalloc (1 + result->nodelen);
     137      sprintf (result->contents, "%s", header);
     138      memcpy (result->contents + strlen (header),
     139              fn_node->contents + text_start, fn_node->nodelen - text_start);
     140      result->contents[strlen (header) + fn_node->nodelen - text_start] = '\0';
     141  
     142     /* Copy and adjust references that appear in footnotes section. */
     143      {
     144        REFERENCE **ref = fn_node->references;
     145  
     146        for (; *ref; ref++)
     147          {
     148            if ((*ref)->start > text_start)
     149              break;
     150          }
     151  
     152        result->references = info_copy_references (ref);
     153  
     154        for (ref = result->references; *ref; ref++)
     155          {
     156            (*ref)->start -= text_start - strlen (header);
     157            (*ref)->end -= text_start - strlen (header);
     158          }
     159      }
     160  
     161      result->nodename = xstrdup (footnote_nodename);
     162      result->flags |= N_IsInternal | N_WasRewritten;
     163  
     164      /* Needed in case the user follows a reference in the footnotes window. */
     165      result->fullpath = fn_node->fullpath;
     166      result->subfile = fn_node->subfile;
     167  
     168      free (header);
     169    }
     170  
     171    free_history_node (footnotes_node);
     172    return result;
     173  }
     174  
     175  /* Create or delete the footnotes window depending on whether footnotes
     176     exist in WINDOW's node or not.  Returns FN_FOUND if footnotes were found
     177     and displayed.  Returns FN_UNFOUND if there were no footnotes found
     178     in WINDOW's node.  Returns FN_UNABLE if there were footnotes, but the
     179     window to show them couldn't be made. */
     180  int
     181  info_get_or_remove_footnotes (WINDOW *window)
     182  {
     183    WINDOW *fn_win;
     184    NODE *new_footnotes = 0;
     185  
     186    fn_win = find_footnotes_window ();
     187  
     188    /* If we are in the footnotes window, change nothing. */
     189    if (fn_win == window)
     190      return FN_FOUND;
     191  
     192    /* Don't display footnotes for the "*" node (entire contents of file) or
     193       for nodes without a name like completion windows. */
     194    if (window->node->nodename && strcmp ("*", window->node->nodename))
     195      /* Try to find footnotes for this window's node. */
     196      new_footnotes = make_footnotes_node (window->node);
     197  
     198    if (!new_footnotes)
     199      {
     200        /* If there was a window showing footnotes, and there are no footnotes
     201           for the current window, delete the old footnote window. */
     202        if (fn_win && windows->next)
     203          info_delete_window_internal (fn_win);
     204        return FN_UNFOUND;
     205      }
     206  
     207    /* If there is no window around showing footnotes, try
     208       to make a new window. */
     209    if (!fn_win)
     210      {
     211        WINDOW *old_active;
     212        WINDOW *last, *win;
     213  
     214        /* Always make this window be the last one appearing in the list.  Find
     215           the last window in the chain. */
     216        for (win = windows, last = windows; win; last = win, win = win->next);
     217  
     218        /* Try to split this window, and make the split window the one to
     219           contain the footnotes. */
     220        old_active = active_window;
     221        active_window = last;
     222        fn_win = window_make_window ();
     223        active_window = old_active;
     224  
     225        /* If we are hacking automatic footnotes, and there are footnotes
     226           but we couldn't display them, print a message to that effect. */
     227        if (!fn_win)
     228          {
     229            if (auto_footnotes_p)
     230              info_error (_("Footnotes could not be displayed"));
     231            return FN_UNABLE;
     232          }
     233      }
     234  
     235    /* Note that info_set_node_of_window calls this function
     236       (info_get_or_remove_footnotes), but we do not recurse indefinitely
     237       because we check if we are in the footnote window above. */
     238    info_set_node_of_window (fn_win, new_footnotes);
     239    fn_win->flags |= W_TempWindow;
     240  
     241    /* Make the height be the number of lines appearing in the footnotes. */
     242    if (new_footnotes)
     243      window_change_window_height (fn_win, fn_win->line_count - fn_win->height);
     244  
     245    return FN_FOUND;
     246  }
     247  
     248  /* Show the footnotes associated with this node in another window. */
     249  DECLARE_INFO_COMMAND (info_show_footnotes,
     250     _("Show the footnotes associated with this node in another window"))
     251  {
     252    /* Make the window go away if it is already showing. */
     253      WINDOW *fn_win = find_footnotes_window ();
     254  
     255      /* If there is an old footnotes window, and it isn't the only window
     256         on the screen, delete it. */
     257      if (fn_win && windows->next)
     258        {
     259          info_delete_window_internal (fn_win);
     260          return;
     261        }
     262  
     263  
     264      switch (info_get_or_remove_footnotes (window))
     265        {
     266        case FN_UNFOUND:
     267          info_error ("%s", msg_no_foot_node);
     268          break;
     269  
     270        case FN_UNABLE:
     271          info_error ("%s", msg_win_too_small);
     272          break;
     273        }
     274  }