xref: /openbsd-src/gnu/usr.bin/texinfo/info/footnotes.c (revision 1076333c323c9f213f0d653fc52002328f47dbe9)
1840175f0Skstailey /* footnotes.c -- Some functions for manipulating footnotes.
2*1076333cSespie    $Id: footnotes.c,v 1.4 2006/07/17 16:12:36 espie Exp $
3fbc94a17Sniklas 
4*1076333cSespie    Copyright (C) 1993, 1997, 1998, 1999, 2002, 2004 Free Software
5*1076333cSespie    Foundation, Inc.
6fbc94a17Sniklas 
7fbc94a17Sniklas    This program is free software; you can redistribute it and/or modify
8fbc94a17Sniklas    it under the terms of the GNU General Public License as published by
9fbc94a17Sniklas    the Free Software Foundation; either version 2, or (at your option)
10fbc94a17Sniklas    any later version.
11fbc94a17Sniklas 
12fbc94a17Sniklas    This program is distributed in the hope that it will be useful,
13fbc94a17Sniklas    but WITHOUT ANY WARRANTY; without even the implied warranty of
14fbc94a17Sniklas    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15fbc94a17Sniklas    GNU General Public License for more details.
16fbc94a17Sniklas 
17fbc94a17Sniklas    You should have received a copy of the GNU General Public License
18fbc94a17Sniklas    along with this program; if not, write to the Free Software
19fbc94a17Sniklas    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20fbc94a17Sniklas 
21*1076333cSespie    Originally written by Brian Fox (bfox@ai.mit.edu). */
22fbc94a17Sniklas 
23fbc94a17Sniklas #include "info.h"
24fbc94a17Sniklas 
25*1076333cSespie /* Nonzero means attempt to show footnotes when displaying a new window. */
26*1076333cSespie int auto_footnotes_p = 0;
27fbc94a17Sniklas 
28fbc94a17Sniklas static char *footnote_nodename = "*Footnotes*";
29fbc94a17Sniklas 
30*1076333cSespie NODE * make_footnotes_node (NODE *node);
31*1076333cSespie 
32fbc94a17Sniklas #define FOOTNOTE_HEADER_FORMAT \
33*1076333cSespie    "*** Footnotes appearing in the node `%s' ***\n"
34fbc94a17Sniklas 
35fbc94a17Sniklas /* Find the window currently showing footnotes. */
36fbc94a17Sniklas static WINDOW *
find_footnotes_window(void)37*1076333cSespie find_footnotes_window (void)
38fbc94a17Sniklas {
39fbc94a17Sniklas   WINDOW *win;
40fbc94a17Sniklas 
41fbc94a17Sniklas   /* Try to find an existing window first. */
42fbc94a17Sniklas   for (win = windows; win; win = win->next)
43fbc94a17Sniklas     if (internal_info_node_p (win->node) &&
44fbc94a17Sniklas         (strcmp (win->node->nodename, footnote_nodename) == 0))
45fbc94a17Sniklas       break;
46fbc94a17Sniklas 
47fbc94a17Sniklas   return (win);
48fbc94a17Sniklas }
49fbc94a17Sniklas 
50fbc94a17Sniklas /* Manufacture a node containing the footnotes of this node, and
51fbc94a17Sniklas    return the manufactured node.  If NODE has no footnotes, return a
52fbc94a17Sniklas    NULL pointer. */
53fbc94a17Sniklas NODE *
make_footnotes_node(NODE * node)54*1076333cSespie make_footnotes_node (NODE *node)
55fbc94a17Sniklas {
56fbc94a17Sniklas   NODE *fn_node, *result = (NODE *)NULL;
57fbc94a17Sniklas   long fn_start;
58fbc94a17Sniklas 
59fbc94a17Sniklas   /* Make the initial assumption that the footnotes appear as simple
60fbc94a17Sniklas      text within this windows node. */
61fbc94a17Sniklas   fn_node = node;
62fbc94a17Sniklas 
63fbc94a17Sniklas   /* See if this node contains the magic footnote label. */
64fbc94a17Sniklas   fn_start =
65672dff93Sespie     info_search_in_node (FOOTNOTE_LABEL, node, 0, (WINDOW *)NULL, 1, 0);
66fbc94a17Sniklas 
67fbc94a17Sniklas   /* If it doesn't, check to see if it has an associated footnotes node. */
68fbc94a17Sniklas   if (fn_start == -1)
69fbc94a17Sniklas     {
70fbc94a17Sniklas       REFERENCE **refs;
71fbc94a17Sniklas 
72fbc94a17Sniklas       refs = info_xrefs_of_node (node);
73fbc94a17Sniklas 
74fbc94a17Sniklas       if (refs)
75fbc94a17Sniklas         {
76fbc94a17Sniklas           register int i;
77fbc94a17Sniklas           char *refname;
78672dff93Sespie           int reflen = strlen ("-Footnotes") + strlen (node->nodename);
79fbc94a17Sniklas 
80672dff93Sespie           refname = (char *)xmalloc (reflen + 1);
81fbc94a17Sniklas 
82fbc94a17Sniklas           strcpy (refname, node->nodename);
83fbc94a17Sniklas           strcat (refname, "-Footnotes");
84fbc94a17Sniklas 
85fbc94a17Sniklas           for (i = 0; refs[i]; i++)
86fbc94a17Sniklas             if ((refs[i]->nodename != (char *)NULL) &&
87672dff93Sespie                 /* Support both the older "foo-Footnotes" and the new
88672dff93Sespie                    style "foo-Footnote-NN" references.  */
89672dff93Sespie                 (strcmp (refs[i]->nodename, refname) == 0 ||
90672dff93Sespie                  (strncmp (refs[i]->nodename, refname, reflen - 1) == 0 &&
91672dff93Sespie                   refs[i]->nodename[reflen - 1] == '-' &&
92672dff93Sespie                   isdigit (refs[i]->nodename[reflen]))))
93fbc94a17Sniklas               {
94fbc94a17Sniklas                 char *filename;
95fbc94a17Sniklas 
96fbc94a17Sniklas                 filename = node->parent;
97fbc94a17Sniklas                 if (!filename)
98fbc94a17Sniklas                   filename = node->filename;
99fbc94a17Sniklas 
100fbc94a17Sniklas                 fn_node = info_get_node (filename, refname);
101fbc94a17Sniklas 
102fbc94a17Sniklas                 if (fn_node)
103fbc94a17Sniklas                   fn_start = 0;
104fbc94a17Sniklas 
105fbc94a17Sniklas                 break;
106fbc94a17Sniklas               }
107fbc94a17Sniklas 
108fbc94a17Sniklas           free (refname);
109fbc94a17Sniklas           info_free_references (refs);
110fbc94a17Sniklas         }
111fbc94a17Sniklas     }
112fbc94a17Sniklas 
113fbc94a17Sniklas   /* If we never found the start of a footnotes area, quit now. */
114fbc94a17Sniklas   if (fn_start == -1)
115fbc94a17Sniklas     return ((NODE *)NULL);
116fbc94a17Sniklas 
117fbc94a17Sniklas   /* Make the new node. */
118fbc94a17Sniklas   result = (NODE *)xmalloc (sizeof (NODE));
119fbc94a17Sniklas   result->flags = 0;
120672dff93Sespie   result->display_pos = 0;
121fbc94a17Sniklas 
122fbc94a17Sniklas   /* Get the size of the footnotes appearing within this node. */
123fbc94a17Sniklas   {
124fbc94a17Sniklas     char *header;
125fbc94a17Sniklas     long text_start = fn_start;
126fbc94a17Sniklas 
127fbc94a17Sniklas     header = (char *)xmalloc
128fbc94a17Sniklas       (1 + strlen (node->nodename) + strlen (FOOTNOTE_HEADER_FORMAT));
129fbc94a17Sniklas     sprintf (header, FOOTNOTE_HEADER_FORMAT, node->nodename);
130fbc94a17Sniklas 
131fbc94a17Sniklas     /* Move the start of the displayed text to right after the first line.
132fbc94a17Sniklas        This effectively skips either "---- footno...", or "File: foo...". */
133fbc94a17Sniklas     while (text_start < fn_node->nodelen)
134fbc94a17Sniklas       if (fn_node->contents[text_start++] == '\n')
135fbc94a17Sniklas         break;
136fbc94a17Sniklas 
137fbc94a17Sniklas     result->nodelen = strlen (header) + fn_node->nodelen - text_start;
138fbc94a17Sniklas 
139fbc94a17Sniklas     /* Set the contents of this node. */
140fbc94a17Sniklas     result->contents = (char *)xmalloc (1 + result->nodelen);
141fbc94a17Sniklas     sprintf (result->contents, "%s", header);
142fbc94a17Sniklas     memcpy (result->contents + strlen (header),
143fbc94a17Sniklas             fn_node->contents + text_start, fn_node->nodelen - text_start);
144fbc94a17Sniklas 
145fbc94a17Sniklas     name_internal_node (result, footnote_nodename);
146fbc94a17Sniklas     free (header);
147fbc94a17Sniklas   }
148fbc94a17Sniklas 
149fbc94a17Sniklas #if defined (NOTDEF)
150fbc94a17Sniklas   /* If the footnotes were gleaned from the node that we were called with,
151fbc94a17Sniklas      shorten the calling node's display length. */
152fbc94a17Sniklas   if (fn_node == node)
153fbc94a17Sniklas     narrow_node (node, 0, fn_start);
154fbc94a17Sniklas #endif /* NOTDEF */
155fbc94a17Sniklas 
156fbc94a17Sniklas   return (result);
157fbc94a17Sniklas }
158fbc94a17Sniklas 
159fbc94a17Sniklas /* Create or delete the footnotes window depending on whether footnotes
160fbc94a17Sniklas    exist in WINDOW's node or not.  Returns FN_FOUND if footnotes were found
161fbc94a17Sniklas    and displayed.  Returns FN_UNFOUND if there were no footnotes found
162fbc94a17Sniklas    in WINDOW's node.  Returns FN_UNABLE if there were footnotes, but the
163fbc94a17Sniklas    window to show them couldn't be made. */
164fbc94a17Sniklas int
info_get_or_remove_footnotes(WINDOW * window)165*1076333cSespie info_get_or_remove_footnotes (WINDOW *window)
166fbc94a17Sniklas {
167fbc94a17Sniklas   WINDOW *fn_win;
168fbc94a17Sniklas   NODE *new_footnotes;
169fbc94a17Sniklas 
170fbc94a17Sniklas   fn_win = find_footnotes_window ();
171fbc94a17Sniklas 
172fbc94a17Sniklas   /* If we are in the footnotes window, change nothing. */
173fbc94a17Sniklas   if (fn_win == window)
174fbc94a17Sniklas     return (FN_FOUND);
175fbc94a17Sniklas 
176fbc94a17Sniklas   /* Try to find footnotes for this window's node. */
177fbc94a17Sniklas   new_footnotes = make_footnotes_node (window->node);
178fbc94a17Sniklas 
179fbc94a17Sniklas   /* If there was a window showing footnotes, and there are no footnotes
180fbc94a17Sniklas      for the current window, delete the old footnote window. */
181fbc94a17Sniklas   if (fn_win && !new_footnotes)
182fbc94a17Sniklas     {
183fbc94a17Sniklas       if (windows->next)
184fbc94a17Sniklas         info_delete_window_internal (fn_win);
185fbc94a17Sniklas     }
186fbc94a17Sniklas 
187fbc94a17Sniklas   /* If there are footnotes for this window's node, but no window around
188fbc94a17Sniklas      showing footnotes, try to make a new window. */
189fbc94a17Sniklas   if (new_footnotes && !fn_win)
190fbc94a17Sniklas     {
191fbc94a17Sniklas       WINDOW *old_active;
192fbc94a17Sniklas       WINDOW *last, *win;
193fbc94a17Sniklas 
194fbc94a17Sniklas       /* Always make this window be the last one appearing in the list.  Find
195fbc94a17Sniklas          the last window in the chain. */
196fbc94a17Sniklas       for (win = windows, last = windows; win; last = win, win = win->next);
197fbc94a17Sniklas 
198fbc94a17Sniklas       /* Try to split this window, and make the split window the one to
199fbc94a17Sniklas          contain the footnotes. */
200fbc94a17Sniklas       old_active = active_window;
201fbc94a17Sniklas       active_window = last;
202fbc94a17Sniklas       fn_win = window_make_window (new_footnotes);
203fbc94a17Sniklas       active_window = old_active;
204fbc94a17Sniklas 
205fbc94a17Sniklas       if (!fn_win)
206fbc94a17Sniklas         {
207fbc94a17Sniklas           free (new_footnotes->contents);
208fbc94a17Sniklas           free (new_footnotes);
209fbc94a17Sniklas 
210fbc94a17Sniklas           /* If we are hacking automatic footnotes, and there are footnotes
211fbc94a17Sniklas              but we couldn't display them, print a message to that effect. */
212fbc94a17Sniklas           if (auto_footnotes_p)
213*1076333cSespie             inform_in_echo_area ((char *) _("Footnotes could not be displayed"));
214fbc94a17Sniklas           return (FN_UNABLE);
215fbc94a17Sniklas         }
216fbc94a17Sniklas     }
217fbc94a17Sniklas 
218fbc94a17Sniklas   /* If there are footnotes, and there is a window to display them,
219fbc94a17Sniklas      make that window be the number of lines appearing in the footnotes. */
220fbc94a17Sniklas   if (new_footnotes && fn_win)
221fbc94a17Sniklas     {
222fbc94a17Sniklas       window_set_node_of_window (fn_win, new_footnotes);
223fbc94a17Sniklas 
224fbc94a17Sniklas       window_change_window_height
225fbc94a17Sniklas         (fn_win, fn_win->line_count - fn_win->height);
226fbc94a17Sniklas 
227fbc94a17Sniklas       remember_window_and_node (fn_win, new_footnotes);
228fbc94a17Sniklas       add_gcable_pointer (new_footnotes->contents);
229fbc94a17Sniklas     }
230fbc94a17Sniklas 
231fbc94a17Sniklas   if (!new_footnotes)
232fbc94a17Sniklas     return (FN_UNFOUND);
233fbc94a17Sniklas   else
234fbc94a17Sniklas     return (FN_FOUND);
235fbc94a17Sniklas }
236fbc94a17Sniklas 
237fbc94a17Sniklas /* Show the footnotes associated with this node in another window. */
238fbc94a17Sniklas DECLARE_INFO_COMMAND (info_show_footnotes,
239840175f0Skstailey    _("Show the footnotes associated with this node in another window"))
240fbc94a17Sniklas {
241fbc94a17Sniklas   /* A negative argument means just make the window go away. */
242fbc94a17Sniklas   if (count < 0)
243fbc94a17Sniklas     {
244fbc94a17Sniklas       WINDOW *fn_win = find_footnotes_window ();
245fbc94a17Sniklas 
246fbc94a17Sniklas       /* If there is an old footnotes window, and it isn't the only window
247fbc94a17Sniklas          on the screen, delete it. */
248fbc94a17Sniklas       if (fn_win && windows->next)
249fbc94a17Sniklas         info_delete_window_internal (fn_win);
250fbc94a17Sniklas     }
251fbc94a17Sniklas   else
252fbc94a17Sniklas     {
253fbc94a17Sniklas       int result;
254fbc94a17Sniklas 
255fbc94a17Sniklas       result = info_get_or_remove_footnotes (window);
256fbc94a17Sniklas 
257fbc94a17Sniklas       switch (result)
258fbc94a17Sniklas         {
259fbc94a17Sniklas         case FN_UNFOUND:
260*1076333cSespie           info_error ((char *) msg_no_foot_node, NULL, NULL);
261fbc94a17Sniklas           break;
262fbc94a17Sniklas 
263fbc94a17Sniklas         case FN_UNABLE:
264*1076333cSespie           info_error ((char *) msg_win_too_small, NULL, NULL);
265fbc94a17Sniklas           break;
266fbc94a17Sniklas         }
267fbc94a17Sniklas     }
268fbc94a17Sniklas }
269