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