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