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