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