Upgrade Texinfo from 4.8 to 4.13 on the vendor branch
[dragonfly.git] / contrib / texinfo / info / nodemenu.c
1 /* nodemenu.c -- produce a menu of all visited nodes.
2    $Id: nodemenu.c,v 1.11 2008/06/11 09:55:42 gray Exp $
3
4    Copyright (C) 1993, 1997, 1998, 2002, 2003, 2004, 2007, 2008
5    Free Software 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 3 of the License, or
10    (at your option) 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, see <http://www.gnu.org/licenses/>.
19
20    Written by Brian Fox (bfox@ai.mit.edu). */
21
22 #include "info.h"
23
24 NODE * get_visited_nodes (Function *filter_func);
25
26 /* Return a line describing the format of a node information line. */
27 static const char *
28 nodemenu_format_info (void)
29 {
30   return _("\n\
31 * Menu:\n\
32   (File)Node                        Lines   Size   Containing File\n\
33   ----------                        -----   ----   ---------------");
34 }
35
36 /* Produce a formatted line of information about NODE.  Here is what we want
37    the output listing to look like:
38
39 * Menu:
40   (File)Node                        Lines   Size   Containing File
41   ----------                        -----   ----   ---------------
42 * (emacs)Buffers::                  48      2230   /usr/gnu/info/emacs/emacs-1
43 * (autoconf)Writing configure.in::  123     58789  /usr/gnu/info/autoconf/autoconf-1
44 * (dir)Top::                        40      589    /usr/gnu/info/dir
45 */
46 static char *
47 format_node_info (NODE *node)
48 {
49   register int i, len;
50   char *parent, *containing_file;
51   static char *line_buffer = NULL;
52
53   if (!line_buffer)
54     line_buffer = xmalloc (1000);
55
56   if (node->parent)
57     {
58       parent = filename_non_directory (node->parent);
59       if (!parent)
60         parent = node->parent;
61     }
62   else
63     parent = NULL;
64
65   containing_file = node->filename;
66
67   if (!parent && !*containing_file)
68     sprintf (line_buffer, "* %s::", node->nodename);
69   else
70     {
71       char *file = NULL;
72
73       if (parent)
74         file = parent;
75       else
76         file = filename_non_directory (containing_file);
77
78       if (!file)
79         file = containing_file;
80
81       if (!*file)
82         file = "dir";
83
84       sprintf (line_buffer, "* (%s)%s::", file, node->nodename);
85     }
86
87   len = pad_to (36, line_buffer);
88
89   {
90     int lines = 1;
91
92     for (i = 0; i < node->nodelen; i++)
93       if (node->contents[i] == '\n')
94         lines++;
95
96     sprintf (line_buffer + len, "%d", lines);
97   }
98
99   len = pad_to (44, line_buffer);
100   sprintf (line_buffer + len, "%ld", node->nodelen);
101
102   if (node->filename && *(node->filename))
103     {
104       len = pad_to (51, line_buffer);
105       strcpy (line_buffer + len, node->filename);
106     }
107
108   return xstrdup (line_buffer);
109 }
110
111 /* Little string comparison routine for qsort (). */
112 static int
113 compare_strings (const void *entry1, const void *entry2)
114 {
115   char **e1 = (char **) entry1;
116   char **e2 = (char **) entry2;
117
118   return mbscasecmp (*e1, *e2);
119 }
120
121 /* The name of the nodemenu node. */
122 static char *nodemenu_nodename = "*Node Menu*";
123
124 /* Produce an informative listing of all the visited nodes, and return it
125    in a node.  If FILTER_FUNC is non-null, it is a function which filters
126    which nodes will appear in the listing.  FILTER_FUNC takes an argument
127    of NODE, and returns non-zero if the node should appear in the listing. */
128 NODE *
129 get_visited_nodes (Function *filter_func)
130 {
131   register int i, iw_index;
132   INFO_WINDOW *info_win;
133   NODE *node;
134   char **lines = NULL;
135   int lines_index = 0, lines_slots = 0;
136
137   if (!info_windows)
138     return NULL;
139
140   for (iw_index = 0; (info_win = info_windows[iw_index]); iw_index++)
141     {
142       for (i = 0; i < info_win->nodes_index; i++)
143         {
144           node = info_win->nodes[i];
145
146           /* We skip mentioning "*Node Menu*" nodes. */
147           if (internal_info_node_p (node) &&
148               (strcmp (node->nodename, nodemenu_nodename) == 0))
149             continue;
150
151           if (node && (!filter_func || (*filter_func) (node)))
152             {
153               char *line;
154
155               line = format_node_info (node);
156               add_pointer_to_array
157                 (line, lines_index, lines, lines_slots, 20, char *);
158             }
159         }
160     }
161
162   /* Sort the array of information lines, if there are any. */
163   if (lines)
164     {
165       register int j, newlen;
166       char **temp;
167
168       qsort (lines, lines_index, sizeof (char *), compare_strings);
169
170       /* Delete duplicates. */
171       for (i = 0, newlen = 1; i < lines_index - 1; i++)
172         {
173           /* Use FILENAME_CMP here, since the most important piece
174              of info in each line is the file name of the node.  */
175           if (FILENAME_CMP (lines[i], lines[i + 1]) == 0)
176             {
177               free (lines[i]);
178               lines[i] = NULL;
179             }
180           else
181             newlen++;
182         }
183
184       /* We have free ()'d and marked all of the duplicate slots.
185          Copy the live slots rather than pruning the dead slots. */
186       temp = xmalloc ((1 + newlen) * sizeof (char *));
187       for (i = 0, j = 0; i < lines_index; i++)
188         if (lines[i])
189           temp[j++] = lines[i];
190
191       temp[j] = NULL;
192       free (lines);
193       lines = temp;
194       lines_index = newlen;
195     }
196
197   initialize_message_buffer ();
198
199   printf_to_message_buffer
200     ("%s", replace_in_documentation
201      (_("Here is the menu of nodes you have recently visited.\n\
202 Select one from this menu, or use `\\[history-node]' in another window.\n"), 0),
203      NULL, NULL);
204
205   printf_to_message_buffer ("%s\n", (char *) nodemenu_format_info (),
206       NULL, NULL);
207
208   for (i = 0; (lines != NULL) && (i < lines_index); i++)
209     {
210       printf_to_message_buffer ("%s\n", lines[i], NULL, NULL);
211       free (lines[i]);
212     }
213
214   if (lines)
215     free (lines);
216
217   node = message_buffer_to_node ();
218   add_gcable_pointer (node->contents);
219   return node;
220 }
221
222 DECLARE_INFO_COMMAND (list_visited_nodes,
223    _("Make a window containing a menu of all of the currently visited nodes"))
224 {
225   WINDOW *new;
226   NODE *node;
227
228   set_remembered_pagetop_and_point (window);
229
230   /* If a window is visible and showing the buffer list already, re-use it. */
231   for (new = windows; new; new = new->next)
232     {
233       node = new->node;
234
235       if (internal_info_node_p (node) &&
236           (strcmp (node->nodename, nodemenu_nodename) == 0))
237         break;
238     }
239
240   /* If we couldn't find an existing window, try to use the next window
241      in the chain. */
242   if (!new)
243     {
244       if (window->next)
245         new = window->next;
246       /* If there is more than one window, wrap around. */
247       else if (window != windows)
248         new = windows;
249     }
250
251   /* If we still don't have a window, make a new one to contain the list. */
252   if (!new)
253     {
254       WINDOW *old_active;
255
256       old_active = active_window;
257       active_window = window;
258       new = window_make_window (NULL);
259       active_window = old_active;
260     }
261
262   /* If we couldn't make a new window, use this one. */
263   if (!new)
264     new = window;
265
266   /* Lines do not wrap in this window. */
267   new->flags |= W_NoWrap;
268   node = get_visited_nodes (NULL);
269   name_internal_node (node, nodemenu_nodename);
270
271 #if 0
272   /* Even if this is an internal node, we don't want the window
273      system to treat it specially.  So we turn off the internalness
274      of it here. */
275   /* Why?  We depend on internal_info_node_p returning true, so we must
276      not remove the flag.  Otherwise, the *Node Menu* nodes themselves
277      appear in the node menu.  --Andreas Schwab
278      <schwab@issan.informatik.uni-dortmund.de>.  */
279   node->flags &= ~N_IsInternal;
280 #endif
281
282   /* If this window is already showing a node menu, reuse the existing node
283      slot. */
284   {
285     int remember_me = 1;
286
287 #if defined (NOTDEF)
288     if (internal_info_node_p (new->node) &&
289         (strcmp (new->node->nodename, nodemenu_nodename) == 0))
290       remember_me = 0;
291 #endif /* NOTDEF */
292
293     window_set_node_of_window (new, node);
294
295     if (remember_me)
296       remember_window_and_node (new, node);
297   }
298
299   active_window = new;
300 }
301
302 DECLARE_INFO_COMMAND (select_visited_node,
303       _("Select a node which has been previously visited in a visible window"))
304 {
305   char *line;
306   NODE *node;
307   REFERENCE **menu;
308
309   node = get_visited_nodes (NULL);
310
311   menu = info_menu_of_node (node);
312   free (node);
313
314   line =
315     info_read_completing_in_echo_area (window,
316         _("Select visited node: "), menu);
317
318   window = active_window;
319
320   /* User aborts, just quit. */
321   if (!line)
322     {
323       info_abort_key (window, 0, 0);
324       info_free_references (menu);
325       return;
326     }
327
328   if (*line)
329     {
330       REFERENCE *entry;
331
332       /* Find the selected label in the references. */
333       entry = info_get_labeled_reference (line, menu);
334
335       if (!entry)
336         info_error (_("The reference disappeared! (%s)."), line, NULL);
337       else
338         info_select_reference (window, entry);
339     }
340
341   free (line);
342   info_free_references (menu);
343
344   if (!info_error_was_printed)
345     window_clear_echo_area ();
346 }