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