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 * 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 * 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 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 * 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