1 1.2 christos /* $NetBSD: session.c,v 1.2 2016/01/14 00:34:52 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* session.c -- user windowing interface to Info. 4 1.1 christos Id: session.c,v 1.16 2004/12/14 00:15:36 karl Exp 5 1.1 christos 6 1.1 christos Copyright (C) 1993, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 7 1.1 christos Free Software Foundation, Inc. 8 1.1 christos 9 1.1 christos This program is free software; you can redistribute it and/or modify 10 1.1 christos it under the terms of the GNU General Public License as published by 11 1.1 christos the Free Software Foundation; either version 2, or (at your option) 12 1.1 christos any later version. 13 1.1 christos 14 1.1 christos This program is distributed in the hope that it will be useful, 15 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of 16 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 1.1 christos GNU General Public License for more details. 18 1.1 christos 19 1.1 christos You should have received a copy of the GNU General Public License 20 1.1 christos along with this program; if not, write to the Free Software 21 1.1 christos Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 22 1.1 christos 23 1.1 christos Originally written by Brian Fox (bfox (at) ai.mit.edu). */ 24 1.1 christos 25 1.1 christos #include "info.h" 26 1.1 christos #include "search.h" 27 1.1 christos #include <sys/ioctl.h> 28 1.1 christos 29 1.1 christos #if defined (HAVE_SYS_TIME_H) 30 1.1 christos # include <sys/time.h> 31 1.1 christos # define HAVE_STRUCT_TIMEVAL 32 1.1 christos #endif /* HAVE_SYS_TIME_H */ 33 1.1 christos 34 1.1 christos #if defined (HANDLE_MAN_PAGES) 35 1.1 christos # include "man.h" 36 1.1 christos #endif 37 1.1 christos 38 1.1 christos static void info_clear_pending_input (void); 39 1.1 christos static void info_set_pending_input (unsigned char key); 40 1.1 christos static void info_handle_pointer (char *label, WINDOW *window); 41 1.1 christos static void display_info_keyseq (int expecting_future_input); 42 1.1 christos char *node_printed_rep (NODE *node); 43 1.1 christos 44 1.1 christos /* **************************************************************** */ 45 1.1 christos /* */ 46 1.1 christos /* Running an Info Session */ 47 1.1 christos /* */ 48 1.1 christos /* **************************************************************** */ 49 1.1 christos 50 1.1 christos /* The place that we are reading input from. */ 51 1.1 christos static FILE *info_input_stream = NULL; 52 1.1 christos 53 1.1 christos /* The last executed command. */ 54 1.1 christos VFunction *info_last_executed_command = NULL; 55 1.1 christos 56 1.1 christos /* Becomes non-zero when 'q' is typed to an Info window. */ 57 1.1 christos int quit_info_immediately = 0; 58 1.1 christos 59 1.1 christos /* Array of structures describing for each window which nodes have been 60 1.1 christos visited in that window. */ 61 1.1 christos INFO_WINDOW **info_windows = NULL; 62 1.1 christos 63 1.1 christos /* Where to add the next window, if we need to add one. */ 64 1.1 christos static int info_windows_index = 0; 65 1.1 christos 66 1.1 christos /* Number of slots allocated to `info_windows'. */ 67 1.1 christos static int info_windows_slots = 0; 68 1.1 christos 69 1.1 christos void remember_window_and_node (WINDOW *window, NODE *node); 70 1.1 christos void forget_window_and_nodes (WINDOW *window); 71 1.1 christos void display_startup_message_and_start (void); 72 1.1 christos 73 1.1 christos /* Begin an info session finding the nodes specified by FILENAME and NODENAMES. 74 1.1 christos For each loaded node, create a new window. Always split the largest of the 75 1.1 christos available windows. */ 76 1.1 christos void 77 1.1 christos begin_multiple_window_info_session (char *filename, char **nodenames) 78 1.1 christos { 79 1.1 christos register int i; 80 1.1 christos WINDOW *window = (WINDOW *)NULL; 81 1.1 christos 82 1.1 christos for (i = 0; nodenames[i]; i++) 83 1.1 christos { 84 1.1 christos NODE *node; 85 1.1 christos 86 1.1 christos node = info_get_node (filename, nodenames[i]); 87 1.1 christos 88 1.1 christos if (!node) 89 1.1 christos break; 90 1.1 christos 91 1.1 christos /* If this is the first node, initialize the info session. */ 92 1.1 christos if (!window) 93 1.1 christos { 94 1.1 christos initialize_info_session (node, 1); 95 1.1 christos window = active_window; 96 1.1 christos } 97 1.1 christos else 98 1.1 christos { 99 1.1 christos /* Find the largest window in WINDOWS, and make that be the active 100 1.1 christos one. Then split it and add our window and node to the list 101 1.1 christos of remembered windows and nodes. Then tile the windows. */ 102 1.1 christos WINDOW *win, *largest = NULL; 103 1.1 christos int max_height = 0; 104 1.1 christos 105 1.1 christos for (win = windows; win; win = win->next) 106 1.1 christos if (win->height > max_height) 107 1.1 christos { 108 1.1 christos max_height = win->height; 109 1.1 christos largest = win; 110 1.1 christos } 111 1.1 christos 112 1.1 christos if (!largest) 113 1.1 christos { 114 1.1 christos display_update_display (windows); 115 1.1 christos info_error ((char *) msg_cant_find_window, NULL, NULL); 116 1.1 christos info_session (); 117 1.1 christos xexit (0); 118 1.1 christos } 119 1.1 christos 120 1.1 christos active_window = largest; 121 1.1 christos window = window_make_window (node); 122 1.1 christos if (window) 123 1.1 christos { 124 1.1 christos window_tile_windows (TILE_INTERNALS); 125 1.1 christos remember_window_and_node (window, node); 126 1.1 christos } 127 1.1 christos else 128 1.1 christos { 129 1.1 christos display_update_display (windows); 130 1.1 christos info_error ((char *) msg_win_too_small, NULL, NULL); 131 1.1 christos info_session (); 132 1.1 christos xexit (0); 133 1.1 christos } 134 1.1 christos } 135 1.1 christos } 136 1.1 christos display_startup_message_and_start (); 137 1.1 christos } 138 1.1 christos 139 1.1 christos /* Start an info session with INITIAL_NODE, and an error message in the echo 140 1.1 christos area made from FORMAT and ARG. */ 141 1.1 christos void 142 1.1 christos begin_info_session_with_error (NODE *initial_node, char *format, 143 1.1 christos void *arg1, void *arg2) 144 1.1 christos { 145 1.1 christos initialize_info_session (initial_node, 1); 146 1.1 christos info_error (format, arg1, arg2); 147 1.1 christos info_session (); 148 1.1 christos } 149 1.1 christos 150 1.1 christos /* Start an info session with INITIAL_NODE. */ 151 1.1 christos void 152 1.1 christos begin_info_session (NODE *initial_node) 153 1.1 christos { 154 1.1 christos initialize_info_session (initial_node, 1); 155 1.1 christos display_startup_message_and_start (); 156 1.1 christos } 157 1.1 christos 158 1.1 christos void 159 1.1 christos display_startup_message_and_start (void) 160 1.1 christos { 161 1.1 christos char *format; 162 1.1 christos 163 1.1 christos format = replace_in_documentation 164 1.1 christos ((char *) _("Welcome to Info version %s. Type \\[get-help-window] for help, \\[menu-item] for menu item."), 165 1.1 christos 0); 166 1.1 christos 167 1.1 christos window_message_in_echo_area (format, VERSION, NULL); 168 1.1 christos info_session (); 169 1.1 christos } 170 1.1 christos 171 1.1 christos /* Run an info session with an already initialized window and node. */ 172 1.1 christos void 173 1.1 christos info_session (void) 174 1.1 christos { 175 1.1 christos display_update_display (windows); 176 1.1 christos info_last_executed_command = NULL; 177 1.1 christos info_read_and_dispatch (); 178 1.1 christos /* On program exit, leave the cursor at the bottom of the window, and 179 1.1 christos restore the terminal I/O. */ 180 1.1 christos terminal_goto_xy (0, screenheight - 1); 181 1.1 christos terminal_clear_to_eol (); 182 1.1 christos fflush (stdout); 183 1.1 christos terminal_unprep_terminal (); 184 1.1 christos close_dribble_file (); 185 1.1 christos } 186 1.1 christos 187 1.1 christos /* Here is a window-location dependent event loop. Called from the 188 1.1 christos functions info_session (), and from read_xxx_in_echo_area (). */ 189 1.1 christos void 190 1.1 christos info_read_and_dispatch (void) 191 1.1 christos { 192 1.1 christos unsigned char key; 193 1.1 christos int done; 194 1.1 christos done = 0; 195 1.1 christos 196 1.1 christos while (!done && !quit_info_immediately) 197 1.1 christos { 198 1.1 christos int lk = 0; 199 1.1 christos 200 1.1 christos /* If we haven't just gone up or down a line, there is no 201 1.1 christos goal column for this window. */ 202 1.1 christos if ((info_last_executed_command != (VFunction *) info_next_line) && 203 1.1 christos (info_last_executed_command != (VFunction *) info_prev_line)) 204 1.1 christos active_window->goal_column = -1; 205 1.1 christos 206 1.1 christos if (echo_area_is_active) 207 1.1 christos { 208 1.1 christos lk = echo_area_last_command_was_kill; 209 1.1 christos echo_area_prep_read (); 210 1.1 christos } 211 1.1 christos 212 1.1 christos if (!info_any_buffered_input_p ()) 213 1.1 christos display_update_display (windows); 214 1.1 christos 215 1.1 christos display_cursor_at_point (active_window); 216 1.1 christos info_initialize_numeric_arg (); 217 1.1 christos 218 1.1 christos initialize_keyseq (); 219 1.1 christos key = info_get_input_char (); 220 1.1 christos 221 1.1 christos /* No errors yet. We just read a character, that's all. Only clear 222 1.1 christos the echo_area if it is not currently active. */ 223 1.1 christos if (!echo_area_is_active) 224 1.1 christos window_clear_echo_area (); 225 1.1 christos 226 1.1 christos info_error_was_printed = 0; 227 1.1 christos 228 1.1 christos /* Do the selected command. */ 229 1.1 christos info_dispatch_on_key (key, active_window->keymap); 230 1.1 christos 231 1.1 christos if (echo_area_is_active) 232 1.1 christos { 233 1.1 christos /* Echo area commands that do killing increment the value of 234 1.1 christos ECHO_AREA_LAST_COMMAND_WAS_KILL. Thus, if there is no 235 1.1 christos change in the value of this variable, the last command 236 1.1 christos executed was not a kill command. */ 237 1.1 christos if (lk == echo_area_last_command_was_kill) 238 1.1 christos echo_area_last_command_was_kill = 0; 239 1.1 christos 240 1.1 christos if (ea_last_executed_command == (VFunction *) ea_newline || 241 1.1 christos info_aborted_echo_area) 242 1.1 christos { 243 1.1 christos ea_last_executed_command = (VFunction *)NULL; 244 1.1 christos done = 1; 245 1.1 christos } 246 1.1 christos 247 1.1 christos if (info_last_executed_command == (VFunction *) info_quit) 248 1.1 christos quit_info_immediately = 1; 249 1.1 christos } 250 1.1 christos else if (info_last_executed_command == (VFunction *) info_quit) 251 1.1 christos done = 1; 252 1.1 christos } 253 1.1 christos } 254 1.1 christos 255 1.1 christos /* Found in signals.c */ 256 1.1 christos extern void initialize_info_signal_handler (void ); 257 1.1 christos 258 1.1 christos /* Initialize the first info session by starting the terminal, window, 259 1.1 christos and display systems. If CLEAR_SCREEN is 0, don't clear the screen. */ 260 1.1 christos void 261 1.1 christos initialize_info_session (NODE *node, int clear_screen) 262 1.1 christos { 263 1.1 christos char *term_name = getenv ("TERM"); 264 1.1 christos terminal_initialize_terminal (term_name); 265 1.1 christos 266 1.1 christos if (terminal_is_dumb_p) 267 1.1 christos { 268 1.1 christos if (!term_name) 269 1.1 christos term_name = "dumb"; 270 1.1 christos 271 1.1 christos info_error ((char *) msg_term_too_dumb, term_name, NULL); 272 1.1 christos xexit (1); 273 1.1 christos } 274 1.1 christos 275 1.1 christos if (clear_screen) 276 1.1 christos { 277 1.1 christos terminal_prep_terminal (); 278 1.1 christos terminal_clear_screen (); 279 1.1 christos } 280 1.1 christos 281 1.1 christos initialize_info_keymaps (); 282 1.1 christos window_initialize_windows (screenwidth, screenheight); 283 1.1 christos initialize_info_signal_handler (); 284 1.1 christos display_initialize_display (screenwidth, screenheight); 285 1.1 christos info_set_node_of_window (0, active_window, node); 286 1.1 christos 287 1.1 christos /* Tell the window system how to notify us when a window needs to be 288 1.1 christos asynchronously deleted (e.g., user resizes window very small). */ 289 1.1 christos window_deletion_notifier = (VFunction *) forget_window_and_nodes; 290 1.1 christos 291 1.1 christos /* If input has not been redirected yet, make it come from unbuffered 292 1.1 christos standard input. */ 293 1.1 christos if (!info_input_stream) 294 1.1 christos { 295 1.1 christos setbuf (stdin, NULL); 296 1.1 christos info_input_stream = stdin; 297 1.1 christos } 298 1.1 christos 299 1.1 christos info_windows_initialized_p = 1; 300 1.1 christos } 301 1.1 christos 302 1.1 christos /* Tell Info that input is coming from the file FILENAME. */ 303 1.1 christos void 304 1.1 christos info_set_input_from_file (char *filename) 305 1.1 christos { 306 1.1 christos FILE *stream; 307 1.1 christos 308 1.1 christos /* Input may include binary characters. */ 309 1.1 christos stream = fopen (filename, FOPEN_RBIN); 310 1.1 christos 311 1.1 christos if (!stream) 312 1.1 christos return; 313 1.1 christos 314 1.1 christos if ((info_input_stream != (FILE *)NULL) && 315 1.1 christos (info_input_stream != stdin)) 316 1.1 christos fclose (info_input_stream); 317 1.1 christos 318 1.1 christos info_input_stream = stream; 319 1.1 christos 320 1.1 christos if (stream != stdin) 321 1.1 christos display_inhibited = 1; 322 1.1 christos } 323 1.1 christos 324 1.1 christos /* Return the INFO_WINDOW containing WINDOW, or NULL if there isn't one. */ 325 1.1 christos static INFO_WINDOW * 326 1.1 christos get_info_window_of_window (WINDOW *window) 327 1.1 christos { 328 1.1 christos register int i; 329 1.1 christos INFO_WINDOW *info_win = (INFO_WINDOW *)NULL; 330 1.1 christos 331 1.1 christos for (i = 0; info_windows && (info_win = info_windows[i]); i++) 332 1.1 christos if (info_win->window == window) 333 1.1 christos break; 334 1.1 christos 335 1.1 christos return (info_win); 336 1.1 christos } 337 1.1 christos 338 1.1 christos /* Reset the remembered pagetop and point of WINDOW to WINDOW's current 339 1.1 christos values if the window and node are the same as the current one being 340 1.1 christos displayed. */ 341 1.1 christos void 342 1.1 christos set_remembered_pagetop_and_point (WINDOW *window) 343 1.1 christos { 344 1.1 christos INFO_WINDOW *info_win; 345 1.1 christos 346 1.1 christos info_win = get_info_window_of_window (window); 347 1.1 christos 348 1.1 christos if (!info_win) 349 1.1 christos return; 350 1.1 christos 351 1.1 christos if (info_win->nodes_index && 352 1.1 christos (info_win->nodes[info_win->current] == window->node)) 353 1.1 christos { 354 1.1 christos info_win->pagetops[info_win->current] = window->pagetop; 355 1.1 christos info_win->points[info_win->current] = window->point; 356 1.1 christos } 357 1.1 christos } 358 1.1 christos 359 1.1 christos void 360 1.1 christos remember_window_and_node (WINDOW *window, NODE *node) 361 1.1 christos { 362 1.1 christos /* See if we already have this window in our list. */ 363 1.1 christos INFO_WINDOW *info_win = get_info_window_of_window (window); 364 1.1 christos 365 1.1 christos /* If the window wasn't already on our list, then make a new entry. */ 366 1.1 christos if (!info_win) 367 1.1 christos { 368 1.1 christos info_win = (INFO_WINDOW *)xmalloc (sizeof (INFO_WINDOW)); 369 1.1 christos info_win->window = window; 370 1.1 christos info_win->nodes = (NODE **)NULL; 371 1.1 christos info_win->pagetops = (int *)NULL; 372 1.1 christos info_win->points = (long *)NULL; 373 1.1 christos info_win->current = 0; 374 1.1 christos info_win->nodes_index = 0; 375 1.1 christos info_win->nodes_slots = 0; 376 1.1 christos 377 1.1 christos add_pointer_to_array (info_win, info_windows_index, info_windows, 378 1.1 christos info_windows_slots, 10, INFO_WINDOW *); 379 1.1 christos } 380 1.1 christos 381 1.1 christos /* If this node, the current pagetop, and the current point are the 382 1.1 christos same as the current saved node and pagetop, don't really add this to 383 1.1 christos the list of history nodes. This may happen only at the very 384 1.1 christos beginning of the program, I'm not sure. --karl */ 385 1.1 christos if (info_win->nodes 386 1.1 christos && info_win->current >= 0 387 1.1 christos && info_win->nodes[info_win->current]->contents == node->contents 388 1.1 christos && info_win->pagetops[info_win->current] == window->pagetop 389 1.1 christos && info_win->points[info_win->current] == window->point) 390 1.1 christos return; 391 1.1 christos 392 1.1 christos /* Remember this node, the currently displayed pagetop, and the current 393 1.1 christos location of point in this window. Because we are updating pagetops 394 1.1 christos and points as well as nodes, it is more efficient to avoid the 395 1.1 christos add_pointer_to_array macro here. */ 396 1.1 christos if (info_win->nodes_index + 2 >= info_win->nodes_slots) 397 1.1 christos { 398 1.1 christos info_win->nodes_slots += 20; 399 1.1 christos info_win->nodes = (NODE **) xrealloc (info_win->nodes, 400 1.1 christos info_win->nodes_slots * sizeof (NODE *)); 401 1.1 christos info_win->pagetops = (int *) xrealloc (info_win->pagetops, 402 1.1 christos info_win->nodes_slots * sizeof (int)); 403 1.1 christos info_win->points = (long *) xrealloc (info_win->points, 404 1.1 christos info_win->nodes_slots * sizeof (long)); 405 1.1 christos } 406 1.1 christos 407 1.1 christos info_win->nodes[info_win->nodes_index] = node; 408 1.1 christos info_win->pagetops[info_win->nodes_index] = window->pagetop; 409 1.1 christos info_win->points[info_win->nodes_index] = window->point; 410 1.1 christos info_win->current = info_win->nodes_index++; 411 1.1 christos info_win->nodes[info_win->nodes_index] = NULL; 412 1.1 christos info_win->pagetops[info_win->nodes_index] = 0; 413 1.1 christos info_win->points[info_win->nodes_index] = 0; 414 1.1 christos } 415 1.1 christos 416 1.1 christos #define DEBUG_FORGET_WINDOW_AND_NODES 417 1.1 christos #if defined (DEBUG_FORGET_WINDOW_AND_NODES) 418 1.1 christos static void 419 1.1 christos consistency_check_info_windows (void) 420 1.1 christos { 421 1.1 christos register int i; 422 1.1 christos 423 1.1 christos for (i = 0; i < info_windows_index; i++) 424 1.1 christos { 425 1.1 christos WINDOW *win; 426 1.1 christos 427 1.1 christos for (win = windows; win; win = win->next) 428 1.1 christos if (win == info_windows[i]->window) 429 1.1 christos break; 430 1.1 christos 431 1.1 christos if (!win) 432 1.1 christos abort (); 433 1.1 christos } 434 1.1 christos } 435 1.1 christos #endif /* DEBUG_FORGET_WINDOW_AND_NODES */ 436 1.1 christos 437 1.1 christos /* Remove WINDOW and its associated list of nodes from INFO_WINDOWS. */ 438 1.1 christos void 439 1.1 christos forget_window_and_nodes (WINDOW *window) 440 1.1 christos { 441 1.1 christos register int i; 442 1.1 christos INFO_WINDOW *info_win = (INFO_WINDOW *)NULL; 443 1.1 christos 444 1.1 christos for (i = 0; info_windows && (info_win = info_windows[i]); i++) 445 1.1 christos if (info_win->window == window) 446 1.1 christos break; 447 1.1 christos 448 1.1 christos /* If we found the window to forget, then do so. */ 449 1.1 christos if (info_win) 450 1.1 christos { 451 1.1 christos while (i < info_windows_index) 452 1.1 christos { 453 1.1 christos info_windows[i] = info_windows[i + 1]; 454 1.1 christos i++; 455 1.1 christos } 456 1.1 christos 457 1.1 christos info_windows_index--; 458 1.1 christos info_windows[info_windows_index] = (INFO_WINDOW *)NULL; 459 1.1 christos 460 1.1 christos if (info_win->nodes) 461 1.1 christos { 462 1.1 christos /* Free the node structures which held onto internal node contents 463 1.1 christos here. This doesn't free the contents; we have a garbage collector 464 1.1 christos which does that. */ 465 1.1 christos for (i = 0; info_win->nodes[i]; i++) 466 1.1 christos if (internal_info_node_p (info_win->nodes[i])) 467 1.1 christos free (info_win->nodes[i]); 468 1.1 christos free (info_win->nodes); 469 1.1 christos 470 1.1 christos maybe_free (info_win->pagetops); 471 1.1 christos maybe_free (info_win->points); 472 1.1 christos } 473 1.1 christos 474 1.1 christos free (info_win); 475 1.1 christos } 476 1.1 christos #if defined (DEBUG_FORGET_WINDOW_AND_NODES) 477 1.1 christos consistency_check_info_windows (); 478 1.1 christos #endif /* DEBUG_FORGET_WINDOW_AND_NODES */ 479 1.1 christos } 480 1.1 christos 481 1.1 christos /* Set WINDOW to show NODE. Remember the new window in our list of Info 482 1.1 christos windows. If we are doing automatic footnote display, also try to display 483 1.1 christos the footnotes for this window. If REMEMBER is nonzero, first call 484 1.1 christos set_remembered_pagetop_and_point. */ 485 1.1 christos void 486 1.1 christos info_set_node_of_window (int remember, WINDOW *window, NODE *node) 487 1.1 christos { 488 1.1 christos if (remember) 489 1.1 christos set_remembered_pagetop_and_point (window); 490 1.1 christos 491 1.1 christos /* Put this node into the window. */ 492 1.1 christos window_set_node_of_window (window, node); 493 1.1 christos 494 1.1 christos /* Remember this node and window in our list of info windows. */ 495 1.1 christos remember_window_and_node (window, node); 496 1.1 christos 497 1.1 christos /* If doing auto-footnote display/undisplay, show the footnotes belonging 498 1.1 christos to this window's node. */ 499 1.1 christos if (auto_footnotes_p) 500 1.1 christos info_get_or_remove_footnotes (window); 501 1.1 christos } 502 1.1 christos 503 1.1 christos 504 1.1 christos /* **************************************************************** */ 506 1.1 christos /* */ 507 1.1 christos /* Info Movement Commands */ 508 1.1 christos /* */ 509 1.1 christos /* **************************************************************** */ 510 1.1 christos 511 1.1 christos /* Change the pagetop of WINDOW to DESIRED_TOP, perhaps scrolling the screen 512 1.1 christos to do so. */ 513 1.1 christos void 514 1.1 christos set_window_pagetop (WINDOW *window, int desired_top) 515 1.1 christos { 516 1.1 christos int point_line, old_pagetop; 517 1.1 christos 518 1.1 christos if (desired_top < 0) 519 1.1 christos desired_top = 0; 520 1.1 christos else if (desired_top > window->line_count) 521 1.1 christos desired_top = window->line_count - 1; 522 1.1 christos 523 1.1 christos if (window->pagetop == desired_top) 524 1.1 christos return; 525 1.1 christos 526 1.1 christos old_pagetop = window->pagetop; 527 1.1 christos window->pagetop = desired_top; 528 1.1 christos 529 1.1 christos /* Make sure that point appears in this window. */ 530 1.1 christos point_line = window_line_of_point (window); 531 1.1 christos if ((point_line < window->pagetop) || 532 1.1 christos ((point_line - window->pagetop) > window->height - 1)) 533 1.1 christos window->point = 534 1.1 christos window->line_starts[window->pagetop] - window->node->contents; 535 1.1 christos 536 1.1 christos window->flags |= W_UpdateWindow; 537 1.1 christos 538 1.1 christos /* Find out which direction to scroll, and scroll the window in that 539 1.1 christos direction. Do this only if there would be a savings in redisplay 540 1.1 christos time. This is true if the amount to scroll is less than the height 541 1.1 christos of the window, and if the number of lines scrolled would be greater 542 1.1 christos than 10 % of the window's height. */ 543 1.1 christos if (old_pagetop < desired_top) 544 1.1 christos { 545 1.1 christos int start, end, amount; 546 1.1 christos 547 1.1 christos amount = desired_top - old_pagetop; 548 1.1 christos 549 1.1 christos if ((amount >= window->height) || 550 1.1 christos (((window->height - amount) * 10) < window->height)) 551 1.1 christos return; 552 1.1 christos 553 1.1 christos start = amount + window->first_row; 554 1.1 christos end = window->height + window->first_row; 555 1.1 christos 556 1.1 christos display_scroll_display (start, end, -amount); 557 1.1 christos } 558 1.1 christos else 559 1.1 christos { 560 1.1 christos int start, end, amount; 561 1.1 christos 562 1.1 christos amount = old_pagetop - desired_top; 563 1.1 christos 564 1.1 christos if ((amount >= window->height) || 565 1.1 christos (((window->height - amount) * 10) < window->height)) 566 1.1 christos return; 567 1.1 christos 568 1.1 christos start = window->first_row; 569 1.1 christos end = (window->first_row + window->height) - amount; 570 1.1 christos display_scroll_display (start, end, amount); 571 1.1 christos } 572 1.1 christos } 573 1.1 christos 574 1.1 christos /* Immediately make WINDOW->point visible on the screen, and move the 575 1.1 christos terminal cursor there. */ 576 1.1 christos static void 577 1.1 christos info_show_point (WINDOW *window) 578 1.1 christos { 579 1.1 christos int old_pagetop; 580 1.1 christos 581 1.1 christos old_pagetop = window->pagetop; 582 1.1 christos window_adjust_pagetop (window); 583 1.1 christos if (old_pagetop != window->pagetop) 584 1.1 christos { 585 1.1 christos int new_pagetop; 586 1.1 christos 587 1.1 christos new_pagetop = window->pagetop; 588 1.1 christos window->pagetop = old_pagetop; 589 1.1 christos set_window_pagetop (window, new_pagetop); 590 1.1 christos } 591 1.1 christos 592 1.1 christos if (window->flags & W_UpdateWindow) 593 1.1 christos display_update_one_window (window); 594 1.1 christos 595 1.1 christos display_cursor_at_point (window); 596 1.1 christos } 597 1.1 christos 598 1.1 christos /* Move WINDOW->point from OLD line index to NEW line index. */ 599 1.1 christos static void 600 1.1 christos move_to_new_line (int old, int new, WINDOW *window) 601 1.1 christos { 602 1.1 christos if (old == -1) 603 1.1 christos { 604 1.1 christos info_error ((char *) msg_cant_find_point, NULL, NULL); 605 1.1 christos } 606 1.1 christos else 607 1.1 christos { 608 1.1 christos int goal; 609 1.1 christos 610 1.1 christos if (new >= window->line_count || new < 0) 611 1.1 christos return; 612 1.1 christos 613 1.1 christos goal = window_get_goal_column (window); 614 1.1 christos window->goal_column = goal; 615 1.1 christos 616 1.1 christos window->point = window->line_starts[new] - window->node->contents; 617 1.1 christos window->point += window_chars_to_goal (window->line_starts[new], goal); 618 1.1 christos info_show_point (window); 619 1.1 christos } 620 1.1 christos } 621 1.1 christos 622 1.1 christos /* Move WINDOW's point down to the next line if possible. */ 623 1.1 christos DECLARE_INFO_COMMAND (info_next_line, _("Move down to the next line")) 624 1.1 christos { 625 1.1 christos int old_line, new_line; 626 1.1 christos 627 1.1 christos if (count < 0) 628 1.1 christos info_prev_line (window, -count, key); 629 1.1 christos else 630 1.1 christos { 631 1.1 christos old_line = window_line_of_point (window); 632 1.1 christos new_line = old_line + count; 633 1.1 christos move_to_new_line (old_line, new_line, window); 634 1.1 christos } 635 1.1 christos } 636 1.1 christos 637 1.1 christos /* Move WINDOW's point up to the previous line if possible. */ 638 1.1 christos DECLARE_INFO_COMMAND (info_prev_line, _("Move up to the previous line")) 639 1.1 christos { 640 1.1 christos int old_line, new_line; 641 1.1 christos 642 1.1 christos if (count < 0) 643 1.1 christos info_next_line (window, -count, key); 644 1.1 christos else 645 1.1 christos { 646 1.1 christos old_line = window_line_of_point (window); 647 1.1 christos new_line = old_line - count; 648 1.1 christos move_to_new_line (old_line, new_line, window); 649 1.1 christos } 650 1.1 christos } 651 1.1 christos 652 1.1 christos /* Move WINDOW's point to the end of the true line. */ 653 1.1 christos DECLARE_INFO_COMMAND (info_end_of_line, _("Move to the end of the line")) 654 1.1 christos { 655 1.1 christos register int point, len; 656 1.1 christos register char *buffer; 657 1.1 christos 658 1.1 christos buffer = window->node->contents; 659 1.1 christos len = window->node->nodelen; 660 1.1 christos 661 1.1 christos for (point = window->point; 662 1.1 christos (point < len) && (buffer[point] != '\n'); 663 1.1 christos point++); 664 1.1 christos 665 1.1 christos if (point != window->point) 666 1.1 christos { 667 1.1 christos window->point = point; 668 1.1 christos info_show_point (window); 669 1.1 christos } 670 1.1 christos } 671 1.1 christos 672 1.1 christos /* Move WINDOW's point to the beginning of the true line. */ 673 1.1 christos DECLARE_INFO_COMMAND (info_beginning_of_line, _("Move to the start of the line")) 674 1.1 christos { 675 1.1 christos register int point; 676 1.1 christos register char *buffer; 677 1.1 christos 678 1.1 christos buffer = window->node->contents; 679 1.1 christos point = window->point; 680 1.1 christos 681 1.1 christos for (; (point) && (buffer[point - 1] != '\n'); point--); 682 1.1 christos 683 1.1 christos /* If at a line start already, do nothing. */ 684 1.1 christos if (point != window->point) 685 1.1 christos { 686 1.1 christos window->point = point; 687 1.1 christos info_show_point (window); 688 1.1 christos } 689 1.1 christos } 690 1.1 christos 691 1.1 christos /* Move point forward in the node. */ 692 1.1 christos DECLARE_INFO_COMMAND (info_forward_char, _("Move forward a character")) 693 1.1 christos { 694 1.1 christos if (count < 0) 695 1.1 christos info_backward_char (window, -count, key); 696 1.1 christos else 697 1.1 christos { 698 1.1 christos window->point += count; 699 1.1 christos 700 1.1 christos if (window->point >= window->node->nodelen) 701 1.1 christos window->point = window->node->nodelen - 1; 702 1.1 christos 703 1.1 christos info_show_point (window); 704 1.1 christos } 705 1.1 christos } 706 1.1 christos 707 1.1 christos /* Move point backward in the node. */ 708 1.1 christos DECLARE_INFO_COMMAND (info_backward_char, _("Move backward a character")) 709 1.1 christos { 710 1.1 christos if (count < 0) 711 1.1 christos info_forward_char (window, -count, key); 712 1.1 christos else 713 1.1 christos { 714 1.1 christos window->point -= count; 715 1.1 christos 716 1.1 christos if (window->point < 0) 717 1.1 christos window->point = 0; 718 1.1 christos 719 1.1 christos info_show_point (window); 720 1.1 christos } 721 1.1 christos } 722 1.1 christos 723 1.1 christos #define alphabetic(c) (islower (c) || isupper (c) || isdigit (c)) 724 1.1 christos 725 1.1 christos /* Move forward a word in this node. */ 726 1.1 christos DECLARE_INFO_COMMAND (info_forward_word, _("Move forward a word")) 727 1.1 christos { 728 1.1 christos long point; 729 1.1 christos char *buffer; 730 1.1 christos int end, c; 731 1.1 christos 732 1.1 christos if (count < 0) 733 1.1 christos { 734 1.1 christos info_backward_word (window, -count, key); 735 1.1 christos return; 736 1.1 christos } 737 1.1 christos 738 1.1 christos point = window->point; 739 1.1 christos buffer = window->node->contents; 740 1.1 christos end = window->node->nodelen; 741 1.1 christos 742 1.1 christos while (count) 743 1.1 christos { 744 1.1 christos if (point + 1 >= end) 745 1.1 christos return; 746 1.1 christos 747 1.1 christos /* If we are not in a word, move forward until we are in one. 748 1.1 christos Then, move forward until we hit a non-alphabetic character. */ 749 1.1 christos c = buffer[point]; 750 1.1 christos 751 1.1 christos if (!alphabetic (c)) 752 1.1 christos { 753 1.1 christos while (++point < end) 754 1.1 christos { 755 1.1 christos c = buffer[point]; 756 1.1 christos if (alphabetic (c)) 757 1.1 christos break; 758 1.1 christos } 759 1.1 christos } 760 1.1 christos 761 1.1 christos if (point >= end) return; 762 1.1 christos 763 1.1 christos while (++point < end) 764 1.1 christos { 765 1.1 christos c = buffer[point]; 766 1.1 christos if (!alphabetic (c)) 767 1.1 christos break; 768 1.1 christos } 769 1.1 christos --count; 770 1.1 christos } 771 1.1 christos window->point = point; 772 1.1 christos info_show_point (window); 773 1.1 christos } 774 1.1 christos 775 1.1 christos DECLARE_INFO_COMMAND (info_backward_word, _("Move backward a word")) 776 1.1 christos { 777 1.1 christos long point; 778 1.1 christos char *buffer; 779 1.1 christos int c; 780 1.1 christos 781 1.1 christos if (count < 0) 782 1.1 christos { 783 1.1 christos info_forward_word (window, -count, key); 784 1.1 christos return; 785 1.1 christos } 786 1.1 christos 787 1.1 christos buffer = window->node->contents; 788 1.1 christos point = window->point; 789 1.1 christos 790 1.1 christos while (count) 791 1.1 christos { 792 1.1 christos if (point == 0) 793 1.1 christos break; 794 1.1 christos 795 1.1 christos /* Like info_forward_word (), except that we look at the 796 1.1 christos characters just before point. */ 797 1.1 christos 798 1.1 christos c = buffer[point - 1]; 799 1.1 christos 800 1.1 christos if (!alphabetic (c)) 801 1.1 christos { 802 1.1 christos while (--point) 803 1.1 christos { 804 1.1 christos c = buffer[point - 1]; 805 1.1 christos if (alphabetic (c)) 806 1.1 christos break; 807 1.1 christos } 808 1.1 christos } 809 1.1 christos 810 1.1 christos while (point) 811 1.1 christos { 812 1.1 christos c = buffer[point - 1]; 813 1.1 christos if (!alphabetic (c)) 814 1.1 christos break; 815 1.1 christos else 816 1.1 christos --point; 817 1.1 christos } 818 1.1 christos --count; 819 1.1 christos } 820 1.1 christos window->point = point; 821 1.1 christos info_show_point (window); 822 1.1 christos } 823 1.1 christos 824 1.1 christos /* Variable controlling the behaviour of default scrolling when you are 825 1.1 christos already at the bottom of a node. Possible values are defined in session.h. 826 1.1 christos The meanings are: 827 1.1 christos 828 1.1 christos IS_Continuous Try to get first menu item, or failing that, the 829 1.1 christos "Next:" pointer, or failing that, the "Up:" and 830 1.1 christos "Next:" of the up. 831 1.1 christos IS_NextOnly Try to get "Next:" menu item. 832 1.1 christos IS_PageOnly Simply give up at the bottom of a node. */ 833 1.1 christos 834 1.1 christos int info_scroll_behaviour = IS_Continuous; 835 1.1 christos 836 1.1 christos /* Choices used by the completer when reading a value for the user-visible 837 1.1 christos variable "scroll-behaviour". */ 838 1.1 christos char *info_scroll_choices[] = { 839 1.1 christos "Continuous", "Next Only", "Page Only", (char *)NULL 840 1.1 christos }; 841 1.1 christos 842 1.1 christos /* Default window sizes for scrolling commands. */ 843 1.1 christos int default_window_size = -1; /* meaning 1 window-full */ 844 1.1 christos int default_scroll_size = -1; /* meaning half screen size */ 845 1.1 christos 846 1.1 christos #define INFO_LABEL_FOUND() \ 847 1.1 christos (info_parsed_nodename || (info_parsed_filename \ 848 1.1 christos && !is_dir_name (info_parsed_filename))) 849 1.1 christos 850 1.1 christos /* Move to 1st menu item, Next, Up/Next, or error in this window. */ 851 1.1 christos static void 852 1.1 christos forward_move_node_structure (WINDOW *window, int behaviour) 853 1.1 christos { 854 1.1 christos switch (behaviour) 855 1.1 christos { 856 1.1 christos case IS_PageOnly: 857 1.1 christos info_error ((char *) msg_at_node_bottom, NULL, NULL); 858 1.1 christos break; 859 1.1 christos 860 1.1 christos case IS_NextOnly: 861 1.1 christos info_next_label_of_node (window->node); 862 1.1 christos if (!info_parsed_nodename && !info_parsed_filename) 863 1.1 christos info_error ((char *) msg_no_pointer, (char *) _("Next"), NULL); 864 1.1 christos else 865 1.1 christos { 866 1.1 christos window_message_in_echo_area ((char *) _("Following Next node..."), 867 1.1 christos NULL, NULL); 868 1.1 christos info_handle_pointer ("Next", window); 869 1.1 christos } 870 1.1 christos break; 871 1.1 christos 872 1.1 christos case IS_Continuous: 873 1.1 christos { 874 1.1 christos /* First things first. If this node contains a menu, move down 875 1.1 christos into the menu. */ 876 1.1 christos { 877 1.1 christos REFERENCE **menu; 878 1.1 christos 879 1.1 christos menu = info_menu_of_node (window->node); 880 1.1 christos 881 1.1 christos if (menu) 882 1.1 christos { 883 1.1 christos info_free_references (menu); 884 1.1 christos window_message_in_echo_area ((char *) _("Selecting first menu item..."), 885 1.1 christos NULL, NULL); 886 1.1 christos info_menu_digit (window, 1, '1'); 887 1.1 christos return; 888 1.1 christos } 889 1.1 christos } 890 1.1 christos 891 1.1 christos /* Okay, this node does not contain a menu. If it contains a 892 1.1 christos "Next:" pointer, use that. */ 893 1.1 christos info_next_label_of_node (window->node); 894 1.1 christos if (INFO_LABEL_FOUND ()) 895 1.1 christos { 896 1.1 christos window_message_in_echo_area ((char *) _("Selecting Next node..."), 897 1.1 christos NULL, NULL); 898 1.1 christos info_handle_pointer ("Next", window); 899 1.1 christos return; 900 1.1 christos } 901 1.1 christos 902 1.1 christos /* Okay, there wasn't a "Next:" for this node. Move "Up:" until we 903 1.1 christos can move "Next:". If that isn't possible, complain that there 904 1.1 christos are no more nodes. */ 905 1.1 christos { 906 1.1 christos int up_counter, old_current; 907 1.1 christos INFO_WINDOW *info_win; 908 1.1 christos 909 1.1 christos /* Remember the current node and location. */ 910 1.1 christos info_win = get_info_window_of_window (window); 911 1.1 christos old_current = info_win->current; 912 1.1 christos 913 1.1 christos /* Back up through the "Up:" pointers until we have found a "Next:" 914 1.1 christos that isn't the same as the first menu item found in that node. */ 915 1.1 christos up_counter = 0; 916 1.1 christos while (!info_error_was_printed) 917 1.1 christos { 918 1.1 christos info_up_label_of_node (window->node); 919 1.1 christos if (INFO_LABEL_FOUND ()) 920 1.1 christos { 921 1.1 christos info_handle_pointer ("Up", window); 922 1.1 christos if (info_error_was_printed) 923 1.1 christos continue; 924 1.1 christos 925 1.1 christos up_counter++; 926 1.1 christos 927 1.1 christos info_next_label_of_node (window->node); 928 1.1 christos 929 1.1 christos /* If no "Next" pointer, keep backing up. */ 930 1.1 christos if (!INFO_LABEL_FOUND ()) 931 1.1 christos continue; 932 1.1 christos 933 1.1 christos /* If this node's first menu item is the same as this node's 934 1.1 christos Next pointer, keep backing up. */ 935 1.1 christos if (!info_parsed_filename) 936 1.1 christos { 937 1.1 christos REFERENCE **menu; 938 1.1 christos char *next_nodename; 939 1.1 christos 940 1.1 christos /* Remember the name of the Next node, since reading 941 1.1 christos the menu can overwrite the contents of the 942 1.1 christos info_parsed_xxx strings. */ 943 1.1 christos next_nodename = xstrdup (info_parsed_nodename); 944 1.1 christos 945 1.1 christos menu = info_menu_of_node (window->node); 946 1.1 christos if (menu && 947 1.1 christos (strcmp 948 1.1 christos (menu[0]->nodename, next_nodename) == 0)) 949 1.1 christos { 950 1.1 christos info_free_references (menu); 951 1.1 christos free (next_nodename); 952 1.1 christos continue; 953 1.1 christos } 954 1.1 christos else 955 1.1 christos { 956 1.1 christos /* Restore the world to where it was before 957 1.1 christos reading the menu contents. */ 958 1.1 christos info_free_references (menu); 959 1.1 christos free (next_nodename); 960 1.1 christos info_next_label_of_node (window->node); 961 1.1 christos } 962 1.1 christos } 963 1.1 christos 964 1.1 christos /* This node has a "Next" pointer, and it is not the 965 1.1 christos same as the first menu item found in this node. */ 966 1.1 christos window_message_in_echo_area 967 1.2 christos ((char *) _("Moving Up %d time(s), then Next."), 968 1.1 christos (void *)((intptr_t)up_counter), NULL); 969 1.1 christos 970 1.1 christos info_handle_pointer ("Next", window); 971 1.1 christos return; 972 1.1 christos } 973 1.1 christos else 974 1.1 christos { 975 1.1 christos /* No more "Up" pointers. Print an error, and call it 976 1.1 christos quits. */ 977 1.1 christos register int i; 978 1.1 christos 979 1.1 christos for (i = 0; i < up_counter; i++) 980 1.1 christos { 981 1.1 christos info_win->nodes_index--; 982 1.1 christos free (info_win->nodes[info_win->nodes_index]); 983 1.1 christos info_win->nodes[info_win->nodes_index] = (NODE *)NULL; 984 1.1 christos } 985 1.1 christos info_win->current = old_current; 986 1.1 christos window->node = info_win->nodes[old_current]; 987 1.1 christos window->pagetop = info_win->pagetops[old_current]; 988 1.1 christos window->point = info_win->points[old_current]; 989 1.1 christos recalculate_line_starts (window); 990 1.1 christos window->flags |= W_UpdateWindow; 991 1.1 christos info_error ((char *) _("No more nodes within this document."), 992 1.1 christos NULL, NULL); 993 1.1 christos } 994 1.1 christos } 995 1.1 christos } 996 1.1 christos break; 997 1.1 christos } 998 1.1 christos } 999 1.1 christos } 1000 1.1 christos 1001 1.1 christos /* Move Prev, Up or error in WINDOW depending on BEHAVIOUR. */ 1002 1.1 christos static void 1003 1.1 christos backward_move_node_structure (WINDOW *window, int behaviour) 1004 1.1 christos { 1005 1.1 christos switch (behaviour) 1006 1.1 christos { 1007 1.1 christos case IS_PageOnly: 1008 1.1 christos info_error ((char *) msg_at_node_top, NULL, NULL); 1009 1.1 christos break; 1010 1.1 christos 1011 1.1 christos case IS_NextOnly: 1012 1.1 christos info_prev_label_of_node (window->node); 1013 1.1 christos if (!info_parsed_nodename && !info_parsed_filename) 1014 1.1 christos info_error ((char *) _("No `Prev' for this node."), NULL, NULL); 1015 1.1 christos else 1016 1.1 christos { 1017 1.1 christos window_message_in_echo_area ((char *) _("Moving Prev in this window."), 1018 1.1 christos NULL, NULL); 1019 1.1 christos info_handle_pointer ("Prev", window); 1020 1.1 christos } 1021 1.1 christos break; 1022 1.1 christos 1023 1.1 christos case IS_Continuous: 1024 1.1 christos info_prev_label_of_node (window->node); 1025 1.1 christos 1026 1.1 christos if (!info_parsed_nodename && (!info_parsed_filename 1027 1.1 christos || is_dir_name (info_parsed_filename))) 1028 1.1 christos { 1029 1.1 christos info_up_label_of_node (window->node); 1030 1.1 christos if (!info_parsed_nodename && (!info_parsed_filename 1031 1.1 christos || is_dir_name (info_parsed_filename))) 1032 1.1 christos info_error ((char *) 1033 1.1 christos _("No `Prev' or `Up' for this node within this document."), 1034 1.1 christos NULL, NULL); 1035 1.1 christos else 1036 1.1 christos { 1037 1.1 christos window_message_in_echo_area ((char *) _("Moving Up in this window."), 1038 1.1 christos NULL, NULL); 1039 1.1 christos info_handle_pointer ("Up", window); 1040 1.1 christos } 1041 1.1 christos } 1042 1.1 christos else 1043 1.1 christos { 1044 1.1 christos REFERENCE **menu; 1045 1.1 christos int inhibit_menu_traversing = 0; 1046 1.1 christos 1047 1.1 christos /* Watch out! If this node's Prev is the same as the Up, then 1048 1.1 christos move Up. Otherwise, we could move Prev, and then to the last 1049 1.1 christos menu item in the Prev. This would cause the user to loop 1050 1.1 christos through a subsection of the info file. */ 1051 1.1 christos if (!info_parsed_filename && info_parsed_nodename) 1052 1.1 christos { 1053 1.1 christos char *pnode; 1054 1.1 christos 1055 1.1 christos pnode = xstrdup (info_parsed_nodename); 1056 1.1 christos info_up_label_of_node (window->node); 1057 1.1 christos 1058 1.1 christos if (!info_parsed_filename && info_parsed_nodename && 1059 1.1 christos strcmp (info_parsed_nodename, pnode) == 0) 1060 1.1 christos { 1061 1.1 christos /* The nodes are the same. Inhibit moving to the last 1062 1.1 christos menu item. */ 1063 1.1 christos free (pnode); 1064 1.1 christos inhibit_menu_traversing = 1; 1065 1.1 christos } 1066 1.1 christos else 1067 1.1 christos { 1068 1.1 christos free (pnode); 1069 1.1 christos info_prev_label_of_node (window->node); 1070 1.1 christos } 1071 1.1 christos } 1072 1.1 christos 1073 1.1 christos /* Move to the previous node. If this node now contains a menu, 1074 1.1 christos and we have not inhibited movement to it, move to the node 1075 1.1 christos corresponding to the last menu item. */ 1076 1.1 christos window_message_in_echo_area ((char *) _("Moving Prev in this window."), 1077 1.1 christos NULL, NULL); 1078 1.1 christos info_handle_pointer ("Prev", window); 1079 1.1 christos 1080 1.1 christos if (!inhibit_menu_traversing) 1081 1.1 christos { 1082 1.1 christos while (!info_error_was_printed && 1083 1.1 christos (menu = info_menu_of_node (window->node))) 1084 1.1 christos { 1085 1.1 christos info_free_references (menu); 1086 1.1 christos window_message_in_echo_area 1087 1.1 christos ((char *) _("Moving to `Prev's last menu item."), NULL, NULL); 1088 1.1 christos info_menu_digit (window, 1, '0'); 1089 1.1 christos } 1090 1.1 christos } 1091 1.1 christos } 1092 1.1 christos break; 1093 1.1 christos } 1094 1.1 christos } 1095 1.1 christos 1096 1.1 christos /* Move continuously forward through the node structure of this info file. */ 1097 1.1 christos DECLARE_INFO_COMMAND (info_global_next_node, 1098 1.1 christos _("Move forwards or down through node structure")) 1099 1.1 christos { 1100 1.1 christos if (count < 0) 1101 1.1 christos info_global_prev_node (window, -count, key); 1102 1.1 christos else 1103 1.1 christos { 1104 1.1 christos while (count && !info_error_was_printed) 1105 1.1 christos { 1106 1.1 christos forward_move_node_structure (window, IS_Continuous); 1107 1.1 christos count--; 1108 1.1 christos } 1109 1.1 christos } 1110 1.1 christos } 1111 1.1 christos 1112 1.1 christos /* Move continuously backward through the node structure of this info file. */ 1113 1.1 christos DECLARE_INFO_COMMAND (info_global_prev_node, 1114 1.1 christos _("Move backwards or up through node structure")) 1115 1.1 christos { 1116 1.1 christos if (count < 0) 1117 1.1 christos info_global_next_node (window, -count, key); 1118 1.1 christos else 1119 1.1 christos { 1120 1.1 christos while (count && !info_error_was_printed) 1121 1.1 christos { 1122 1.1 christos backward_move_node_structure (window, IS_Continuous); 1123 1.1 christos count--; 1124 1.1 christos } 1125 1.1 christos } 1126 1.1 christos } 1127 1.1 christos 1128 1.1 christos static void _scroll_forward(WINDOW *window, int count, 1129 1.1 christos unsigned char key, int behaviour); 1130 1.1 christos static void _scroll_backward(WINDOW *window, int count, 1131 1.1 christos unsigned char key, int behaviour); 1132 1.1 christos 1133 1.1 christos static void 1134 1.1 christos _scroll_forward(WINDOW *window, int count, unsigned char key, int behaviour) 1135 1.1 christos { 1136 1.1 christos if (count < 0) 1137 1.1 christos _scroll_backward (window, -count, key, behaviour); 1138 1.1 christos else 1139 1.1 christos { 1140 1.1 christos int desired_top; 1141 1.1 christos 1142 1.1 christos /* Without an explicit numeric argument, scroll the bottom two 1143 1.1 christos lines to the top of this window, Or, if at bottom of window, 1144 1.1 christos and the chosen behaviour is to scroll through nodes get the 1145 1.1 christos "Next" node for this window. */ 1146 1.1 christos if (default_window_size > 0) 1147 1.1 christos desired_top = window->pagetop + default_window_size; 1148 1.1 christos else if (!info_explicit_arg && count == 1) 1149 1.1 christos { 1150 1.1 christos desired_top = window->pagetop + (window->height - 2); 1151 1.1 christos 1152 1.1 christos /* If there are no more lines to scroll here, error, or get 1153 1.1 christos another node, depending on BEHAVIOUR. */ 1154 1.1 christos if (desired_top > window->line_count) 1155 1.1 christos { 1156 1.1 christos forward_move_node_structure (window, behaviour); 1157 1.1 christos return; 1158 1.1 christos } 1159 1.1 christos } 1160 1.1 christos else 1161 1.1 christos desired_top = window->pagetop + count; 1162 1.1 christos 1163 1.1 christos if (desired_top >= window->line_count) 1164 1.1 christos desired_top = window->line_count - 2; 1165 1.1 christos 1166 1.1 christos if (window->pagetop > desired_top) 1167 1.1 christos return; 1168 1.1 christos else 1169 1.1 christos set_window_pagetop (window, desired_top); 1170 1.1 christos } 1171 1.1 christos } 1172 1.1 christos 1173 1.1 christos static void 1174 1.1 christos _scroll_backward(WINDOW *window, int count, unsigned char key, int behaviour) 1175 1.1 christos { 1176 1.1 christos if (count < 0) 1177 1.1 christos _scroll_forward (window, -count, key, behaviour); 1178 1.1 christos else 1179 1.1 christos { 1180 1.1 christos int desired_top; 1181 1.1 christos 1182 1.1 christos /* Without an explicit numeric argument, scroll the top two lines 1183 1.1 christos to the bottom of this window, or, depending on the selected 1184 1.1 christos behaviour, move to the previous, or Up'th node. */ 1185 1.1 christos if (default_window_size > 0) 1186 1.1 christos desired_top = window->pagetop - default_window_size; 1187 1.1 christos else if (!info_explicit_arg && count == 1) 1188 1.1 christos { 1189 1.1 christos desired_top = window->pagetop - (window->height - 2); 1190 1.1 christos 1191 1.1 christos if ((desired_top < 0) && (window->pagetop == 0)) 1192 1.1 christos { 1193 1.1 christos backward_move_node_structure (window, behaviour); 1194 1.1 christos return; 1195 1.1 christos } 1196 1.1 christos } 1197 1.1 christos else 1198 1.1 christos desired_top = window->pagetop - count; 1199 1.1 christos 1200 1.1 christos if (desired_top < 0) 1201 1.1 christos desired_top = 0; 1202 1.1 christos 1203 1.1 christos set_window_pagetop (window, desired_top); 1204 1.1 christos } 1205 1.1 christos } 1206 1.1 christos 1207 1.1 christos /* Show the next screen of WINDOW's node. */ 1208 1.1 christos DECLARE_INFO_COMMAND (info_scroll_forward, _("Scroll forward in this window")) 1209 1.1 christos { 1210 1.1 christos _scroll_forward (window, count, key, info_scroll_behaviour); 1211 1.1 christos } 1212 1.1 christos 1213 1.1 christos /* Like info_scroll_forward, but sets default_window_size as a side 1214 1.1 christos effect. */ 1215 1.1 christos DECLARE_INFO_COMMAND (info_scroll_forward_set_window, 1216 1.1 christos _("Scroll forward in this window and set default window size")) 1217 1.1 christos { 1218 1.1 christos if (info_explicit_arg) 1219 1.1 christos default_window_size = count; 1220 1.1 christos _scroll_forward (window, count, key, info_scroll_behaviour); 1221 1.1 christos } 1222 1.1 christos 1223 1.1 christos /* Show the next screen of WINDOW's node but never advance to next node. */ 1224 1.1 christos DECLARE_INFO_COMMAND (info_scroll_forward_page_only, _("Scroll forward in this window staying within node")) 1225 1.1 christos { 1226 1.1 christos _scroll_forward (window, count, key, IS_PageOnly); 1227 1.1 christos } 1228 1.1 christos 1229 1.1 christos /* Like info_scroll_forward_page_only, but sets default_window_size as a side 1230 1.1 christos effect. */ 1231 1.1 christos DECLARE_INFO_COMMAND (info_scroll_forward_page_only_set_window, 1232 1.1 christos _("Scroll forward in this window staying within node and set default window size")) 1233 1.1 christos { 1234 1.1 christos if (info_explicit_arg) 1235 1.1 christos default_window_size = count; 1236 1.1 christos _scroll_forward (window, count, key, IS_PageOnly); 1237 1.1 christos } 1238 1.1 christos 1239 1.1 christos /* Show the previous screen of WINDOW's node. */ 1240 1.1 christos DECLARE_INFO_COMMAND (info_scroll_backward, _("Scroll backward in this window")) 1241 1.1 christos { 1242 1.1 christos _scroll_backward (window, count, key, info_scroll_behaviour); 1243 1.1 christos } 1244 1.1 christos 1245 1.1 christos /* Like info_scroll_backward, but sets default_window_size as a side 1246 1.1 christos effect. */ 1247 1.1 christos DECLARE_INFO_COMMAND (info_scroll_backward_set_window, 1248 1.1 christos _("Scroll backward in this window and set default window size")) 1249 1.1 christos { 1250 1.1 christos if (info_explicit_arg) 1251 1.1 christos default_window_size = count; 1252 1.1 christos _scroll_backward (window, count, key, info_scroll_behaviour); 1253 1.1 christos } 1254 1.1 christos 1255 1.1 christos /* Show the previous screen of WINDOW's node but never move to previous 1256 1.1 christos node. */ 1257 1.1 christos DECLARE_INFO_COMMAND (info_scroll_backward_page_only, _("Scroll backward in this window staying within node")) 1258 1.1 christos { 1259 1.1 christos _scroll_backward (window, count, key, IS_PageOnly); 1260 1.1 christos } 1261 1.1 christos 1262 1.1 christos /* Like info_scroll_backward_page_only, but sets default_window_size as a side 1263 1.1 christos effect. */ 1264 1.1 christos DECLARE_INFO_COMMAND (info_scroll_backward_page_only_set_window, 1265 1.1 christos _("Scroll backward in this window staying within node and set default window size")) 1266 1.1 christos { 1267 1.1 christos if (info_explicit_arg) 1268 1.1 christos default_window_size = count; 1269 1.1 christos _scroll_backward (window, count, key, IS_PageOnly); 1270 1.1 christos } 1271 1.1 christos 1272 1.1 christos /* Move to the beginning of the node. */ 1273 1.1 christos DECLARE_INFO_COMMAND (info_beginning_of_node, _("Move to the start of this node")) 1274 1.1 christos { 1275 1.1 christos window->pagetop = window->point = 0; 1276 1.1 christos window->flags |= W_UpdateWindow; 1277 1.1 christos } 1278 1.1 christos 1279 1.1 christos /* Move to the end of the node. */ 1280 1.1 christos DECLARE_INFO_COMMAND (info_end_of_node, _("Move to the end of this node")) 1281 1.1 christos { 1282 1.1 christos window->point = window->node->nodelen - 1; 1283 1.1 christos info_show_point (window); 1284 1.1 christos } 1285 1.1 christos 1286 1.1 christos /* Scroll the window forward by N lines. */ 1287 1.1 christos DECLARE_INFO_COMMAND (info_down_line, _("Scroll down by lines")) 1288 1.1 christos { 1289 1.1 christos if (count < 0) 1290 1.1 christos info_up_line (window, -count, key); 1291 1.1 christos else 1292 1.1 christos { 1293 1.1 christos int desired_top = window->pagetop + count; 1294 1.1 christos 1295 1.1 christos if (desired_top >= window->line_count) 1296 1.1 christos desired_top = window->line_count - 2; 1297 1.1 christos 1298 1.1 christos if (window->pagetop <= desired_top) 1299 1.1 christos set_window_pagetop (window, desired_top); 1300 1.1 christos } 1301 1.1 christos } 1302 1.1 christos 1303 1.1 christos /* Scroll the window backward by N lines. */ 1304 1.1 christos DECLARE_INFO_COMMAND (info_up_line, _("Scroll up by lines")) 1305 1.1 christos { 1306 1.1 christos if (count < 0) 1307 1.1 christos info_down_line (window, -count, key); 1308 1.1 christos else 1309 1.1 christos { 1310 1.1 christos int desired_top = window->pagetop - count; 1311 1.1 christos 1312 1.1 christos if (desired_top < 0) 1313 1.1 christos desired_top = 0; 1314 1.1 christos 1315 1.1 christos set_window_pagetop (window, desired_top); 1316 1.1 christos } 1317 1.1 christos } 1318 1.1 christos 1319 1.1 christos /* Scroll the window forward by N lines and remember N as default for 1320 1.1 christos subsequent commands. */ 1321 1.1 christos DECLARE_INFO_COMMAND (info_scroll_half_screen_down, 1322 1.1 christos _("Scroll down by half screen size")) 1323 1.1 christos { 1324 1.1 christos if (count < 0) 1325 1.1 christos info_scroll_half_screen_up (window, -count, key); 1326 1.1 christos else 1327 1.1 christos { 1328 1.1 christos int scroll_size = (the_screen->height + 1) / 2; 1329 1.1 christos int desired_top; 1330 1.1 christos 1331 1.1 christos if (info_explicit_arg) 1332 1.1 christos default_scroll_size = count; 1333 1.1 christos if (default_scroll_size > 0) 1334 1.1 christos scroll_size = default_scroll_size; 1335 1.1 christos 1336 1.1 christos desired_top = window->pagetop + scroll_size; 1337 1.1 christos if (desired_top >= window->line_count) 1338 1.1 christos desired_top = window->line_count - 2; 1339 1.1 christos 1340 1.1 christos if (window->pagetop <= desired_top) 1341 1.1 christos set_window_pagetop (window, desired_top); 1342 1.1 christos } 1343 1.1 christos } 1344 1.1 christos 1345 1.1 christos /* Scroll the window backward by N lines and remember N as default for 1346 1.1 christos subsequent commands. */ 1347 1.1 christos DECLARE_INFO_COMMAND (info_scroll_half_screen_up, 1348 1.1 christos _("Scroll up by half screen size")) 1349 1.1 christos { 1350 1.1 christos if (count < 0) 1351 1.1 christos info_scroll_half_screen_down (window, -count, key); 1352 1.1 christos else 1353 1.1 christos { 1354 1.1 christos int scroll_size = (the_screen->height + 1) / 2; 1355 1.1 christos int desired_top; 1356 1.1 christos 1357 1.1 christos if (info_explicit_arg) 1358 1.1 christos default_scroll_size = count; 1359 1.1 christos if (default_scroll_size > 0) 1360 1.1 christos scroll_size = default_scroll_size; 1361 1.1 christos 1362 1.1 christos desired_top = window->pagetop - scroll_size; 1363 1.1 christos if (desired_top < 0) 1364 1.1 christos desired_top = 0; 1365 1.1 christos 1366 1.1 christos set_window_pagetop (window, desired_top); 1367 1.1 christos } 1368 1.1 christos } 1369 1.1 christos 1370 1.1 christos /* **************************************************************** */ 1372 1.1 christos /* */ 1373 1.1 christos /* Commands for Manipulating Windows */ 1374 1.1 christos /* */ 1375 1.1 christos /* **************************************************************** */ 1376 1.1 christos 1377 1.1 christos /* Make the next window in the chain be the active window. */ 1378 1.1 christos DECLARE_INFO_COMMAND (info_next_window, _("Select the next window")) 1379 1.1 christos { 1380 1.1 christos if (count < 0) 1381 1.1 christos { 1382 1.1 christos info_prev_window (window, -count, key); 1383 1.1 christos return; 1384 1.1 christos } 1385 1.1 christos 1386 1.1 christos /* If no other window, error now. */ 1387 1.1 christos if (!windows->next && !echo_area_is_active) 1388 1.1 christos { 1389 1.1 christos info_error ((char *) msg_one_window, NULL, NULL); 1390 1.1 christos return; 1391 1.1 christos } 1392 1.1 christos 1393 1.1 christos while (count--) 1394 1.1 christos { 1395 1.1 christos if (window->next) 1396 1.1 christos window = window->next; 1397 1.1 christos else 1398 1.1 christos { 1399 1.1 christos if (window == the_echo_area || !echo_area_is_active) 1400 1.1 christos window = windows; 1401 1.1 christos else 1402 1.1 christos window = the_echo_area; 1403 1.1 christos } 1404 1.1 christos } 1405 1.1 christos 1406 1.1 christos if (active_window != window) 1407 1.1 christos { 1408 1.1 christos if (auto_footnotes_p) 1409 1.1 christos info_get_or_remove_footnotes (window); 1410 1.1 christos 1411 1.1 christos window->flags |= W_UpdateWindow; 1412 1.1 christos active_window = window; 1413 1.1 christos } 1414 1.1 christos } 1415 1.1 christos 1416 1.1 christos /* Make the previous window in the chain be the active window. */ 1417 1.1 christos DECLARE_INFO_COMMAND (info_prev_window, _("Select the previous window")) 1418 1.1 christos { 1419 1.1 christos if (count < 0) 1420 1.1 christos { 1421 1.1 christos info_next_window (window, -count, key); 1422 1.1 christos return; 1423 1.1 christos } 1424 1.1 christos 1425 1.1 christos /* Only one window? */ 1426 1.1 christos 1427 1.1 christos if (!windows->next && !echo_area_is_active) 1428 1.1 christos { 1429 1.1 christos info_error ((char *) msg_one_window, NULL, NULL); 1430 1.1 christos return; 1431 1.1 christos } 1432 1.1 christos 1433 1.1 christos while (count--) 1434 1.1 christos { 1435 1.1 christos /* If we are in the echo area, or if the echo area isn't active and we 1436 1.1 christos are in the first window, find the last window in the chain. */ 1437 1.1 christos if (window == the_echo_area || 1438 1.1 christos (window == windows && !echo_area_is_active)) 1439 1.1 christos { 1440 1.1 christos register WINDOW *win, *last = NULL; 1441 1.1 christos 1442 1.1 christos for (win = windows; win; win = win->next) 1443 1.1 christos last = win; 1444 1.1 christos 1445 1.1 christos window = last; 1446 1.1 christos } 1447 1.1 christos else 1448 1.1 christos { 1449 1.1 christos if (window == windows) 1450 1.1 christos window = the_echo_area; 1451 1.1 christos else 1452 1.1 christos window = window->prev; 1453 1.1 christos } 1454 1.1 christos } 1455 1.1 christos 1456 1.1 christos if (active_window != window) 1457 1.1 christos { 1458 1.1 christos if (auto_footnotes_p) 1459 1.1 christos info_get_or_remove_footnotes (window); 1460 1.1 christos 1461 1.1 christos window->flags |= W_UpdateWindow; 1462 1.1 christos active_window = window; 1463 1.1 christos } 1464 1.1 christos } 1465 1.1 christos 1466 1.1 christos /* Split WINDOW into two windows, both showing the same node. If we 1467 1.1 christos are automatically tiling windows, re-tile after the split. */ 1468 1.1 christos DECLARE_INFO_COMMAND (info_split_window, _("Split the current window")) 1469 1.1 christos { 1470 1.1 christos WINDOW *split, *old_active; 1471 1.1 christos int pagetop; 1472 1.1 christos 1473 1.1 christos /* Remember the current pagetop of the window being split. If it doesn't 1474 1.1 christos change, we can scroll its contents around after the split. */ 1475 1.1 christos pagetop = window->pagetop; 1476 1.1 christos 1477 1.1 christos /* Make the new window. */ 1478 1.1 christos old_active = active_window; 1479 1.1 christos active_window = window; 1480 1.1 christos split = window_make_window (window->node); 1481 1.1 christos active_window = old_active; 1482 1.1 christos 1483 1.1 christos if (!split) 1484 1.1 christos { 1485 1.1 christos info_error ((char *) msg_win_too_small, NULL, NULL); 1486 1.1 christos } 1487 1.1 christos else 1488 1.1 christos { 1489 1.1 christos #if defined (SPLIT_BEFORE_ACTIVE) 1490 1.1 christos /* Try to scroll the old window into its new postion. */ 1491 1.1 christos if (pagetop == window->pagetop) 1492 1.1 christos { 1493 1.1 christos int start, end, amount; 1494 1.1 christos 1495 1.1 christos start = split->first_row; 1496 1.1 christos end = start + window->height; 1497 1.1 christos amount = split->height + 1; 1498 1.1 christos display_scroll_display (start, end, amount); 1499 1.1 christos } 1500 1.1 christos #else /* !SPLIT_BEFORE_ACTIVE */ 1501 1.1 christos /* Make sure point still appears in the active window. */ 1502 1.1 christos info_show_point (window); 1503 1.1 christos #endif /* !SPLIT_BEFORE_ACTIVE */ 1504 1.1 christos 1505 1.1 christos /* If the window just split was one internal to Info, try to display 1506 1.1 christos something else in it. */ 1507 1.1 christos if (internal_info_node_p (split->node)) 1508 1.1 christos { 1509 1.1 christos register int i, j; 1510 1.1 christos INFO_WINDOW *iw; 1511 1.1 christos NODE *node = (NODE *)NULL; 1512 1.1 christos char *filename; 1513 1.1 christos 1514 1.1 christos for (i = 0; (iw = info_windows[i]); i++) 1515 1.1 christos { 1516 1.1 christos for (j = 0; j < iw->nodes_index; j++) 1517 1.1 christos if (!internal_info_node_p (iw->nodes[j])) 1518 1.1 christos { 1519 1.1 christos if (iw->nodes[j]->parent) 1520 1.1 christos filename = iw->nodes[j]->parent; 1521 1.1 christos else 1522 1.1 christos filename = iw->nodes[j]->filename; 1523 1.1 christos 1524 1.1 christos node = info_get_node (filename, iw->nodes[j]->nodename); 1525 1.1 christos if (node) 1526 1.1 christos { 1527 1.1 christos window_set_node_of_window (split, node); 1528 1.1 christos i = info_windows_index - 1; 1529 1.1 christos break; 1530 1.1 christos } 1531 1.1 christos } 1532 1.1 christos } 1533 1.1 christos } 1534 1.1 christos split->pagetop = window->pagetop; 1535 1.1 christos 1536 1.1 christos if (auto_tiling_p) 1537 1.1 christos window_tile_windows (DONT_TILE_INTERNALS); 1538 1.1 christos else 1539 1.1 christos window_adjust_pagetop (split); 1540 1.1 christos 1541 1.1 christos remember_window_and_node (split, split->node); 1542 1.1 christos } 1543 1.1 christos } 1544 1.1 christos 1545 1.1 christos /* Delete WINDOW, forgetting the list of last visited nodes. If we are 1546 1.1 christos automatically displaying footnotes, show or remove the footnotes 1547 1.1 christos window. If we are automatically tiling windows, re-tile after the 1548 1.1 christos deletion. */ 1549 1.1 christos DECLARE_INFO_COMMAND (info_delete_window, _("Delete the current window")) 1550 1.1 christos { 1551 1.1 christos if (!windows->next) 1552 1.1 christos { 1553 1.1 christos info_error ((char *) msg_cant_kill_last, NULL, NULL); 1554 1.1 christos } 1555 1.1 christos else if (window->flags & W_WindowIsPerm) 1556 1.1 christos { 1557 1.1 christos info_error ((char *) _("Cannot delete a permanent window"), NULL, NULL); 1558 1.1 christos } 1559 1.1 christos else 1560 1.1 christos { 1561 1.1 christos info_delete_window_internal (window); 1562 1.1 christos 1563 1.1 christos if (auto_footnotes_p) 1564 1.1 christos info_get_or_remove_footnotes (active_window); 1565 1.1 christos 1566 1.1 christos if (auto_tiling_p) 1567 1.1 christos window_tile_windows (DONT_TILE_INTERNALS); 1568 1.1 christos } 1569 1.1 christos } 1570 1.1 christos 1571 1.1 christos /* Do the physical deletion of WINDOW, and forget this window and 1572 1.1 christos associated nodes. */ 1573 1.1 christos void 1574 1.1 christos info_delete_window_internal (WINDOW *window) 1575 1.1 christos { 1576 1.1 christos if (windows->next && ((window->flags & W_WindowIsPerm) == 0)) 1577 1.1 christos { 1578 1.1 christos /* We not only delete the window from the display, we forget it from 1579 1.1 christos our list of remembered windows. */ 1580 1.1 christos forget_window_and_nodes (window); 1581 1.1 christos window_delete_window (window); 1582 1.1 christos 1583 1.1 christos if (echo_area_is_active) 1584 1.1 christos echo_area_inform_of_deleted_window (window); 1585 1.1 christos } 1586 1.1 christos } 1587 1.1 christos 1588 1.1 christos /* Just keep WINDOW, deleting all others. */ 1589 1.1 christos DECLARE_INFO_COMMAND (info_keep_one_window, _("Delete all other windows")) 1590 1.1 christos { 1591 1.1 christos int num_deleted; /* The number of windows we deleted. */ 1592 1.1 christos int pagetop, start, end; 1593 1.1 christos 1594 1.1 christos /* Remember a few things about this window. We may be able to speed up 1595 1.1 christos redisplay later by scrolling its contents. */ 1596 1.1 christos pagetop = window->pagetop; 1597 1.1 christos start = window->first_row; 1598 1.1 christos end = start + window->height; 1599 1.1 christos 1600 1.1 christos num_deleted = 0; 1601 1.1 christos 1602 1.1 christos while (1) 1603 1.1 christos { 1604 1.1 christos WINDOW *win; 1605 1.1 christos 1606 1.1 christos /* Find an eligible window and delete it. If no eligible windows 1607 1.1 christos are found, we are done. A window is eligible for deletion if 1608 1.1 christos is it not permanent, and it is not WINDOW. */ 1609 1.1 christos for (win = windows; win; win = win->next) 1610 1.1 christos if (win != window && ((win->flags & W_WindowIsPerm) == 0)) 1611 1.1 christos break; 1612 1.1 christos 1613 1.1 christos if (!win) 1614 1.1 christos break; 1615 1.1 christos 1616 1.1 christos info_delete_window_internal (win); 1617 1.1 christos num_deleted++; 1618 1.1 christos } 1619 1.1 christos 1620 1.1 christos /* Scroll the contents of this window into the right place so that the 1621 1.1 christos user doesn't have to wait any longer than necessary for redisplay. */ 1622 1.1 christos if (num_deleted) 1623 1.1 christos { 1624 1.1 christos int amount; 1625 1.1 christos 1626 1.1 christos amount = (window->first_row - start); 1627 1.1 christos amount -= (window->pagetop - pagetop); 1628 1.1 christos display_scroll_display (start, end, amount); 1629 1.1 christos } 1630 1.1 christos 1631 1.1 christos window->flags |= W_UpdateWindow; 1632 1.1 christos } 1633 1.1 christos 1634 1.1 christos /* Scroll the "other" window of WINDOW. */ 1635 1.1 christos DECLARE_INFO_COMMAND (info_scroll_other_window, _("Scroll the other window")) 1636 1.1 christos { 1637 1.1 christos WINDOW *other; 1638 1.1 christos 1639 1.1 christos /* If only one window, give up. */ 1640 1.1 christos if (!windows->next) 1641 1.1 christos { 1642 1.1 christos info_error ((char *) msg_one_window, NULL, NULL); 1643 1.1 christos return; 1644 1.1 christos } 1645 1.1 christos 1646 1.1 christos other = window->next; 1647 1.1 christos 1648 1.1 christos if (!other) 1649 1.1 christos other = window->prev; 1650 1.1 christos 1651 1.1 christos info_scroll_forward (other, count, key); 1652 1.1 christos } 1653 1.1 christos 1654 1.1 christos /* Scroll the "other" window of WINDOW. */ 1655 1.1 christos DECLARE_INFO_COMMAND (info_scroll_other_window_backward, 1656 1.1 christos _("Scroll the other window backward")) 1657 1.1 christos { 1658 1.1 christos info_scroll_other_window (window, -count, key); 1659 1.1 christos } 1660 1.1 christos 1661 1.1 christos /* Change the size of WINDOW by AMOUNT. */ 1662 1.1 christos DECLARE_INFO_COMMAND (info_grow_window, _("Grow (or shrink) this window")) 1663 1.1 christos { 1664 1.1 christos window_change_window_height (window, count); 1665 1.1 christos } 1666 1.1 christos 1667 1.1 christos /* When non-zero, tiling takes place automatically when info_split_window 1668 1.1 christos is called. */ 1669 1.1 christos int auto_tiling_p = 0; 1670 1.1 christos 1671 1.1 christos /* Tile all of the visible windows. */ 1672 1.1 christos DECLARE_INFO_COMMAND (info_tile_windows, 1673 1.1 christos _("Divide the available screen space among the visible windows")) 1674 1.1 christos { 1675 1.1 christos window_tile_windows (TILE_INTERNALS); 1676 1.1 christos } 1677 1.1 christos 1678 1.1 christos /* Toggle the state of this window's wrapping of lines. */ 1679 1.1 christos DECLARE_INFO_COMMAND (info_toggle_wrap, 1680 1.1 christos _("Toggle the state of line wrapping in the current window")) 1681 1.1 christos { 1682 1.1 christos window_toggle_wrap (window); 1683 1.1 christos } 1684 1.1 christos 1685 1.1 christos /* **************************************************************** */ 1687 1.1 christos /* */ 1688 1.1 christos /* Info Node Commands */ 1689 1.1 christos /* */ 1690 1.1 christos /* **************************************************************** */ 1691 1.1 christos 1692 1.1 christos /* Return (FILENAME)NODENAME for NODE, or just NODENAME if NODE's 1693 1.1 christos filename is not set. */ 1694 1.1 christos char * 1695 1.1 christos node_printed_rep (NODE *node) 1696 1.1 christos { 1697 1.1 christos char *rep; 1698 1.1 christos 1699 1.1 christos if (node->filename) 1700 1.1 christos { 1701 1.1 christos char *filename 1702 1.1 christos = filename_non_directory (node->parent ? node->parent : node->filename); 1703 1.1 christos rep = xmalloc (1 + strlen (filename) + 1 + strlen (node->nodename) + 1); 1704 1.1 christos sprintf (rep, "(%s)%s", filename, node->nodename); 1705 1.1 christos } 1706 1.1 christos else 1707 1.1 christos rep = node->nodename; 1708 1.1 christos 1709 1.1 christos return rep; 1710 1.1 christos } 1711 1.1 christos 1712 1.1 christos 1713 1.1 christos /* Using WINDOW for various defaults, select the node referenced by ENTRY 1714 1.1 christos in it. If the node is selected, the window and node are remembered. */ 1715 1.1 christos void 1716 1.1 christos info_select_reference (WINDOW *window, REFERENCE *entry) 1717 1.1 christos { 1718 1.1 christos NODE *node; 1719 1.1 christos char *filename, *nodename, *file_system_error; 1720 1.1 christos 1721 1.1 christos file_system_error = (char *)NULL; 1722 1.1 christos 1723 1.1 christos filename = entry->filename; 1724 1.1 christos if (!filename) 1725 1.1 christos filename = window->node->parent; 1726 1.1 christos if (!filename) 1727 1.1 christos filename = window->node->filename; 1728 1.1 christos 1729 1.1 christos if (filename) 1730 1.1 christos filename = xstrdup (filename); 1731 1.1 christos 1732 1.1 christos if (entry->nodename) 1733 1.1 christos nodename = xstrdup (entry->nodename); 1734 1.1 christos else 1735 1.1 christos nodename = xstrdup ("Top"); 1736 1.1 christos 1737 1.1 christos node = info_get_node (filename, nodename); 1738 1.1 christos 1739 1.1 christos /* Try something a little weird. If the node couldn't be found, and the 1740 1.1 christos reference was of the form "foo::", see if the entry->label can be found 1741 1.1 christos as a file, with a node of "Top". */ 1742 1.1 christos if (!node) 1743 1.1 christos { 1744 1.1 christos if (info_recent_file_error) 1745 1.1 christos file_system_error = xstrdup (info_recent_file_error); 1746 1.1 christos 1747 1.1 christos if (entry->nodename && (strcmp (entry->nodename, entry->label) == 0)) 1748 1.1 christos { 1749 1.1 christos node = info_get_node (entry->label, "Top"); 1750 1.1 christos if (!node && info_recent_file_error) 1751 1.1 christos { 1752 1.1 christos maybe_free (file_system_error); 1753 1.1 christos file_system_error = xstrdup (info_recent_file_error); 1754 1.1 christos } 1755 1.1 christos } 1756 1.1 christos } 1757 1.1 christos 1758 1.1 christos if (!node) 1759 1.1 christos { 1760 1.1 christos if (file_system_error) 1761 1.1 christos info_error (file_system_error, NULL, NULL); 1762 1.1 christos else 1763 1.1 christos info_error ((char *) msg_cant_find_node, nodename, NULL); 1764 1.1 christos } 1765 1.1 christos 1766 1.1 christos maybe_free (file_system_error); 1767 1.1 christos maybe_free (filename); 1768 1.1 christos maybe_free (nodename); 1769 1.1 christos 1770 1.1 christos if (node) 1771 1.1 christos info_set_node_of_window (1, window, node); 1772 1.1 christos } 1773 1.1 christos 1774 1.1 christos /* Parse the node specification in LINE using WINDOW to default the filename. 1775 1.1 christos Select the parsed node in WINDOW and remember it, or error if the node 1776 1.1 christos couldn't be found. */ 1777 1.1 christos static void 1778 1.1 christos info_parse_and_select (char *line, WINDOW *window) 1779 1.1 christos { 1780 1.1 christos REFERENCE entry; 1781 1.1 christos 1782 1.1 christos info_parse_node (line, DONT_SKIP_NEWLINES); 1783 1.1 christos 1784 1.1 christos entry.nodename = info_parsed_nodename; 1785 1.1 christos entry.filename = info_parsed_filename; 1786 1.1 christos entry.label = "*info-parse-and-select*"; 1787 1.1 christos 1788 1.1 christos info_select_reference (window, &entry); 1789 1.1 christos } 1790 1.1 christos 1791 1.1 christos /* Given that the values of INFO_PARSED_FILENAME and INFO_PARSED_NODENAME 1792 1.1 christos are previously filled, try to get the node represented by them into 1793 1.1 christos WINDOW. The node should have been pointed to by the LABEL pointer of 1794 1.1 christos WINDOW->node. */ 1795 1.1 christos static void 1796 1.1 christos info_handle_pointer (char *label, WINDOW *window) 1797 1.1 christos { 1798 1.1 christos if (info_parsed_filename || info_parsed_nodename) 1799 1.1 christos { 1800 1.1 christos char *filename, *nodename; 1801 1.1 christos NODE *node; 1802 1.1 christos 1803 1.1 christos filename = nodename = (char *)NULL; 1804 1.1 christos 1805 1.1 christos if (info_parsed_filename) 1806 1.1 christos filename = xstrdup (info_parsed_filename); 1807 1.1 christos else 1808 1.1 christos { 1809 1.1 christos if (window->node->parent) 1810 1.1 christos filename = xstrdup (window->node->parent); 1811 1.1 christos else if (window->node->filename) 1812 1.1 christos filename = xstrdup (window->node->filename); 1813 1.1 christos } 1814 1.1 christos 1815 1.1 christos if (info_parsed_nodename) 1816 1.1 christos nodename = xstrdup (info_parsed_nodename); 1817 1.1 christos else 1818 1.1 christos nodename = xstrdup ("Top"); 1819 1.1 christos 1820 1.1 christos node = info_get_node (filename, nodename); 1821 1.1 christos 1822 1.1 christos if (node) 1823 1.1 christos { 1824 1.1 christos INFO_WINDOW *info_win; 1825 1.1 christos 1826 1.1 christos info_win = get_info_window_of_window (window); 1827 1.1 christos if (info_win) 1828 1.1 christos { 1829 1.1 christos info_win->pagetops[info_win->current] = window->pagetop; 1830 1.1 christos info_win->points[info_win->current] = window->point; 1831 1.1 christos } 1832 1.1 christos info_set_node_of_window (1, window, node); 1833 1.1 christos } 1834 1.1 christos else 1835 1.1 christos { 1836 1.1 christos if (info_recent_file_error) 1837 1.1 christos info_error (info_recent_file_error, NULL, NULL); 1838 1.1 christos else 1839 1.1 christos info_error ((char *) msg_cant_file_node, filename, nodename); 1840 1.1 christos } 1841 1.1 christos 1842 1.1 christos free (filename); 1843 1.1 christos free (nodename); 1844 1.1 christos } 1845 1.1 christos else 1846 1.1 christos { 1847 1.1 christos info_error ((char *) msg_no_pointer, label, NULL); 1848 1.1 christos } 1849 1.1 christos } 1850 1.1 christos 1851 1.1 christos /* Make WINDOW display the "Next:" node of the node currently being 1852 1.1 christos displayed. */ 1853 1.1 christos DECLARE_INFO_COMMAND (info_next_node, _("Select the Next node")) 1854 1.1 christos { 1855 1.1 christos info_next_label_of_node (window->node); 1856 1.1 christos info_handle_pointer ("Next", window); 1857 1.1 christos } 1858 1.1 christos 1859 1.1 christos /* Make WINDOW display the "Prev:" node of the node currently being 1860 1.1 christos displayed. */ 1861 1.1 christos DECLARE_INFO_COMMAND (info_prev_node, _("Select the Prev node")) 1862 1.1 christos { 1863 1.1 christos info_prev_label_of_node (window->node); 1864 1.1 christos info_handle_pointer ("Prev", window); 1865 1.1 christos } 1866 1.1 christos 1867 1.1 christos /* Make WINDOW display the "Up:" node of the node currently being 1868 1.1 christos displayed. */ 1869 1.1 christos DECLARE_INFO_COMMAND (info_up_node, _("Select the Up node")) 1870 1.1 christos { 1871 1.1 christos info_up_label_of_node (window->node); 1872 1.1 christos info_handle_pointer ("Up", window); 1873 1.1 christos } 1874 1.1 christos 1875 1.1 christos /* Make WINDOW display the last node of this info file. */ 1876 1.1 christos DECLARE_INFO_COMMAND (info_last_node, _("Select the last node in this file")) 1877 1.1 christos { 1878 1.1 christos register int i; 1879 1.1 christos FILE_BUFFER *fb = file_buffer_of_window (window); 1880 1.1 christos NODE *node = (NODE *)NULL; 1881 1.1 christos 1882 1.1 christos if (fb && fb->tags) 1883 1.1 christos { 1884 1.1 christos int last_node_tag_idx = -1; 1885 1.1 christos 1886 1.1 christos /* If no explicit argument, or argument of zero, default to the 1887 1.1 christos last node. */ 1888 1.1 christos if (count == 0 || (count == 1 && !info_explicit_arg)) 1889 1.1 christos count = -1; 1890 1.1 christos for (i = 0; count && fb->tags[i]; i++) 1891 1.1 christos if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */ 1892 1.1 christos { 1893 1.1 christos count--; 1894 1.1 christos last_node_tag_idx = i; 1895 1.1 christos } 1896 1.1 christos if (count > 0) 1897 1.1 christos i = last_node_tag_idx + 1; 1898 1.1 christos if (i > 0) 1899 1.1 christos node = info_get_node (fb->filename, fb->tags[i - 1]->nodename); 1900 1.1 christos } 1901 1.1 christos 1902 1.1 christos if (!node) 1903 1.1 christos info_error ((char *) _("This window has no additional nodes"), NULL, NULL); 1904 1.1 christos else 1905 1.1 christos info_set_node_of_window (1, window, node); 1906 1.1 christos } 1907 1.1 christos 1908 1.1 christos /* Make WINDOW display the first node of this info file. */ 1909 1.1 christos DECLARE_INFO_COMMAND (info_first_node, _("Select the first node in this file")) 1910 1.1 christos { 1911 1.1 christos FILE_BUFFER *fb = file_buffer_of_window (window); 1912 1.1 christos NODE *node = (NODE *)NULL; 1913 1.1 christos 1914 1.1 christos /* If no explicit argument, or argument of zero, default to the 1915 1.1 christos first node. */ 1916 1.1 christos if (count == 0) 1917 1.1 christos count = 1; 1918 1.1 christos if (fb && fb->tags) 1919 1.1 christos { 1920 1.1 christos register int i; 1921 1.1 christos int last_node_tag_idx = -1; 1922 1.1 christos 1923 1.1 christos for (i = 0; count && fb->tags[i]; i++) 1924 1.1 christos if (fb->tags[i]->nodelen != 0) /* don't count anchor tags */ 1925 1.1 christos { 1926 1.1 christos count--; 1927 1.1 christos last_node_tag_idx = i; 1928 1.1 christos } 1929 1.1 christos if (count > 0) 1930 1.1 christos i = last_node_tag_idx + 1; 1931 1.1 christos if (i > 0) 1932 1.1 christos node = info_get_node (fb->filename, fb->tags[i - 1]->nodename); 1933 1.1 christos } 1934 1.1 christos 1935 1.1 christos if (!node) 1936 1.1 christos info_error ((char *) _("This window has no additional nodes"), NULL, NULL); 1937 1.1 christos else 1938 1.1 christos info_set_node_of_window (1, window, node); 1939 1.1 christos } 1940 1.1 christos 1941 1.1 christos /* Select the last menu item in WINDOW->node. */ 1942 1.1 christos DECLARE_INFO_COMMAND (info_last_menu_item, 1943 1.1 christos _("Select the last item in this node's menu")) 1944 1.1 christos { 1945 1.1 christos info_menu_digit (window, 1, '0'); 1946 1.1 christos } 1947 1.1 christos 1948 1.1 christos /* Use KEY (a digit) to select the Nth menu item in WINDOW->node. */ 1949 1.1 christos DECLARE_INFO_COMMAND (info_menu_digit, _("Select this menu item")) 1950 1.1 christos { 1951 1.1 christos register int i, item; 1952 1.1 christos register REFERENCE **menu; 1953 1.1 christos 1954 1.1 christos menu = info_menu_of_node (window->node); 1955 1.1 christos 1956 1.1 christos if (!menu) 1957 1.1 christos { 1958 1.1 christos info_error ((char *) msg_no_menu_node, NULL, NULL); 1959 1.1 christos return; 1960 1.1 christos } 1961 1.1 christos 1962 1.1 christos /* We have the menu. See if there are this many items in it. */ 1963 1.1 christos item = key - '0'; 1964 1.2 christos 1965 1.1 christos /* Special case. Item "0" is the last item in this menu. */ 1966 1.1 christos if (item == 0) 1967 1.1 christos for (i = 0; menu[i] && menu[i + 1]; i++); 1968 1.1 christos else 1969 1.1 christos { 1970 1.1 christos for (i = 0; menu[i]; i++) 1971 1.1 christos if (i == item - 1) 1972 1.1 christos break; 1973 1.1 christos } 1974 1.1 christos 1975 1.1 christos if (menu[i]) 1976 1.1 christos { 1977 1.1 christos info_select_reference (window, menu[i]); 1978 1.1 christos if (menu[i]->line_number > 0) 1979 1.1 christos info_next_line (window, menu[i]->line_number - 1, key); 1980 1.2 christos } 1981 1.1 christos else 1982 1.1 christos info_error ((char *) _("There aren't %d items in this menu."), 1983 1.1 christos (void *)((intptr_t)item), NULL); 1984 1.1 christos 1985 1.1 christos info_free_references (menu); 1986 1.1 christos return; 1987 1.1 christos } 1988 1.1 christos 1989 1.1 christos 1990 1.1 christos 1991 1.1 christos /* Return a pointer to the xref in XREF_LIST that is nearest to POS, or 1993 1.1 christos NULL if XREF_LIST is empty. That is, if POS is within any of the 1994 1.1 christos given xrefs, return that one. Otherwise, return the one with the 1995 1.1 christos nearest beginning or end. If there are two that are equidistant, 1996 1.1 christos prefer the one forward. The return is in newly-allocated memory, 1997 1.1 christos since the caller frees it. 1998 1.1 christos 1999 1.1 christos This is called from info_menu_or_ref_item with XREF_LIST being all 2000 1.1 christos the xrefs in the node, and POS being point. The ui function that 2001 1.1 christos starts it all off is select-reference-this-line. 2002 1.1 christos 2003 1.1 christos This is not the same logic as in info.el. Info-get-token prefers 2004 1.1 christos searching backwards to searching forwards, and has a hardwired search 2005 1.1 christos limit of 200 chars (in Emacs 21.2). */ 2006 1.1 christos 2007 1.1 christos static REFERENCE ** 2008 1.1 christos nearest_xref (REFERENCE **xref_list, long int pos) 2009 1.1 christos { 2010 1.1 christos int this_xref; 2011 1.1 christos int nearest = -1; 2012 1.1 christos long best_delta = -1; 2013 1.1 christos 2014 1.1 christos for (this_xref = 0; xref_list[this_xref]; this_xref++) 2015 1.1 christos { 2016 1.1 christos long delta; 2017 1.1 christos REFERENCE *xref = xref_list[this_xref]; 2018 1.1 christos if (xref->start <= pos && pos <= xref->end) 2019 1.1 christos { /* POS is within this xref, we're done */ 2020 1.1 christos nearest = this_xref; 2021 1.1 christos break; 2022 1.1 christos } 2023 1.2 christos 2024 1.1 christos /* See how far POS is from this xref. Take into account the 2025 1.1 christos `*Note' that begins the xref, since as far as the user is 2026 1.1 christos concerned, that's where it starts. */ 2027 1.1 christos delta = MIN (labs (pos - (xref->start - (long)strlen (INFO_XREF_LABEL))), 2028 1.1 christos labs (pos - xref->end)); 2029 1.1 christos 2030 1.1 christos /* It's the <= instead of < that makes us choose the forward xref 2031 1.1 christos of POS if two are equidistant. Of course, because of all the 2032 1.1 christos punctuation surrounding xrefs, it's not necessarily obvious 2033 1.1 christos where one ends. */ 2034 1.1 christos if (delta <= best_delta || best_delta < 0) 2035 1.1 christos { 2036 1.1 christos nearest = this_xref; 2037 1.1 christos best_delta = delta; 2038 1.1 christos } 2039 1.1 christos } 2040 1.1 christos 2041 1.1 christos /* Maybe there was no list to search through. */ 2042 1.1 christos if (nearest < 0) 2043 1.1 christos return NULL; 2044 1.1 christos 2045 1.1 christos /* Ok, we have a nearest xref, make a list of it. */ 2046 1.1 christos { 2047 1.1 christos REFERENCE **ret = xmalloc (sizeof (REFERENCE *) * 2); 2048 1.1 christos ret[0] = info_copy_reference (xref_list[nearest]); 2049 1.1 christos ret[1] = NULL; 2050 1.1 christos return ret; 2051 1.1 christos } 2052 1.1 christos } 2053 1.1 christos 2054 1.1 christos 2055 1.1 christos /* Read a menu or followed reference from the user defaulting to the 2056 1.1 christos reference found on the current line, and select that node. The 2057 1.1 christos reading is done with completion. BUILDER is the function used 2058 1.1 christos to build the list of references. ASK_P is non-zero if the user 2059 1.1 christos should be prompted, or zero to select the default item. */ 2060 1.1 christos static void 2061 1.1 christos info_menu_or_ref_item (WINDOW *window, int count, 2062 1.1 christos unsigned char key, REFERENCE **(*builder) (NODE *node), int ask_p) 2063 1.1 christos { 2064 1.1 christos char *line; 2065 1.1 christos REFERENCE *entry; 2066 1.1 christos REFERENCE *defentry = NULL; 2067 1.1 christos REFERENCE **menu = (*builder) (window->node); 2068 1.1 christos 2069 1.1 christos if (!menu) 2070 1.1 christos { 2071 1.1 christos if (builder == info_menu_of_node) 2072 1.1 christos info_error ((char *) msg_no_menu_node, NULL, NULL); 2073 1.1 christos else 2074 1.1 christos info_error ((char *) msg_no_xref_node, NULL, NULL); 2075 1.1 christos return; 2076 1.1 christos } 2077 1.1 christos 2078 1.1 christos /* Default the selected reference to the one which is on the line that 2079 1.1 christos point is in. */ 2080 1.1 christos { 2081 1.1 christos REFERENCE **refs = NULL; 2082 1.1 christos int point_line = window_line_of_point (window); 2083 1.1 christos 2084 1.1 christos if (point_line != -1) 2085 1.1 christos { 2086 1.1 christos SEARCH_BINDING binding; 2087 1.1 christos 2088 1.1 christos binding.buffer = window->node->contents; 2089 1.1 christos binding.start = window->line_starts[point_line] - binding.buffer; 2090 1.1 christos if (window->line_starts[point_line + 1]) 2091 1.1 christos binding.end = window->line_starts[point_line + 1] - binding.buffer; 2092 1.1 christos else 2093 1.1 christos binding.end = window->node->nodelen; 2094 1.1 christos binding.flags = 0; 2095 1.1 christos 2096 1.1 christos if (builder == info_menu_of_node) 2097 1.1 christos { 2098 1.1 christos if (point_line) 2099 1.1 christos { 2100 1.1 christos binding.start--; 2101 1.1 christos refs = info_menu_items (&binding); 2102 1.1 christos } 2103 1.1 christos } 2104 1.1 christos else 2105 1.1 christos { 2106 1.1 christos #if defined (HANDLE_MAN_PAGES) 2107 1.1 christos if (window->node->flags & N_IsManPage) 2108 1.1 christos refs = manpage_xrefs_in_binding (window->node, &binding); 2109 1.1 christos else 2110 1.1 christos #endif /* HANDLE_MAN_PAGES */ 2111 1.1 christos refs = nearest_xref (menu, window->point); 2112 1.1 christos } 2113 1.1 christos 2114 1.1 christos if (refs && refs[0]) 2115 1.1 christos { 2116 1.1 christos if (strcmp (refs[0]->label, "Menu") != 0 2117 1.1 christos || builder == info_xrefs_of_node) 2118 1.1 christos { 2119 1.1 christos int which = 0; 2120 1.1 christos 2121 1.1 christos /* For xrefs, find the closest reference to point, 2122 1.1 christos unless we only have one reference (as we will if 2123 1.1 christos we've called nearest_xref above). It would be better 2124 1.1 christos to have only one piece of code, but the conditions 2125 1.1 christos when we call this are tangled. */ 2126 1.1 christos if (builder == info_xrefs_of_node && refs[1]) 2127 1.1 christos { 2128 1.1 christos int closest = -1; 2129 1.1 christos 2130 1.1 christos for (; refs[which]; which++) 2131 1.1 christos { 2132 1.1 christos if (window->point >= refs[which]->start 2133 1.1 christos && window->point <= refs[which]->end) 2134 1.1 christos { 2135 1.1 christos closest = which; 2136 1.1 christos break; 2137 1.1 christos } 2138 1.1 christos else if (window->point < refs[which]->start) 2139 1.1 christos break; 2140 1.1 christos } 2141 1.1 christos if (which > 0) 2142 1.1 christos { 2143 1.1 christos if (closest == -1) 2144 1.1 christos which--; 2145 1.1 christos else 2146 1.1 christos which = closest; 2147 1.1 christos } 2148 1.1 christos } 2149 1.1 christos 2150 1.1 christos defentry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); 2151 1.1 christos defentry->label = xstrdup (refs[which]->label); 2152 1.1 christos defentry->filename = refs[which]->filename; 2153 1.1 christos defentry->nodename = refs[which]->nodename; 2154 1.1 christos defentry->line_number = refs[which]->line_number; 2155 1.1 christos 2156 1.1 christos if (defentry->filename) 2157 1.1 christos defentry->filename = xstrdup (defentry->filename); 2158 1.1 christos if (defentry->nodename) 2159 1.1 christos defentry->nodename = xstrdup (defentry->nodename); 2160 1.1 christos } 2161 1.1 christos info_free_references (refs); 2162 1.1 christos } 2163 1.1 christos } 2164 1.1 christos } 2165 1.1 christos 2166 1.1 christos /* If we are going to ask the user a question, do it now. */ 2167 1.1 christos if (ask_p) 2168 1.1 christos { 2169 1.1 christos char *prompt; 2170 1.1 christos 2171 1.1 christos /* Build the prompt string. */ 2172 1.1 christos if (builder == info_menu_of_node) 2173 1.1 christos { 2174 1.1 christos if (defentry) 2175 1.1 christos { 2176 1.1 christos prompt = xmalloc (strlen (defentry->label) 2177 1.1 christos + strlen (_("Menu item (%s): "))); 2178 1.1 christos sprintf (prompt, _("Menu item (%s): "), defentry->label); 2179 1.1 christos } 2180 1.1 christos else 2181 1.1 christos prompt = xstrdup (_("Menu item: ")); 2182 1.1 christos } 2183 1.1 christos else 2184 1.1 christos { 2185 1.1 christos if (defentry) 2186 1.1 christos { 2187 1.1 christos prompt = xmalloc (strlen (defentry->label) 2188 1.1 christos + strlen (_("Follow xref (%s): "))); 2189 1.1 christos sprintf (prompt, _("Follow xref (%s): "), defentry->label); 2190 1.1 christos } 2191 1.1 christos else 2192 1.1 christos prompt = xstrdup (_("Follow xref: ")); 2193 1.1 christos } 2194 1.1 christos 2195 1.1 christos line = info_read_completing_in_echo_area (window, prompt, menu); 2196 1.1 christos free (prompt); 2197 1.1 christos 2198 1.1 christos window = active_window; 2199 1.1 christos 2200 1.1 christos /* User aborts, just quit. */ 2201 1.1 christos if (!line) 2202 1.1 christos { 2203 1.1 christos maybe_free (defentry); 2204 1.1 christos info_free_references (menu); 2205 1.1 christos info_abort_key (window, 0, 0); 2206 1.1 christos return; 2207 1.1 christos } 2208 1.1 christos 2209 1.1 christos /* If we had a default and the user accepted it, use that. */ 2210 1.1 christos if (!*line) 2211 1.1 christos { 2212 1.1 christos free (line); 2213 1.1 christos if (defentry) 2214 1.1 christos line = xstrdup (defentry->label); 2215 1.1 christos else 2216 1.1 christos line = (char *)NULL; 2217 1.1 christos } 2218 1.1 christos } 2219 1.1 christos else 2220 1.1 christos { 2221 1.1 christos /* Not going to ask any questions. If we have a default entry, use 2222 1.1 christos that, otherwise return. */ 2223 1.1 christos if (!defentry) 2224 1.1 christos return; 2225 1.1 christos else 2226 1.1 christos line = xstrdup (defentry->label); 2227 1.1 christos } 2228 1.1 christos 2229 1.1 christos if (line) 2230 1.1 christos { 2231 1.1 christos /* It is possible that the references have more than a single 2232 1.1 christos entry with the same label, and also LINE is down-cased, which 2233 1.1 christos complicates matters even more. Try to be as accurate as we 2234 1.1 christos can: if they've chosen the default, use defentry directly. */ 2235 1.1 christos if (defentry && strcmp (line, defentry->label) == 0) 2236 1.1 christos entry = defentry; 2237 1.1 christos else 2238 1.1 christos /* Find the selected label in the references. If there are 2239 1.1 christos more than one label which matches, find the one that's 2240 1.1 christos closest to point. */ 2241 1.1 christos { 2242 1.1 christos register int i; 2243 1.1 christos int best = -1, min_dist = window->node->nodelen; 2244 1.1 christos REFERENCE *ref; 2245 1.1 christos 2246 1.1 christos for (i = 0; menu && (ref = menu[i]); i++) 2247 1.1 christos { 2248 1.1 christos /* Need to use strcasecmp because LINE is downcased 2249 1.1 christos inside info_read_completing_in_echo_area. */ 2250 1.2 christos if (strcasecmp (line, ref->label) == 0) 2251 1.1 christos { 2252 1.1 christos /* ref->end is more accurate estimate of position 2253 1.1 christos for menus than ref->start. Go figure. */ 2254 1.1 christos int dist = labs (window->point - ref->end); 2255 1.1 christos 2256 1.1 christos if (dist < min_dist) 2257 1.1 christos { 2258 1.1 christos min_dist = dist; 2259 1.1 christos best = i; 2260 1.1 christos } 2261 1.1 christos } 2262 1.1 christos } 2263 1.1 christos if (best != -1) 2264 1.1 christos entry = menu[best]; 2265 1.1 christos else 2266 1.1 christos entry = (REFERENCE *)NULL; 2267 1.1 christos } 2268 1.1 christos 2269 1.1 christos if (!entry && defentry) 2270 1.1 christos info_error ((char *) _("The reference disappeared! (%s)."), line, NULL); 2271 1.1 christos else 2272 1.1 christos { 2273 1.1 christos NODE *orig = window->node; 2274 1.1 christos info_select_reference (window, entry); 2275 1.1 christos 2276 1.1 christos if (builder == info_xrefs_of_node && window->node != orig 2277 1.1 christos && !(window->node->flags & N_FromAnchor)) 2278 1.1 christos { /* Search for this reference in the node. */ 2279 1.1 christos long offset; 2280 1.1 christos long start; 2281 1.1 christos 2282 1.1 christos if (window->line_count > 0) 2283 1.1 christos start = window->line_starts[1] - window->node->contents; 2284 1.1 christos else 2285 1.1 christos start = 0; 2286 1.1 christos 2287 1.1 christos offset = 2288 1.1 christos info_target_search_node (window->node, entry->label, start); 2289 1.1 christos 2290 1.1 christos if (offset != -1) 2291 1.1 christos { 2292 1.1 christos window->point = offset; 2293 1.1 christos window_adjust_pagetop (window); 2294 1.1 christos } 2295 1.1 christos } 2296 1.1 christos 2297 1.1 christos if (entry->line_number > 0) 2298 1.1 christos /* next_line starts at line 1? Anyway, the -1 makes it 2299 1.1 christos move to the right line. */ 2300 1.1 christos info_next_line (window, entry->line_number - 1, key); 2301 1.1 christos } 2302 1.1 christos 2303 1.1 christos free (line); 2304 1.1 christos if (defentry) 2305 1.1 christos { 2306 1.1 christos free (defentry->label); 2307 1.1 christos maybe_free (defentry->filename); 2308 1.1 christos maybe_free (defentry->nodename); 2309 1.1 christos free (defentry); 2310 1.1 christos } 2311 1.1 christos } 2312 1.1 christos 2313 1.1 christos info_free_references (menu); 2314 1.1 christos 2315 1.1 christos if (!info_error_was_printed) 2316 1.1 christos window_clear_echo_area (); 2317 1.1 christos } 2318 1.1 christos 2319 1.1 christos /* Read a line (with completion) which is the name of a menu item, 2320 1.1 christos and select that item. */ 2321 1.1 christos DECLARE_INFO_COMMAND (info_menu_item, _("Read a menu item and select its node")) 2322 1.1 christos { 2323 1.1 christos info_menu_or_ref_item (window, count, key, info_menu_of_node, 1); 2324 1.1 christos } 2325 1.1 christos 2326 1.1 christos /* Read a line (with completion) which is the name of a reference to 2327 1.1 christos follow, and select the node. */ 2328 1.1 christos DECLARE_INFO_COMMAND 2329 1.1 christos (info_xref_item, _("Read a footnote or cross reference and select its node")) 2330 1.1 christos { 2331 1.1 christos info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1); 2332 1.1 christos } 2333 1.1 christos 2334 1.1 christos /* Position the cursor at the start of this node's menu. */ 2335 1.1 christos DECLARE_INFO_COMMAND (info_find_menu, _("Move to the start of this node's menu")) 2336 1.1 christos { 2337 1.1 christos SEARCH_BINDING binding; 2338 1.1 christos long position; 2339 1.1 christos 2340 1.1 christos binding.buffer = window->node->contents; 2341 1.1 christos binding.start = 0; 2342 1.1 christos binding.end = window->node->nodelen; 2343 1.1 christos binding.flags = S_FoldCase | S_SkipDest; 2344 1.1 christos 2345 1.1 christos position = search (INFO_MENU_LABEL, &binding); 2346 1.1 christos 2347 1.1 christos if (position == -1) 2348 1.1 christos info_error ((char *) msg_no_menu_node, NULL, NULL); 2349 1.1 christos else 2350 1.1 christos { 2351 1.1 christos window->point = position; 2352 1.1 christos window_adjust_pagetop (window); 2353 1.1 christos window->flags |= W_UpdateWindow; 2354 1.1 christos } 2355 1.1 christos } 2356 1.1 christos 2357 1.1 christos /* Visit as many menu items as is possible, each in a separate window. */ 2358 1.1 christos DECLARE_INFO_COMMAND (info_visit_menu, 2359 1.1 christos _("Visit as many menu items at once as possible")) 2360 1.1 christos { 2361 1.1 christos register int i; 2362 1.1 christos REFERENCE *entry, **menu; 2363 1.1 christos 2364 1.1 christos menu = info_menu_of_node (window->node); 2365 1.1 christos 2366 1.1 christos if (!menu) 2367 1.1 christos info_error ((char *) msg_no_menu_node, NULL, NULL); 2368 1.1 christos 2369 1.1 christos for (i = 0; (!info_error_was_printed) && (entry = menu[i]); i++) 2370 1.1 christos { 2371 1.1 christos WINDOW *new; 2372 1.1 christos 2373 1.1 christos new = window_make_window (window->node); 2374 1.1 christos window_tile_windows (TILE_INTERNALS); 2375 1.1 christos 2376 1.1 christos if (!new) 2377 1.1 christos info_error ((char *) msg_win_too_small, NULL, NULL); 2378 1.1 christos else 2379 1.1 christos { 2380 1.1 christos active_window = new; 2381 1.1 christos info_select_reference (new, entry); 2382 1.1 christos } 2383 1.1 christos } 2384 1.1 christos } 2385 1.1 christos 2386 1.1 christos /* Read a line of input which is a node name, and go to that node. */ 2387 1.1 christos DECLARE_INFO_COMMAND (info_goto_node, _("Read a node name and select it")) 2388 1.1 christos { 2389 1.1 christos char *line; 2390 1.1 christos 2391 1.1 christos #define GOTO_COMPLETES 2392 1.1 christos #if defined (GOTO_COMPLETES) 2393 1.1 christos /* Build a completion list of all of the known nodes. */ 2394 1.1 christos { 2395 1.1 christos register int fbi, i; 2396 1.1 christos FILE_BUFFER *current; 2397 1.1 christos REFERENCE **items = (REFERENCE **)NULL; 2398 1.1 christos int items_index = 0; 2399 1.1 christos int items_slots = 0; 2400 1.1 christos 2401 1.1 christos current = file_buffer_of_window (window); 2402 1.1 christos 2403 1.1 christos for (fbi = 0; info_loaded_files && info_loaded_files[fbi]; fbi++) 2404 1.1 christos { 2405 1.1 christos FILE_BUFFER *fb; 2406 1.1 christos REFERENCE *entry; 2407 1.1 christos int this_is_the_current_fb; 2408 1.1 christos 2409 1.1 christos fb = info_loaded_files[fbi]; 2410 1.1 christos this_is_the_current_fb = (current == fb); 2411 1.1 christos 2412 1.1 christos entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); 2413 1.1 christos entry->filename = entry->nodename = (char *)NULL; 2414 1.1 christos entry->label = (char *)xmalloc (4 + strlen (fb->filename)); 2415 1.1 christos sprintf (entry->label, "(%s)*", fb->filename); 2416 1.1 christos 2417 1.1 christos add_pointer_to_array 2418 1.1 christos (entry, items_index, items, items_slots, 10, REFERENCE *); 2419 1.1 christos 2420 1.1 christos if (fb->tags) 2421 1.1 christos { 2422 1.1 christos for (i = 0; fb->tags[i]; i++) 2423 1.1 christos { 2424 1.1 christos entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); 2425 1.1 christos entry->filename = entry->nodename = (char *)NULL; 2426 1.1 christos if (this_is_the_current_fb) 2427 1.1 christos entry->label = xstrdup (fb->tags[i]->nodename); 2428 1.1 christos else 2429 1.1 christos { 2430 1.1 christos entry->label = (char *) xmalloc 2431 1.1 christos (4 + strlen (fb->filename) + 2432 1.1 christos strlen (fb->tags[i]->nodename)); 2433 1.1 christos sprintf (entry->label, "(%s)%s", 2434 1.1 christos fb->filename, fb->tags[i]->nodename); 2435 1.1 christos } 2436 1.1 christos 2437 1.1 christos add_pointer_to_array 2438 1.1 christos (entry, items_index, items, items_slots, 100, REFERENCE *); 2439 1.1 christos } 2440 1.1 christos } 2441 1.1 christos } 2442 1.1 christos line = info_read_maybe_completing (window, (char *) _("Goto node: "), 2443 1.1 christos items); 2444 1.1 christos info_free_references (items); 2445 1.1 christos } 2446 1.1 christos #else /* !GOTO_COMPLETES */ 2447 1.1 christos line = info_read_in_echo_area (window, (char *) _("Goto node: ")); 2448 1.1 christos #endif /* !GOTO_COMPLETES */ 2449 1.1 christos 2450 1.1 christos /* If the user aborted, quit now. */ 2451 1.1 christos if (!line) 2452 1.1 christos { 2453 1.1 christos info_abort_key (window, 0, 0); 2454 1.1 christos return; 2455 1.1 christos } 2456 1.1 christos 2457 1.1 christos canonicalize_whitespace (line); 2458 1.1 christos 2459 1.1 christos if (*line) 2460 1.1 christos info_parse_and_select (line, window); 2461 1.1 christos 2462 1.1 christos free (line); 2463 1.1 christos if (!info_error_was_printed) 2464 1.1 christos window_clear_echo_area (); 2465 1.1 christos } 2466 1.1 christos 2467 1.1 christos /* Follow the menu list in MENUS (list of strings terminated by a NULL 2469 1.1 christos entry) from INITIAL_NODE. If can't continue at any point (no menu or 2470 1.1 christos no menu entry for the next item), return the node so far -- that 2471 1.1 christos might be INITIAL_NODE itself. If error, *ERRSTR and *ERRARG[12] will 2472 1.1 christos be set to the error message and argument for message, otherwise they 2473 1.1 christos will be NULL. */ 2474 1.1 christos 2475 1.1 christos NODE * 2476 1.1 christos info_follow_menus (NODE *initial_node, char **menus, 2477 1.1 christos const char **errstr, char **errarg1, char **errarg2) 2478 1.1 christos { 2479 1.1 christos NODE *node = NULL; 2480 1.1 christos *errstr = *errarg1 = *errarg2 = NULL; 2481 1.1 christos 2482 1.1 christos for (; *menus; menus++) 2483 1.1 christos { 2484 1.1 christos static char *first_arg = NULL; 2485 1.1 christos REFERENCE **menu; 2486 1.1 christos REFERENCE *entry; 2487 1.1 christos char *arg = *menus; /* Remember the name of the menu entry we want. */ 2488 1.1 christos 2489 1.1 christos /* A leading space is certainly NOT part of a node name. Most 2490 1.1 christos probably, they typed a space after the separating comma. The 2491 1.1 christos strings in menus[] have their whitespace canonicalized, so 2492 1.1 christos there's at most one space to ignore. */ 2493 1.1 christos if (*arg == ' ') 2494 1.1 christos arg++; 2495 1.1 christos if (!first_arg) 2496 1.1 christos first_arg = arg; 2497 1.1 christos 2498 1.1 christos /* Build and return a list of the menu items in this node. */ 2499 1.1 christos menu = info_menu_of_node (initial_node); 2500 1.1 christos 2501 1.1 christos /* If no menu item in this node, stop here, but let the user 2502 1.1 christos continue to use Info. Perhaps they wanted this node and didn't 2503 1.1 christos realize it. */ 2504 1.1 christos if (!menu) 2505 1.1 christos { 2506 1.1 christos if (arg == first_arg) 2507 1.1 christos { 2508 1.1 christos node = make_manpage_node (first_arg); 2509 1.1 christos if (node) 2510 1.1 christos goto maybe_got_node; 2511 1.1 christos } 2512 1.1 christos *errstr = _("No menu in node `%s'."); 2513 1.1 christos *errarg1 = node_printed_rep (initial_node); 2514 1.1 christos return initial_node; 2515 1.1 christos } 2516 1.1 christos 2517 1.1 christos /* Find the specified menu item. */ 2518 1.1 christos entry = info_get_labeled_reference (arg, menu); 2519 1.1 christos 2520 1.1 christos /* If the item wasn't found, search the list sloppily. Perhaps this 2521 1.1 christos user typed "buffer" when they really meant "Buffers". */ 2522 1.1 christos if (!entry) 2523 1.1 christos { 2524 1.1 christos int i; 2525 1.1 christos int best_guess = -1; 2526 1.1 christos 2527 1.1 christos for (i = 0; (entry = menu[i]); i++) 2528 1.1 christos { 2529 1.1 christos if (strcasecmp (entry->label, arg) == 0) 2530 1.1 christos break; 2531 1.1 christos else 2532 1.1 christos if ((best_guess == -1) 2533 1.1 christos && (strncasecmp (entry->label, arg, strlen (arg)) == 0)) 2534 1.1 christos best_guess = i; 2535 1.1 christos } 2536 1.1 christos 2537 1.1 christos if (!entry && best_guess != -1) 2538 1.1 christos entry = menu[best_guess]; 2539 1.1 christos } 2540 1.1 christos 2541 1.1 christos /* If we still failed to find the reference, start Info with the current 2542 1.1 christos node anyway. It is probably a misspelling. */ 2543 1.1 christos if (!entry) 2544 1.1 christos { 2545 1.1 christos if (arg == first_arg) 2546 1.1 christos { 2547 1.1 christos /* Maybe they typed "info foo" instead of "info -f foo". */ 2548 1.1 christos node = info_get_node (first_arg, 0); 2549 1.1 christos if (node) 2550 1.1 christos add_file_directory_to_path (first_arg); 2551 1.1 christos else 2552 1.1 christos node = make_manpage_node (first_arg); 2553 1.1 christos if (node) 2554 1.1 christos goto maybe_got_node; 2555 1.1 christos } 2556 1.1 christos 2557 1.1 christos info_free_references (menu); 2558 1.1 christos *errstr = _("No menu item `%s' in node `%s'."); 2559 1.1 christos *errarg1 = arg; 2560 1.1 christos *errarg2 = node_printed_rep (initial_node); 2561 1.1 christos return initial_node; 2562 1.1 christos } 2563 1.1 christos 2564 1.1 christos /* We have found the reference that the user specified. If no 2565 1.1 christos filename in this reference, define it. */ 2566 1.1 christos if (!entry->filename) 2567 1.1 christos entry->filename = xstrdup (initial_node->parent ? initial_node->parent 2568 1.1 christos : initial_node->filename); 2569 1.1 christos 2570 1.1 christos /* Try to find this node. */ 2571 1.1 christos node = info_get_node (entry->filename, entry->nodename); 2572 1.1 christos if (!node && arg == first_arg) 2573 1.1 christos { 2574 1.1 christos node = make_manpage_node (first_arg); 2575 1.1 christos if (node) 2576 1.1 christos goto maybe_got_node; 2577 1.1 christos } 2578 1.1 christos 2579 1.1 christos /* Since we cannot find it, try using the label of the entry as a 2580 1.1 christos file, i.e., "(LABEL)Top". */ 2581 1.1 christos if (!node && entry->nodename 2582 1.1 christos && strcmp (entry->label, entry->nodename) == 0) 2583 1.1 christos node = info_get_node (entry->label, "Top"); 2584 1.1 christos 2585 1.1 christos maybe_got_node: 2586 1.1 christos if (!node) 2587 1.1 christos { 2588 1.1 christos *errstr = _("Unable to find node referenced by `%s' in `%s'."); 2589 1.1 christos *errarg1 = xstrdup (entry->label); 2590 1.1 christos *errarg2 = node_printed_rep (initial_node); 2591 1.1 christos info_free_references (menu); 2592 1.1 christos return initial_node; 2593 1.1 christos } 2594 1.1 christos 2595 1.1 christos info_free_references (menu); 2596 1.1 christos 2597 1.1 christos /* Success. Go round the loop again. */ 2598 1.1 christos free (initial_node); 2599 1.1 christos initial_node = node; 2600 1.1 christos } 2601 1.1 christos 2602 1.1 christos return initial_node; 2603 1.1 christos } 2604 1.1 christos 2605 1.1 christos /* Split STR into individual node names by writing null bytes in wherever 2606 1.1 christos there are commas and constructing a list of the resulting pointers. 2607 1.1 christos (We can do this since STR has had canonicalize_whitespace called on it.) 2608 1.1 christos Return array terminated with NULL. */ 2609 1.1 christos 2610 1.1 christos static char ** 2611 1.1 christos split_list_of_nodenames (char *str) 2612 1.1 christos { 2613 1.1 christos unsigned len = 2; 2614 1.1 christos char **nodes = xmalloc (len * sizeof (char *)); 2615 1.1 christos 2616 1.1 christos nodes[len - 2] = str; 2617 1.1 christos 2618 1.1 christos while (*str++) 2619 1.1 christos { 2620 1.1 christos if (*str == ',') 2621 1.1 christos { 2622 1.1 christos *str++ = 0; /* get past the null byte */ 2623 1.1 christos len++; 2624 1.1 christos nodes = xrealloc (nodes, len * sizeof (char *)); 2625 1.1 christos nodes[len - 2] = str; 2626 1.1 christos } 2627 1.1 christos } 2628 1.1 christos 2629 1.1 christos nodes[len - 1] = NULL; 2630 1.1 christos 2631 1.1 christos return nodes; 2632 1.1 christos } 2633 1.1 christos 2634 1.1 christos 2635 1.1 christos /* Read a line of input which is a sequence of menus (starting from 2636 1.1 christos dir), and follow them. */ 2637 1.1 christos DECLARE_INFO_COMMAND (info_menu_sequence, 2638 1.1 christos _("Read a list of menus starting from dir and follow them")) 2639 1.1 christos { 2640 1.1 christos char *line = info_read_in_echo_area (window, (char *) _("Follow menus: ")); 2641 1.1 christos 2642 1.1 christos /* If the user aborted, quit now. */ 2643 1.1 christos if (!line) 2644 1.1 christos { 2645 1.1 christos info_abort_key (window, 0, 0); 2646 1.1 christos return; 2647 1.1 christos } 2648 1.1 christos 2649 1.1 christos canonicalize_whitespace (line); 2650 1.1 christos 2651 1.1 christos if (*line) 2652 1.1 christos { 2653 1.1 christos const char *errstr; 2654 1.1 christos char *errarg1, *errarg2; 2655 1.1 christos NODE *dir_node = info_get_node (NULL, NULL); 2656 1.1 christos char **nodes = split_list_of_nodenames (line); 2657 1.1 christos NODE *node = NULL; 2658 1.1 christos 2659 1.1 christos /* If DIR_NODE is NULL, they might be reading a file directly, 2660 1.1 christos like in "info -d . -f ./foo". Try using "Top" instead. */ 2661 1.1 christos if (!dir_node) 2662 1.1 christos { 2663 1.1 christos char *file_name = window->node->parent; 2664 1.1 christos 2665 1.1 christos if (!file_name) 2666 1.1 christos file_name = window->node->filename; 2667 1.1 christos dir_node = info_get_node (file_name, NULL); 2668 1.1 christos } 2669 1.1 christos 2670 1.1 christos /* If we still cannot find the starting point, give up. 2671 1.1 christos We cannot allow a NULL pointer inside info_follow_menus. */ 2672 1.1 christos if (!dir_node) 2673 1.1 christos info_error ((char *) msg_cant_find_node, "Top", NULL); 2674 1.1 christos else 2675 1.1 christos node = info_follow_menus (dir_node, nodes, &errstr, &errarg1, &errarg2); 2676 1.1 christos 2677 1.1 christos free (nodes); 2678 1.1 christos if (!errstr) 2679 1.1 christos info_set_node_of_window (1, window, node); 2680 1.1 christos else 2681 1.1 christos info_error ((char *) errstr, errarg1, errarg2); 2682 1.1 christos } 2683 1.1 christos 2684 1.1 christos free (line); 2685 1.1 christos if (!info_error_was_printed) 2686 1.1 christos window_clear_echo_area (); 2687 1.1 christos } 2688 1.1 christos 2689 1.1 christos /* Search the menu MENU for a (possibly mis-spelled) entry ARG. 2690 1.1 christos Return the menu entry, or the best guess for what they meant by ARG, 2691 1.1 christos or NULL if there's nothing in this menu seems to fit the bill. 2692 1.1 christos If EXACT is non-zero, allow only exact matches. */ 2693 1.1 christos static REFERENCE * 2694 1.1 christos entry_in_menu (char *arg, REFERENCE **menu, int exact) 2695 1.1 christos { 2696 1.1 christos REFERENCE *entry; 2697 1.1 christos 2698 1.1 christos /* First, try to find the specified menu item verbatim. */ 2699 1.1 christos entry = info_get_labeled_reference (arg, menu); 2700 1.1 christos 2701 1.1 christos /* If the item wasn't found, search the list sloppily. Perhaps we 2702 1.1 christos have "Option Summary", but ARG is "option". */ 2703 1.1 christos if (!entry && !exact) 2704 1.1 christos { 2705 1.1 christos int i; 2706 1.1 christos int best_guess = -1; 2707 1.1 christos 2708 1.1 christos for (i = 0; (entry = menu[i]); i++) 2709 1.1 christos { 2710 1.1 christos if (strcasecmp (entry->label, arg) == 0) 2711 1.1 christos break; 2712 1.1 christos else 2713 1.1 christos if (strncasecmp (entry->label, arg, strlen (arg)) == 0) 2714 1.1 christos best_guess = i; 2715 1.1 christos } 2716 1.1 christos 2717 1.1 christos if (!entry && best_guess != -1) 2718 1.1 christos entry = menu[best_guess]; 2719 1.1 christos } 2720 1.1 christos 2721 1.1 christos return entry; 2722 1.1 christos } 2723 1.1 christos 2724 1.1 christos /* Find the node that is the best candidate to list the PROGRAM's 2725 1.1 christos invocation info and its command-line options, by looking for menu 2726 1.1 christos items and chains of menu items with characteristic names. */ 2727 1.1 christos void 2728 1.1 christos info_intuit_options_node (WINDOW *window, NODE *initial_node, char *program) 2729 1.1 christos { 2730 1.1 christos /* The list of node names typical for GNU manuals where the program 2731 1.1 christos usage and specifically the command-line arguments are described. 2732 1.1 christos This is pure heuristics. I gathered these node names by looking 2733 1.1 christos at all the Info files I could put my hands on. If you are 2734 1.1 christos looking for evidence to complain to the GNU project about 2735 1.1 christos non-uniform style of documentation, here you have your case! */ 2736 1.1 christos static const char *invocation_nodes[] = { 2737 1.1 christos "%s invocation", 2738 1.1 christos "Invoking %s", 2739 1.1 christos "Preliminaries", /* m4 has Invoking under Preliminaries! */ 2740 1.1 christos "Invocation", 2741 1.1 christos "Command Arguments",/* Emacs */ 2742 1.1 christos "Invoking `%s'", 2743 1.1 christos "%s options", 2744 1.1 christos "Options", 2745 1.1 christos "Option ", /* e.g. "Option Summary" */ 2746 1.1 christos "Invoking", 2747 1.1 christos "All options", /* tar, paxutils */ 2748 1.1 christos "Arguments", 2749 1.1 christos "%s cmdline", /* ar */ 2750 1.1 christos "%s", /* last resort */ 2751 1.1 christos (const char *)0 2752 1.1 christos }; 2753 1.1 christos NODE *node = NULL; 2754 1.1 christos REFERENCE **menu; 2755 1.1 christos const char **try_node; 2756 1.1 christos 2757 1.1 christos /* We keep looking deeper and deeper in the menu structure until 2758 1.1 christos there are no more menus or no menu items from the above list. 2759 1.1 christos Some manuals have the invocation node sitting 3 or 4 levels deep 2760 1.1 christos in the menu hierarchy... */ 2761 1.1 christos for (node = initial_node; node; initial_node = node) 2762 1.1 christos { 2763 1.1 christos REFERENCE *entry = NULL; 2764 1.1 christos 2765 1.1 christos /* Build and return a list of the menu items in this node. */ 2766 1.1 christos menu = info_menu_of_node (initial_node); 2767 1.1 christos 2768 1.1 christos /* If no menu item in this node, stop here. Perhaps this node 2769 1.1 christos is the one they need. */ 2770 1.1 christos if (!menu) 2771 1.1 christos break; 2772 1.1 christos 2773 1.1 christos /* Look for node names typical for usage nodes in this menu. */ 2774 1.1 christos for (try_node = invocation_nodes; *try_node; try_node++) 2775 1.1 christos { 2776 1.1 christos char *nodename; 2777 1.1 christos 2778 1.1 christos nodename = xmalloc (strlen (program) + strlen (*try_node)); 2779 1.1 christos sprintf (nodename, *try_node, program); 2780 1.1 christos /* The last resort "%s" is dangerous, so we restrict it 2781 1.1 christos to exact matches here. */ 2782 1.1 christos entry = entry_in_menu (nodename, menu, 2783 1.1 christos strcmp (*try_node, "%s") == 0); 2784 1.1 christos free (nodename); 2785 1.1 christos if (entry) 2786 1.1 christos break; 2787 1.1 christos } 2788 1.1 christos 2789 1.1 christos if (!entry) 2790 1.1 christos break; 2791 1.1 christos 2792 1.1 christos if (!entry->filename) 2793 1.1 christos entry->filename = xstrdup (initial_node->parent ? initial_node->parent 2794 1.1 christos : initial_node->filename); 2795 1.1 christos /* Try to find this node. */ 2796 1.1 christos node = info_get_node (entry->filename, entry->nodename); 2797 1.1 christos info_free_references (menu); 2798 1.1 christos if (!node) 2799 1.1 christos break; 2800 1.1 christos } 2801 1.1 christos 2802 1.1 christos /* We've got our best shot at the invocation node. Now select it. */ 2803 1.1 christos if (initial_node) 2804 1.1 christos info_set_node_of_window (1, window, initial_node); 2805 1.1 christos if (!info_error_was_printed) 2806 1.1 christos window_clear_echo_area (); 2807 1.1 christos } 2808 1.1 christos 2809 1.1 christos /* Given a name of an Info file, find the name of the package it 2810 1.1 christos describes by removing the leading directories and extensions. */ 2811 1.1 christos char * 2812 1.1 christos program_name_from_file_name (char *file_name) 2813 1.1 christos { 2814 1.1 christos int i; 2815 1.1 christos char *program_name = xstrdup (filename_non_directory (file_name)); 2816 1.1 christos 2817 1.1 christos for (i = strlen (program_name) - 1; i > 0; i--) 2818 1.1 christos if (program_name[i] == '.' 2819 1.1 christos && (FILENAME_CMPN (program_name + i, ".info", 5) == 0 2820 1.1 christos || FILENAME_CMPN (program_name + i, ".inf", 4) == 0 2821 1.1 christos #ifdef __MSDOS__ 2822 1.1 christos || FILENAME_CMPN (program_name + i, ".i", 2) == 0 2823 1.1 christos #endif 2824 1.1 christos || isdigit (program_name[i + 1]))) /* a man page foo.1 */ 2825 1.1 christos { 2826 1.1 christos program_name[i] = 0; 2827 1.1 christos break; 2828 1.1 christos } 2829 1.1 christos return program_name; 2830 1.1 christos } 2831 1.1 christos 2832 1.1 christos DECLARE_INFO_COMMAND (info_goto_invocation_node, 2833 1.1 christos _("Find the node describing program invocation")) 2834 1.1 christos { 2835 1.1 christos const char *invocation_prompt = _("Find Invocation node of [%s]: "); 2836 1.1 christos char *program_name, *line; 2837 1.1 christos char *default_program_name, *prompt, *file_name; 2838 1.1 christos NODE *top_node; 2839 1.1 christos 2840 1.1 christos /* Intuit the name of the program they are likely to want. 2841 1.1 christos We use the file name of the current Info file as a hint. */ 2842 1.1 christos file_name = window->node->parent ? window->node->parent 2843 1.1 christos : window->node->filename; 2844 1.1 christos default_program_name = program_name_from_file_name (file_name); 2845 1.1 christos 2846 1.1 christos prompt = (char *)xmalloc (strlen (default_program_name) + 2847 1.1 christos strlen (invocation_prompt)); 2848 1.1 christos sprintf (prompt, invocation_prompt, default_program_name); 2849 1.1 christos line = info_read_in_echo_area (window, prompt); 2850 1.1 christos free (prompt); 2851 1.1 christos if (!line) 2852 1.1 christos { 2853 1.1 christos info_abort_key (window, 0, 0); 2854 1.1 christos return; 2855 1.1 christos } 2856 1.1 christos if (*line) 2857 1.1 christos program_name = line; 2858 1.1 christos else 2859 1.1 christos program_name = default_program_name; 2860 1.1 christos 2861 1.1 christos /* In interactive usage they'd probably expect us to begin looking 2862 1.1 christos from the Top node. */ 2863 1.1 christos top_node = info_get_node (file_name, NULL); 2864 1.1 christos if (!top_node) 2865 1.1 christos info_error ((char *) msg_cant_find_node, "Top", NULL); 2866 1.1 christos 2867 1.1 christos info_intuit_options_node (window, top_node, program_name); 2868 1.1 christos free (line); 2869 1.1 christos free (default_program_name); 2870 1.1 christos } 2871 1.1 christos 2872 1.1 christos #if defined (HANDLE_MAN_PAGES) 2874 1.1 christos DECLARE_INFO_COMMAND (info_man, _("Read a manpage reference and select it")) 2875 1.1 christos { 2876 1.1 christos char *line; 2877 1.1 christos 2878 1.1 christos line = info_read_in_echo_area (window, (char *) _("Get Manpage: ")); 2879 1.1 christos 2880 1.1 christos if (!line) 2881 1.1 christos { 2882 1.1 christos info_abort_key (window, 0, 0); 2883 1.1 christos return; 2884 1.1 christos } 2885 1.1 christos 2886 1.1 christos canonicalize_whitespace (line); 2887 1.1 christos 2888 1.1 christos if (*line) 2889 1.1 christos { 2890 1.1 christos char *goto_command; 2891 1.1 christos 2892 1.1 christos goto_command = (char *)xmalloc 2893 1.1 christos (4 + strlen (MANPAGE_FILE_BUFFER_NAME) + strlen (line)); 2894 1.1 christos 2895 1.1 christos sprintf (goto_command, "(%s)%s", MANPAGE_FILE_BUFFER_NAME, line); 2896 1.1 christos 2897 1.1 christos info_parse_and_select (goto_command, window); 2898 1.1 christos free (goto_command); 2899 1.1 christos } 2900 1.1 christos 2901 1.1 christos free (line); 2902 1.1 christos if (!info_error_was_printed) 2903 1.1 christos window_clear_echo_area (); 2904 1.1 christos } 2905 1.1 christos #endif /* HANDLE_MAN_PAGES */ 2906 1.1 christos 2907 1.1 christos /* Move to the "Top" node in this file. */ 2908 1.1 christos DECLARE_INFO_COMMAND (info_top_node, _("Select the node `Top' in this file")) 2909 1.1 christos { 2910 1.1 christos info_parse_and_select ("Top", window); 2911 1.1 christos } 2912 1.1 christos 2913 1.1 christos /* Move to the node "(dir)Top". */ 2914 1.1 christos DECLARE_INFO_COMMAND (info_dir_node, _("Select the node `(dir)'")) 2915 1.1 christos { 2916 1.1 christos info_parse_and_select ("(dir)Top", window); 2917 1.1 christos } 2918 1.1 christos 2919 1.1 christos 2920 1.1 christos /* Read the name of a node to kill. The list of available nodes comes 2922 1.1 christos from the nodes appearing in the current window configuration. */ 2923 1.1 christos static char * 2924 1.1 christos read_nodename_to_kill (WINDOW *window) 2925 1.1 christos { 2926 1.1 christos int iw; 2927 1.1 christos char *nodename; 2928 1.1 christos INFO_WINDOW *info_win; 2929 1.1 christos REFERENCE **menu = NULL; 2930 1.1 christos int menu_index = 0, menu_slots = 0; 2931 1.1 christos char *default_nodename = xstrdup (active_window->node->nodename); 2932 1.1 christos char *prompt = xmalloc (strlen (_("Kill node (%s): ")) + strlen (default_nodename)); 2933 1.1 christos 2934 1.1 christos sprintf (prompt, _("Kill node (%s): "), default_nodename); 2935 1.1 christos 2936 1.1 christos for (iw = 0; (info_win = info_windows[iw]); iw++) 2937 1.1 christos { 2938 1.1 christos REFERENCE *entry = (REFERENCE *)xmalloc (sizeof (REFERENCE)); 2939 1.1 christos entry->label = xstrdup (info_win->window->node->nodename); 2940 1.1 christos entry->filename = entry->nodename = (char *)NULL; 2941 1.1 christos 2942 1.1 christos add_pointer_to_array (entry, menu_index, menu, menu_slots, 10, 2943 1.1 christos REFERENCE *); 2944 1.1 christos } 2945 1.1 christos 2946 1.1 christos nodename = info_read_completing_in_echo_area (window, prompt, menu); 2947 1.1 christos free (prompt); 2948 1.1 christos info_free_references (menu); 2949 1.1 christos if (nodename && !*nodename) 2950 1.1 christos { 2951 1.1 christos free (nodename); 2952 1.1 christos nodename = default_nodename; 2953 1.1 christos } 2954 1.1 christos else 2955 1.1 christos free (default_nodename); 2956 1.1 christos 2957 1.1 christos return nodename; 2958 1.1 christos } 2959 1.1 christos 2960 1.1 christos 2961 1.1 christos /* Delete NODENAME from this window, showing the most 2962 1.1 christos recently selected node in this window. */ 2963 1.1 christos static void 2964 1.1 christos kill_node (WINDOW *window, char *nodename) 2965 1.1 christos { 2966 1.1 christos int iw, i; 2967 1.1 christos INFO_WINDOW *info_win; 2968 1.1 christos NODE *temp; 2969 1.1 christos 2970 1.1 christos /* If there is no nodename to kill, quit now. */ 2971 1.1 christos if (!nodename) 2972 1.1 christos { 2973 1.1 christos info_abort_key (window, 0, 0); 2974 1.1 christos return; 2975 1.1 christos } 2976 1.1 christos 2977 1.1 christos /* If there is a nodename, find it in our window list. */ 2978 1.1 christos for (iw = 0; (info_win = info_windows[iw]); iw++) 2979 1.1 christos if (strcmp (nodename, info_win->nodes[info_win->current]->nodename) == 0 2980 1.1 christos && info_win->window == window) 2981 1.1 christos break; 2982 1.1 christos 2983 1.1 christos if (!info_win) 2984 1.1 christos { 2985 1.1 christos if (*nodename) 2986 1.1 christos info_error ((char *) _("Cannot kill node `%s'"), nodename, NULL); 2987 1.1 christos else 2988 1.1 christos window_clear_echo_area (); 2989 1.1 christos 2990 1.1 christos return; 2991 1.1 christos } 2992 1.1 christos 2993 1.1 christos /* If there are no more nodes left anywhere to view, complain and exit. */ 2994 1.1 christos if (info_windows_index == 1 && info_windows[0]->nodes_index == 1) 2995 1.1 christos { 2996 1.1 christos info_error ((char *) _("Cannot kill the last node"), NULL, NULL); 2997 1.1 christos return; 2998 1.1 christos } 2999 1.1 christos 3000 1.1 christos /* INFO_WIN contains the node that the user wants to stop viewing. Delete 3001 1.1 christos this node from the list of nodes previously shown in this window. */ 3002 1.1 christos for (i = info_win->current; i < info_win->nodes_index; i++) 3003 1.1 christos info_win->nodes[i] = info_win->nodes[i + 1]; 3004 1.1 christos 3005 1.1 christos /* There is one less node in this window's history list. */ 3006 1.1 christos info_win->nodes_index--; 3007 1.1 christos 3008 1.1 christos /* Make this window show the most recent history node. */ 3009 1.1 christos info_win->current = info_win->nodes_index - 1; 3010 1.1 christos 3011 1.1 christos /* If there aren't any nodes left in this window, steal one from the 3012 1.1 christos next window. */ 3013 1.1 christos if (info_win->current < 0) 3014 1.1 christos { 3015 1.1 christos INFO_WINDOW *stealer; 3016 1.1 christos int which, pagetop; 3017 1.1 christos long point; 3018 1.1 christos 3019 1.1 christos if (info_windows[iw + 1]) 3020 1.1 christos stealer = info_windows[iw + 1]; 3021 1.1 christos else 3022 1.1 christos stealer = info_windows[0]; 3023 1.1 christos 3024 1.1 christos /* If the node being displayed in the next window is not the most 3025 1.1 christos recently loaded one, get the most recently loaded one. */ 3026 1.1 christos if ((stealer->nodes_index - 1) != stealer->current) 3027 1.1 christos which = stealer->nodes_index - 1; 3028 1.1 christos 3029 1.1 christos /* Else, if there is another node behind the stealers current node, 3030 1.1 christos use that one. */ 3031 1.1 christos else if (stealer->current > 0) 3032 1.1 christos which = stealer->current - 1; 3033 1.1 christos 3034 1.1 christos /* Else, just use the node appearing in STEALER's window. */ 3035 1.1 christos else 3036 1.1 christos which = stealer->current; 3037 1.1 christos 3038 1.1 christos /* Copy this node. */ 3039 1.1 christos { 3040 1.1 christos NODE *copy = xmalloc (sizeof (NODE)); 3041 1.1 christos 3042 1.1 christos temp = stealer->nodes[which]; 3043 1.1 christos point = stealer->points[which]; 3044 1.1 christos pagetop = stealer->pagetops[which]; 3045 1.1 christos 3046 1.1 christos copy->filename = temp->filename; 3047 1.1 christos copy->parent = temp->parent; 3048 1.1 christos copy->nodename = temp->nodename; 3049 1.1 christos copy->contents = temp->contents; 3050 1.1 christos copy->nodelen = temp->nodelen; 3051 1.1 christos copy->flags = temp->flags; 3052 1.1 christos copy->display_pos = temp->display_pos; 3053 1.1 christos 3054 1.1 christos temp = copy; 3055 1.1 christos } 3056 1.1 christos 3057 1.1 christos window_set_node_of_window (info_win->window, temp); 3058 1.1 christos window->point = point; 3059 1.1 christos window->pagetop = pagetop; 3060 1.1 christos remember_window_and_node (info_win->window, temp); 3061 1.1 christos } 3062 1.1 christos else 3063 1.1 christos { 3064 1.1 christos temp = info_win->nodes[info_win->current]; 3065 1.1 christos temp->display_pos = info_win->points[info_win->current]; 3066 1.1 christos window_set_node_of_window (info_win->window, temp); 3067 1.1 christos } 3068 1.1 christos 3069 1.1 christos if (!info_error_was_printed) 3070 1.1 christos window_clear_echo_area (); 3071 1.1 christos 3072 1.1 christos if (auto_footnotes_p) 3073 1.1 christos info_get_or_remove_footnotes (window); 3074 1.1 christos } 3075 1.1 christos 3076 1.1 christos /* Kill current node, thus going back one in the node history. I (karl) 3077 1.1 christos do not think this is completely correct yet, because of the 3078 1.1 christos window-changing stuff in kill_node, but it's a lot better than the 3079 1.1 christos previous implementation, which did not account for nodes being 3080 1.1 christos visited twice at all. */ 3081 1.1 christos DECLARE_INFO_COMMAND (info_history_node, 3082 1.1 christos _("Select the most recently selected node")) 3083 1.1 christos { 3084 1.1 christos kill_node (window, active_window->node->nodename); 3085 1.1 christos } 3086 1.1 christos 3087 1.1 christos /* Kill named node. */ 3088 1.1 christos DECLARE_INFO_COMMAND (info_kill_node, _("Kill this node")) 3089 1.1 christos { 3090 1.1 christos char *nodename = read_nodename_to_kill (window); 3091 1.1 christos kill_node (window, nodename); 3092 1.1 christos } 3093 1.1 christos 3094 1.1 christos 3095 1.1 christos /* Read the name of a file and select the entire file. */ 3097 1.1 christos DECLARE_INFO_COMMAND (info_view_file, _("Read the name of a file and select it")) 3098 1.1 christos { 3099 1.1 christos char *line; 3100 1.1 christos 3101 1.1 christos line = info_read_in_echo_area (window, (char *) _("Find file: ")); 3102 1.1 christos if (!line) 3103 1.1 christos { 3104 1.1 christos info_abort_key (active_window, 1, 0); 3105 1.1 christos return; 3106 1.1 christos } 3107 1.1 christos 3108 1.1 christos if (*line) 3109 1.1 christos { 3110 1.1 christos NODE *node; 3111 1.1 christos 3112 1.1 christos node = info_get_node (line, "*"); 3113 1.1 christos if (!node) 3114 1.1 christos { 3115 1.1 christos if (info_recent_file_error) 3116 1.1 christos info_error (info_recent_file_error, NULL, NULL); 3117 1.1 christos else 3118 1.1 christos info_error ((char *) _("Cannot find `%s'."), line, NULL); 3119 1.1 christos } 3120 1.1 christos else 3121 1.1 christos info_set_node_of_window (1, window, node); 3122 1.1 christos 3123 1.1 christos free (line); 3124 1.1 christos } 3125 1.1 christos 3126 1.1 christos if (!info_error_was_printed) 3127 1.1 christos window_clear_echo_area (); 3128 1.1 christos } 3129 1.1 christos 3130 1.1 christos /* **************************************************************** */ 3132 1.1 christos /* */ 3133 1.1 christos /* Dumping and Printing Nodes */ 3134 1.1 christos /* */ 3135 1.1 christos /* **************************************************************** */ 3136 1.1 christos 3137 1.1 christos #define VERBOSE_NODE_DUMPING 3138 1.1 christos static void write_node_to_stream (NODE *node, FILE *stream); 3139 1.1 christos static void dump_node_to_stream (char *filename, char *nodename, 3140 1.1 christos FILE *stream, int dump_subnodes); 3141 1.1 christos static void initialize_dumping (void); 3142 1.1 christos 3143 1.1 christos /* Dump the nodes specified by FILENAME and NODENAMES to the file named 3144 1.1 christos in OUTPUT_FILENAME. If DUMP_SUBNODES is non-zero, recursively dump 3145 1.1 christos the nodes which appear in the menu of each node dumped. */ 3146 1.1 christos void 3147 1.1 christos dump_nodes_to_file (char *filename, char **nodenames, 3148 1.1 christos char *output_filename, int dump_subnodes) 3149 1.1 christos { 3150 1.1 christos register int i; 3151 1.1 christos FILE *output_stream; 3152 1.1 christos 3153 1.1 christos /* Get the stream to print the nodes to. Special case of an output 3154 1.1 christos filename of "-" means to dump the nodes to stdout. */ 3155 1.1 christos if (strcmp (output_filename, "-") == 0) 3156 1.1 christos output_stream = stdout; 3157 1.1 christos else 3158 1.1 christos output_stream = fopen (output_filename, "w"); 3159 1.1 christos 3160 1.1 christos if (!output_stream) 3161 1.1 christos { 3162 1.1 christos info_error ((char *) _("Could not create output file `%s'."), 3163 1.1 christos output_filename, NULL); 3164 1.1 christos return; 3165 1.1 christos } 3166 1.1 christos 3167 1.1 christos /* Print each node to stream. */ 3168 1.1 christos initialize_dumping (); 3169 1.1 christos for (i = 0; nodenames[i]; i++) 3170 1.1 christos dump_node_to_stream (filename, nodenames[i], output_stream, dump_subnodes); 3171 1.1 christos 3172 1.1 christos if (output_stream != stdout) 3173 1.1 christos fclose (output_stream); 3174 1.1 christos 3175 1.1 christos #if defined (VERBOSE_NODE_DUMPING) 3176 1.1 christos info_error ((char *) _("Done."), NULL, NULL); 3177 1.1 christos #endif /* VERBOSE_NODE_DUMPING */ 3178 1.1 christos } 3179 1.1 christos 3180 1.1 christos /* A place to remember already dumped nodes. */ 3181 1.1 christos static char **dumped_already = (char **)NULL; 3182 1.1 christos static int dumped_already_index = 0; 3183 1.1 christos static int dumped_already_slots = 0; 3184 1.1 christos 3185 1.1 christos static void 3186 1.1 christos initialize_dumping (void) 3187 1.1 christos { 3188 1.1 christos dumped_already_index = 0; 3189 1.1 christos } 3190 1.1 christos 3191 1.1 christos /* Get and print the node specified by FILENAME and NODENAME to STREAM. 3192 1.1 christos If DUMP_SUBNODES is non-zero, recursively dump the nodes which appear 3193 1.1 christos in the menu of each node dumped. */ 3194 1.1 christos static void 3195 1.1 christos dump_node_to_stream (char *filename, char *nodename, 3196 1.1 christos FILE *stream, int dump_subnodes) 3197 1.1 christos { 3198 1.1 christos register int i; 3199 1.1 christos NODE *node; 3200 1.1 christos 3201 1.1 christos node = info_get_node (filename, nodename); 3202 1.1 christos 3203 1.1 christos if (!node) 3204 1.1 christos { 3205 1.1 christos if (info_recent_file_error) 3206 1.1 christos info_error (info_recent_file_error, NULL, NULL); 3207 1.1 christos else 3208 1.1 christos { 3209 1.1 christos if (filename && *nodename != '(') 3210 1.1 christos info_error ((char *) msg_cant_file_node, 3211 1.1 christos filename_non_directory (filename), 3212 1.1 christos nodename); 3213 1.1 christos else 3214 1.1 christos info_error ((char *) msg_cant_find_node, nodename, NULL); 3215 1.1 christos } 3216 1.1 christos return; 3217 1.1 christos } 3218 1.1 christos 3219 1.1 christos /* If we have already dumped this node, don't dump it again. */ 3220 1.1 christos for (i = 0; i < dumped_already_index; i++) 3221 1.1 christos if (strcmp (node->nodename, dumped_already[i]) == 0) 3222 1.1 christos { 3223 1.1 christos free (node); 3224 1.1 christos return; 3225 1.1 christos } 3226 1.1 christos add_pointer_to_array (node->nodename, dumped_already_index, dumped_already, 3227 1.1 christos dumped_already_slots, 50, char *); 3228 1.1 christos 3229 1.1 christos #if defined (VERBOSE_NODE_DUMPING) 3230 1.1 christos /* Maybe we should print some information about the node being output. */ 3231 1.1 christos info_error ((char *) _("Writing node %s..."), node_printed_rep (node), NULL); 3232 1.1 christos #endif /* VERBOSE_NODE_DUMPING */ 3233 1.1 christos 3234 1.1 christos write_node_to_stream (node, stream); 3235 1.1 christos 3236 1.1 christos /* If we are dumping subnodes, get the list of menu items in this node, 3237 1.1 christos and dump each one recursively. */ 3238 1.1 christos if (dump_subnodes) 3239 1.1 christos { 3240 1.1 christos REFERENCE **menu = (REFERENCE **)NULL; 3241 1.1 christos 3242 1.1 christos /* If this node is an Index, do not dump the menu references. */ 3243 1.1 christos if (string_in_line ("Index", node->nodename) == -1) 3244 1.1 christos menu = info_menu_of_node (node); 3245 1.1 christos 3246 1.1 christos if (menu) 3247 1.1 christos { 3248 1.1 christos for (i = 0; menu[i]; i++) 3249 1.1 christos { 3250 1.1 christos /* We don't dump Info files which are different than the 3251 1.1 christos current one. */ 3252 1.1 christos if (!menu[i]->filename) 3253 1.1 christos dump_node_to_stream 3254 1.1 christos (filename, menu[i]->nodename, stream, dump_subnodes); 3255 1.1 christos } 3256 1.1 christos info_free_references (menu); 3257 1.1 christos } 3258 1.1 christos } 3259 1.1 christos 3260 1.1 christos free (node); 3261 1.1 christos } 3262 1.1 christos 3263 1.1 christos /* Dump NODE to FILENAME. If DUMP_SUBNODES is non-zero, recursively dump 3264 1.1 christos the nodes which appear in the menu of each node dumped. */ 3265 1.1 christos void 3266 1.1 christos dump_node_to_file (NODE *node, char *filename, int dump_subnodes) 3267 1.1 christos { 3268 1.1 christos FILE *output_stream; 3269 1.1 christos char *nodes_filename; 3270 1.1 christos 3271 1.1 christos /* Get the stream to print this node to. Special case of an output 3272 1.1 christos filename of "-" means to dump the nodes to stdout. */ 3273 1.1 christos if (strcmp (filename, "-") == 0) 3274 1.1 christos output_stream = stdout; 3275 1.1 christos else 3276 1.1 christos output_stream = fopen (filename, "w"); 3277 1.1 christos 3278 1.1 christos if (!output_stream) 3279 1.1 christos { 3280 1.1 christos info_error ((char *) _("Could not create output file `%s'."), filename, 3281 1.1 christos NULL); 3282 1.1 christos return; 3283 1.1 christos } 3284 1.1 christos 3285 1.1 christos if (node->parent) 3286 1.1 christos nodes_filename = node->parent; 3287 1.1 christos else 3288 1.1 christos nodes_filename = node->filename; 3289 1.1 christos 3290 1.1 christos initialize_dumping (); 3291 1.1 christos dump_node_to_stream 3292 1.1 christos (nodes_filename, node->nodename, output_stream, dump_subnodes); 3293 1.1 christos 3294 1.1 christos if (output_stream != stdout) 3295 1.1 christos fclose (output_stream); 3296 1.1 christos 3297 1.1 christos #if defined (VERBOSE_NODE_DUMPING) 3298 1.1 christos info_error ((char *) _("Done."), NULL, NULL); 3299 1.1 christos #endif /* VERBOSE_NODE_DUMPING */ 3300 1.1 christos } 3301 1.1 christos 3302 1.1 christos #if !defined (DEFAULT_INFO_PRINT_COMMAND) 3303 1.1 christos # define DEFAULT_INFO_PRINT_COMMAND "lpr" 3304 1.1 christos #endif /* !DEFAULT_INFO_PRINT_COMMAND */ 3305 1.1 christos 3306 1.1 christos DECLARE_INFO_COMMAND (info_print_node, 3307 1.1 christos _("Pipe the contents of this node through INFO_PRINT_COMMAND")) 3308 1.1 christos { 3309 1.1 christos print_node (window->node); 3310 1.1 christos } 3311 1.1 christos 3312 1.1 christos /* Print NODE on a printer piping it into INFO_PRINT_COMMAND. */ 3313 1.1 christos void 3314 1.1 christos print_node (NODE *node) 3315 1.1 christos { 3316 1.1 christos FILE *printer_pipe; 3317 1.1 christos char *print_command = getenv ("INFO_PRINT_COMMAND"); 3318 1.1 christos int piping = 0; 3319 1.1 christos 3320 1.1 christos if (!print_command || !*print_command) 3321 1.1 christos print_command = DEFAULT_INFO_PRINT_COMMAND; 3322 1.1 christos 3323 1.1 christos /* Note that on MS-DOS/MS-Windows, this MUST open the pipe in the 3324 1.1 christos (default) text mode, since the printer drivers there need to see 3325 1.1 christos DOS-style CRLF pairs at the end of each line. 3326 1.1 christos 3327 1.1 christos FIXME: if we are to support Mac-style text files, we might need 3328 1.1 christos to convert the text here. */ 3329 1.1 christos 3330 1.1 christos /* INFO_PRINT_COMMAND which says ">file" means write to that file. 3331 1.1 christos Presumably, the name of the file is the local printer device. */ 3332 1.1 christos if (*print_command == '>') 3333 1.1 christos printer_pipe = fopen (++print_command, "w"); 3334 1.1 christos else 3335 1.1 christos { 3336 1.1 christos printer_pipe = popen (print_command, "w"); 3337 1.1 christos piping = 1; 3338 1.1 christos } 3339 1.1 christos 3340 1.1 christos if (!printer_pipe) 3341 1.1 christos { 3342 1.1 christos info_error ((char *) _("Cannot open pipe to `%s'."), print_command, NULL); 3343 1.1 christos return; 3344 1.1 christos } 3345 1.1 christos 3346 1.1 christos #if defined (VERBOSE_NODE_DUMPING) 3347 1.1 christos /* Maybe we should print some information about the node being output. */ 3348 1.1 christos info_error ((char *) _("Printing node %s..."), node_printed_rep (node), NULL); 3349 1.1 christos #endif /* VERBOSE_NODE_DUMPING */ 3350 1.1 christos 3351 1.1 christos write_node_to_stream (node, printer_pipe); 3352 1.1 christos if (piping) 3353 1.1 christos pclose (printer_pipe); 3354 1.1 christos else 3355 1.1 christos fclose (printer_pipe); 3356 1.1 christos 3357 1.1 christos #if defined (VERBOSE_NODE_DUMPING) 3358 1.1 christos info_error ((char *) _("Done."), NULL, NULL); 3359 1.1 christos #endif /* VERBOSE_NODE_DUMPING */ 3360 1.1 christos } 3361 1.1 christos 3362 1.1 christos static void 3363 1.1 christos write_node_to_stream (NODE *node, FILE *stream) 3364 1.1 christos { 3365 1.1 christos fwrite (node->contents, 1, node->nodelen, stream); 3366 1.1 christos } 3367 1.1 christos 3368 1.1 christos /* **************************************************************** */ 3370 1.1 christos /* */ 3371 1.1 christos /* Info Searching Commands */ 3372 1.1 christos /* */ 3373 1.1 christos /* **************************************************************** */ 3374 1.1 christos 3375 1.1 christos /* Variable controlling the garbage collection of files briefly visited 3376 1.1 christos during searches. Such files are normally gc'ed, unless they were 3377 1.1 christos compressed to begin with. If this variable is non-zero, it says 3378 1.1 christos to gc even those file buffer contents which had to be uncompressed. */ 3379 1.1 christos int gc_compressed_files = 0; 3380 1.1 christos 3381 1.1 christos static void info_gc_file_buffers (void); 3382 1.1 christos static void info_search_1 (WINDOW *window, int count, 3383 1.1 christos unsigned char key, int case_sensitive, int ask_for_string); 3384 1.1 christos 3385 1.1 christos static char *search_string = (char *)NULL; 3386 1.1 christos static int search_string_size = 0; 3387 1.1 christos static int isearch_is_active = 0; 3388 1.1 christos 3389 1.1 christos static int last_search_direction = 0; 3390 1.1 christos static int last_search_case_sensitive = 0; 3391 1.1 christos 3392 1.1 christos /* Return the file buffer which belongs to WINDOW's node. */ 3393 1.1 christos FILE_BUFFER * 3394 1.1 christos file_buffer_of_window (WINDOW *window) 3395 1.1 christos { 3396 1.1 christos /* If this window has no node, then it has no file buffer. */ 3397 1.1 christos if (!window->node) 3398 1.1 christos return ((FILE_BUFFER *)NULL); 3399 1.1 christos 3400 1.1 christos if (window->node->parent) 3401 1.1 christos return (info_find_file (window->node->parent)); 3402 1.1 christos 3403 1.1 christos if (window->node->filename) 3404 1.1 christos return (info_find_file (window->node->filename)); 3405 1.1 christos 3406 1.1 christos return ((FILE_BUFFER *)NULL); 3407 1.1 christos } 3408 1.1 christos 3409 1.1 christos /* Search for STRING in NODE starting at START. Return -1 if the string 3410 1.1 christos was not found, or the location of the string if it was. If WINDOW is 3411 1.1 christos passed as non-null, set the window's node to be NODE, its point to be 3412 1.1 christos the found string, and readjust the window's pagetop. Final argument 3413 1.1 christos DIR says which direction to search in. If it is positive, search 3414 1.1 christos forward, else backwards. */ 3415 1.1 christos long 3416 1.1 christos info_search_in_node (char *string, NODE *node, long int start, 3417 1.1 christos WINDOW *window, int dir, int case_sensitive) 3418 1.1 christos { 3419 1.1 christos SEARCH_BINDING binding; 3420 1.1 christos long offset; 3421 1.1 christos 3422 1.1 christos binding.buffer = node->contents; 3423 1.1 christos binding.start = start; 3424 1.1 christos binding.end = node->nodelen; 3425 1.1 christos binding.flags = 0; 3426 1.1 christos if (!case_sensitive) 3427 1.1 christos binding.flags |= S_FoldCase; 3428 1.1 christos 3429 1.1 christos if (dir < 0) 3430 1.1 christos { 3431 1.1 christos binding.end = 0; 3432 1.1 christos binding.flags |= S_SkipDest; 3433 1.1 christos } 3434 1.1 christos 3435 1.1 christos if (binding.start < 0) 3436 1.1 christos return (-1); 3437 1.1 christos 3438 1.1 christos /* For incremental searches, we always wish to skip past the string. */ 3439 1.1 christos if (isearch_is_active) 3440 1.1 christos binding.flags |= S_SkipDest; 3441 1.1 christos 3442 1.1 christos offset = search (string, &binding); 3443 1.1 christos 3444 1.1 christos if (offset != -1 && window) 3445 1.1 christos { 3446 1.1 christos set_remembered_pagetop_and_point (window); 3447 1.1 christos if (window->node != node) 3448 1.1 christos window_set_node_of_window (window, node); 3449 1.1 christos window->point = offset; 3450 1.1 christos window_adjust_pagetop (window); 3451 1.1 christos } 3452 1.1 christos return (offset); 3453 1.1 christos } 3454 1.1 christos 3455 1.1 christos /* Search NODE, looking for the largest possible match of STRING. Start the 3456 1.1 christos search at START. Return the absolute position of the match, or -1, if 3457 1.1 christos no part of the string could be found. */ 3458 1.1 christos long 3459 1.1 christos info_target_search_node (NODE *node, char *string, long int start) 3460 1.1 christos { 3461 1.1 christos register int i; 3462 1.1 christos long offset = 0; 3463 1.1 christos char *target; 3464 1.1 christos 3465 1.1 christos target = xstrdup (string); 3466 1.1 christos i = strlen (target); 3467 1.1 christos 3468 1.1 christos /* Try repeatedly searching for this string while removing words from 3469 1.1 christos the end of it. */ 3470 1.1 christos while (i) 3471 1.1 christos { 3472 1.1 christos target[i] = '\0'; 3473 1.1 christos offset = info_search_in_node (target, node, start, (WINDOW *)NULL, 1, 0); 3474 1.1 christos 3475 1.1 christos if (offset != -1) 3476 1.1 christos break; 3477 1.1 christos 3478 1.1 christos /* Delete the last word from TARGET. */ 3479 1.1 christos for (; i && (!whitespace (target[i]) && (target[i] != ',')); i--); 3480 1.1 christos } 3481 1.1 christos free (target); 3482 1.1 christos return (offset); 3483 1.1 christos } 3484 1.1 christos 3485 1.1 christos /* Search for STRING starting in WINDOW at point. If the string is found 3486 1.1 christos in this node, set point to that position. Otherwise, get the file buffer 3487 1.1 christos associated with WINDOW's node, and search through each node in that file. 3488 1.1 christos If the search fails, return non-zero, else zero. Side-effect window 3489 1.1 christos leaving the node and point where the string was found current. */ 3490 1.1 christos static int 3491 1.1 christos info_search_internal (char *string, WINDOW *window, 3492 1.1 christos int dir, int case_sensitive) 3493 1.1 christos { 3494 1.1 christos register int i; 3495 1.1 christos FILE_BUFFER *file_buffer; 3496 1.1 christos char *initial_nodename; 3497 1.1 christos long ret, start = 0; 3498 1.1 christos 3499 1.1 christos file_buffer = file_buffer_of_window (window); 3500 1.1 christos initial_nodename = window->node->nodename; 3501 1.1 christos 3502 1.1 christos /* This used to begin from window->point, unless this was a repeated 3503 1.1 christos search command. But invoking search with an argument loses with 3504 1.1 christos that logic, since info_last_executed_command is then set to 3505 1.1 christos info_add_digit_to_numeric_arg. I think there's no sense in 3506 1.1 christos ``finding'' a string that is already under the cursor, anyway. */ 3507 1.1 christos ret = info_search_in_node 3508 1.1 christos (string, window->node, window->point + dir, window, dir, 3509 1.1 christos case_sensitive); 3510 1.1 christos 3511 1.1 christos if (ret != -1) 3512 1.1 christos { 3513 1.1 christos /* We won! */ 3514 1.1 christos if (!echo_area_is_active && !isearch_is_active) 3515 1.1 christos window_clear_echo_area (); 3516 1.1 christos return (0); 3517 1.1 christos } 3518 1.1 christos 3519 1.1 christos /* The string wasn't found in the current node. Search through the 3520 1.1 christos window's file buffer, iff the current node is not "*". */ 3521 1.1 christos if (!file_buffer || (strcmp (initial_nodename, "*") == 0)) 3522 1.1 christos return (-1); 3523 1.1 christos 3524 1.1 christos /* If this file has tags, search through every subfile, starting at 3525 1.1 christos this node's subfile and node. Otherwise, search through the 3526 1.1 christos file's node list. */ 3527 1.1 christos if (file_buffer->tags) 3528 1.1 christos { 3529 1.1 christos register int current_tag = 0, number_of_tags; 3530 1.1 christos char *last_subfile; 3531 1.1 christos TAG *tag; 3532 1.1 christos 3533 1.1 christos /* Find number of tags and current tag. */ 3534 1.1 christos last_subfile = (char *)NULL; 3535 1.1 christos for (i = 0; file_buffer->tags[i]; i++) 3536 1.1 christos if (strcmp (initial_nodename, file_buffer->tags[i]->nodename) == 0) 3537 1.1 christos { 3538 1.1 christos current_tag = i; 3539 1.1 christos last_subfile = file_buffer->tags[i]->filename; 3540 1.1 christos } 3541 1.1 christos 3542 1.1 christos number_of_tags = i; 3543 1.1 christos 3544 1.1 christos /* If there is no last_subfile, our tag wasn't found. */ 3545 1.1 christos if (!last_subfile) 3546 1.1 christos return (-1); 3547 1.1 christos 3548 1.1 christos /* Search through subsequent nodes, wrapping around to the top 3549 1.1 christos of the info file until we find the string or return to this 3550 1.1 christos window's node and point. */ 3551 1.1 christos while (1) 3552 1.1 christos { 3553 1.1 christos NODE *node; 3554 1.1 christos 3555 1.1 christos /* Allow C-g to quit the search, failing it if pressed. */ 3556 1.1 christos return_if_control_g (-1); 3557 1.1 christos 3558 1.1 christos /* Find the next tag that isn't an anchor. */ 3559 1.1 christos for (i = current_tag + dir; i != current_tag; i += dir) 3560 1.1 christos { 3561 1.1 christos if (i < 0) 3562 1.1 christos i = number_of_tags - 1; 3563 1.1 christos else if (i == number_of_tags) 3564 1.1 christos i = 0; 3565 1.1 christos 3566 1.1 christos tag = file_buffer->tags[i]; 3567 1.1 christos if (tag->nodelen != 0) 3568 1.1 christos break; 3569 1.1 christos } 3570 1.1 christos 3571 1.1 christos /* If we got past out starting point, bail out. */ 3572 1.1 christos if (i == current_tag) 3573 1.1 christos return (-1); 3574 1.1 christos current_tag = i; 3575 1.1 christos 3576 1.1 christos if (!echo_area_is_active && (last_subfile != tag->filename)) 3577 1.1 christos { 3578 1.1 christos window_message_in_echo_area 3579 1.1 christos ((char *) _("Searching subfile %s ..."), 3580 1.1 christos filename_non_directory (tag->filename), NULL); 3581 1.1 christos 3582 1.1 christos last_subfile = tag->filename; 3583 1.1 christos } 3584 1.1 christos 3585 1.1 christos node = info_get_node (file_buffer->filename, tag->nodename); 3586 1.1 christos 3587 1.1 christos if (!node) 3588 1.1 christos { 3589 1.1 christos /* If not doing i-search... */ 3590 1.1 christos if (!echo_area_is_active) 3591 1.1 christos { 3592 1.1 christos if (info_recent_file_error) 3593 1.1 christos info_error (info_recent_file_error, NULL, NULL); 3594 1.1 christos else 3595 1.1 christos info_error ((char *) msg_cant_file_node, 3596 1.1 christos filename_non_directory (file_buffer->filename), 3597 1.1 christos tag->nodename); 3598 1.1 christos } 3599 1.1 christos return (-1); 3600 1.1 christos } 3601 1.1 christos 3602 1.1 christos if (dir < 0) 3603 1.1 christos start = tag->nodelen; 3604 1.1 christos 3605 1.1 christos ret = 3606 1.1 christos info_search_in_node (string, node, start, window, dir, 3607 1.1 christos case_sensitive); 3608 1.1 christos 3609 1.1 christos /* Did we find the string in this node? */ 3610 1.1 christos if (ret != -1) 3611 1.1 christos { 3612 1.1 christos /* Yes! We win. */ 3613 1.1 christos remember_window_and_node (window, node); 3614 1.1 christos if (!echo_area_is_active) 3615 1.1 christos window_clear_echo_area (); 3616 1.1 christos return (0); 3617 1.1 christos } 3618 1.1 christos 3619 1.1 christos /* No. Free this node, and make sure that we haven't passed 3620 1.1 christos our starting point. */ 3621 1.1 christos free (node); 3622 1.1 christos 3623 1.1 christos if (strcmp (initial_nodename, tag->nodename) == 0) 3624 1.1 christos return (-1); 3625 1.1 christos } 3626 1.1 christos } 3627 1.1 christos return (-1); 3628 1.1 christos } 3629 1.1 christos 3630 1.1 christos DECLARE_INFO_COMMAND (info_search_case_sensitively, 3631 1.1 christos _("Read a string and search for it case-sensitively")) 3632 1.1 christos { 3633 1.1 christos last_search_direction = count > 0 ? 1 : -1; 3634 1.1 christos last_search_case_sensitive = 1; 3635 1.1 christos info_search_1 (window, count, key, 1, 1); 3636 1.1 christos } 3637 1.1 christos 3638 1.1 christos DECLARE_INFO_COMMAND (info_search, _("Read a string and search for it")) 3639 1.1 christos { 3640 1.1 christos last_search_direction = count > 0 ? 1 : -1; 3641 1.1 christos last_search_case_sensitive = 0; 3642 1.1 christos info_search_1 (window, count, key, 0, 1); 3643 1.1 christos } 3644 1.1 christos 3645 1.1 christos DECLARE_INFO_COMMAND (info_search_backward, 3646 1.1 christos _("Read a string and search backward for it")) 3647 1.1 christos { 3648 1.1 christos last_search_direction = count > 0 ? -1 : 1; 3649 1.1 christos last_search_case_sensitive = 0; 3650 1.1 christos info_search_1 (window, -count, key, 0, 1); 3651 1.1 christos } 3652 1.1 christos 3653 1.1 christos static void 3654 1.1 christos info_search_1 (WINDOW *window, int count, unsigned char key, 3655 1.1 christos int case_sensitive, int ask_for_string) 3656 1.1 christos { 3657 1.1 christos char *line, *prompt; 3658 1.1 christos int result, old_pagetop; 3659 1.1 christos int direction; 3660 1.1 christos 3661 1.1 christos if (count < 0) 3662 1.1 christos { 3663 1.1 christos direction = -1; 3664 1.1 christos count = -count; 3665 1.1 christos } 3666 1.1 christos else 3667 1.1 christos { 3668 1.1 christos direction = 1; 3669 1.1 christos if (count == 0) 3670 1.1 christos count = 1; /* for backward compatibility */ 3671 1.1 christos } 3672 1.1 christos 3673 1.1 christos /* Read a string from the user, defaulting the search to SEARCH_STRING. */ 3674 1.1 christos if (!search_string) 3675 1.1 christos { 3676 1.1 christos search_string = (char *)xmalloc (search_string_size = 100); 3677 1.1 christos search_string[0] = '\0'; 3678 1.1 christos } 3679 1.1 christos 3680 1.1 christos if (ask_for_string) 3681 1.1 christos { 3682 1.1 christos prompt = (char *)xmalloc (strlen (_("%s%sfor string [%s]: ")) 3683 1.1 christos + strlen (_("Search backward")) 3684 1.1 christos + strlen (_("Search")) 3685 1.1 christos + strlen (_(" case-sensitively ")) 3686 1.1 christos + strlen (_(" ")) 3687 1.1 christos + strlen (search_string)); 3688 1.1 christos 3689 1.1 christos sprintf (prompt, _("%s%sfor string [%s]: "), 3690 1.1 christos direction < 0 ? _("Search backward") : _("Search"), 3691 1.1 christos case_sensitive ? _(" case-sensitively ") : _(" "), 3692 1.1 christos search_string); 3693 1.1 christos 3694 1.1 christos line = info_read_in_echo_area (window, prompt); 3695 1.1 christos free (prompt); 3696 1.1 christos 3697 1.1 christos if (!line) 3698 1.1 christos { 3699 1.1 christos info_abort_key (window, 0, 0); 3700 1.1 christos return; 3701 1.1 christos } 3702 1.1 christos 3703 1.1 christos if (*line) 3704 1.1 christos { 3705 1.1 christos if (strlen (line) + 1 > (unsigned int) search_string_size) 3706 1.1 christos search_string = (char *) xrealloc 3707 1.1 christos (search_string, (search_string_size += 50 + strlen (line))); 3708 1.1 christos 3709 1.1 christos strcpy (search_string, line); 3710 1.1 christos free (line); 3711 1.1 christos } 3712 1.1 christos } 3713 1.1 christos 3714 1.1 christos /* If the search string includes upper-case letters, make the search 3715 1.1 christos case-sensitive. */ 3716 1.1 christos if (case_sensitive == 0) 3717 1.1 christos for (line = search_string; *line; line++) 3718 1.1 christos if (isupper (*line)) 3719 1.1 christos { 3720 1.1 christos case_sensitive = 1; 3721 1.1 christos break; 3722 1.1 christos } 3723 1.1 christos 3724 1.1 christos old_pagetop = active_window->pagetop; 3725 1.1 christos for (result = 0; result == 0 && count--; ) 3726 1.1 christos result = info_search_internal (search_string, 3727 1.1 christos active_window, direction, case_sensitive); 3728 1.1 christos 3729 1.1 christos if (result != 0 && !info_error_was_printed) 3730 1.1 christos info_error ((char *) _("Search failed."), NULL, NULL); 3731 1.1 christos else if (old_pagetop != active_window->pagetop) 3732 1.1 christos { 3733 1.1 christos int new_pagetop; 3734 1.1 christos 3735 1.1 christos new_pagetop = active_window->pagetop; 3736 1.1 christos active_window->pagetop = old_pagetop; 3737 1.1 christos set_window_pagetop (active_window, new_pagetop); 3738 1.1 christos if (auto_footnotes_p) 3739 1.1 christos info_get_or_remove_footnotes (active_window); 3740 1.1 christos } 3741 1.1 christos 3742 1.1 christos /* Perhaps free the unreferenced file buffers that were searched, but 3743 1.1 christos not retained. */ 3744 1.1 christos info_gc_file_buffers (); 3745 1.1 christos } 3746 1.1 christos 3747 1.1 christos DECLARE_INFO_COMMAND (info_search_next, 3748 1.1 christos _("Repeat last search in the same direction")) 3749 1.1 christos { 3750 1.1 christos if (!last_search_direction) 3751 1.1 christos info_error ((char *) _("No previous search string"), NULL, NULL); 3752 1.1 christos else 3753 1.1 christos info_search_1 (window, last_search_direction * count, 3754 1.1 christos key, last_search_case_sensitive, 0); 3755 1.1 christos } 3756 1.1 christos 3757 1.1 christos DECLARE_INFO_COMMAND (info_search_previous, 3758 1.1 christos _("Repeat last search in the reverse direction")) 3759 1.1 christos { 3760 1.1 christos if (!last_search_direction) 3761 1.1 christos info_error ((char *) _("No previous search string"), NULL, NULL); 3762 1.1 christos else 3763 1.1 christos info_search_1 (window, -last_search_direction * count, 3764 1.1 christos key, last_search_case_sensitive, 0); 3765 1.1 christos } 3766 1.1 christos 3767 1.1 christos /* **************************************************************** */ 3768 1.1 christos /* */ 3769 1.1 christos /* Incremental Searching */ 3770 1.1 christos /* */ 3771 1.1 christos /* **************************************************************** */ 3772 1.1 christos 3773 1.1 christos static void incremental_search (WINDOW *window, int count, 3774 1.1 christos unsigned char ignore); 3775 1.1 christos 3776 1.1 christos DECLARE_INFO_COMMAND (isearch_forward, 3777 1.1 christos _("Search interactively for a string as you type it")) 3778 1.1 christos { 3779 1.1 christos incremental_search (window, count, key); 3780 1.1 christos } 3781 1.1 christos 3782 1.1 christos DECLARE_INFO_COMMAND (isearch_backward, 3783 1.1 christos _("Search interactively for a string as you type it")) 3784 1.1 christos { 3785 1.1 christos incremental_search (window, -count, key); 3786 1.1 christos } 3787 1.1 christos 3788 1.1 christos /* Incrementally search for a string as it is typed. */ 3789 1.1 christos /* The last accepted incremental search string. */ 3790 1.1 christos static char *last_isearch_accepted = (char *)NULL; 3791 1.1 christos 3792 1.1 christos /* The current incremental search string. */ 3793 1.1 christos static char *isearch_string = (char *)NULL; 3794 1.1 christos static int isearch_string_index = 0; 3795 1.1 christos static int isearch_string_size = 0; 3796 1.1 christos static unsigned char isearch_terminate_search_key = ESC; 3797 1.1 christos 3798 1.1 christos /* Array of search states. */ 3799 1.1 christos static SEARCH_STATE **isearch_states = (SEARCH_STATE **)NULL; 3800 1.1 christos static int isearch_states_index = 0; 3801 1.1 christos static int isearch_states_slots = 0; 3802 1.1 christos 3803 1.1 christos /* Push the state of this search. */ 3804 1.1 christos static void 3805 1.1 christos push_isearch (WINDOW *window, int search_index, int direction, int failing) 3806 1.1 christos { 3807 1.1 christos SEARCH_STATE *state; 3808 1.1 christos 3809 1.1 christos state = (SEARCH_STATE *)xmalloc (sizeof (SEARCH_STATE)); 3810 1.1 christos window_get_state (window, state); 3811 1.1 christos state->search_index = search_index; 3812 1.1 christos state->direction = direction; 3813 1.1 christos state->failing = failing; 3814 1.1 christos 3815 1.1 christos add_pointer_to_array (state, isearch_states_index, isearch_states, 3816 1.1 christos isearch_states_slots, 20, SEARCH_STATE *); 3817 1.1 christos } 3818 1.1 christos 3819 1.1 christos /* Pop the state of this search to WINDOW, SEARCH_INDEX, and DIRECTION. */ 3820 1.1 christos static void 3821 1.1 christos pop_isearch (WINDOW *window, int *search_index, int *direction, int *failing) 3822 1.1 christos { 3823 1.1 christos SEARCH_STATE *state; 3824 1.1 christos 3825 1.1 christos if (isearch_states_index) 3826 1.1 christos { 3827 1.1 christos isearch_states_index--; 3828 1.1 christos state = isearch_states[isearch_states_index]; 3829 1.1 christos window_set_state (window, state); 3830 1.1 christos *search_index = state->search_index; 3831 1.1 christos *direction = state->direction; 3832 1.1 christos *failing = state->failing; 3833 1.1 christos 3834 1.1 christos free (state); 3835 1.1 christos isearch_states[isearch_states_index] = (SEARCH_STATE *)NULL; 3836 1.1 christos } 3837 1.1 christos } 3838 1.1 christos 3839 1.1 christos /* Free the memory used by isearch_states. */ 3840 1.1 christos static void 3841 1.1 christos free_isearch_states (void) 3842 1.1 christos { 3843 1.1 christos register int i; 3844 1.1 christos 3845 1.1 christos for (i = 0; i < isearch_states_index; i++) 3846 1.1 christos { 3847 1.1 christos free (isearch_states[i]); 3848 1.1 christos isearch_states[i] = (SEARCH_STATE *)NULL; 3849 1.1 christos } 3850 1.1 christos isearch_states_index = 0; 3851 1.1 christos } 3852 1.1 christos 3853 1.1 christos /* Display the current search in the echo area. */ 3854 1.1 christos static void 3855 1.1 christos show_isearch_prompt (int dir, unsigned char *string, int failing_p) 3856 1.1 christos { 3857 1.1 christos register int i; 3858 1.1 christos const char *prefix; 3859 1.1 christos char *prompt, *p_rep; 3860 1.1 christos unsigned int prompt_len, p_rep_index, p_rep_size; 3861 1.1 christos 3862 1.1 christos if (dir < 0) 3863 1.1 christos prefix = _("I-search backward: "); 3864 1.1 christos else 3865 1.1 christos prefix = _("I-search: "); 3866 1.1 christos 3867 1.1 christos p_rep_index = p_rep_size = 0; 3868 1.1 christos p_rep = (char *)NULL; 3869 1.1 christos for (i = 0; string[i]; i++) 3870 1.1 christos { 3871 1.1 christos char *rep; 3872 1.1 christos 3873 1.1 christos switch (string[i]) 3874 1.1 christos { 3875 1.1 christos case ' ': rep = " "; break; 3876 1.1 christos case LFD: rep = "\\n"; break; 3877 1.1 christos case TAB: rep = "\\t"; break; 3878 1.1 christos default: 3879 1.1 christos rep = pretty_keyname (string[i]); 3880 1.1 christos } 3881 1.1 christos if ((p_rep_index + strlen (rep) + 1) >= p_rep_size) 3882 1.1 christos p_rep = (char *)xrealloc (p_rep, p_rep_size += 100); 3883 1.1 christos 3884 1.1 christos strcpy (p_rep + p_rep_index, rep); 3885 1.1 christos p_rep_index += strlen (rep); 3886 1.1 christos } 3887 1.1 christos 3888 1.1 christos prompt_len = strlen (prefix) + p_rep_index + 1; 3889 1.1 christos if (failing_p) 3890 1.1 christos prompt_len += strlen (_("Failing ")); 3891 1.1 christos prompt = xmalloc (prompt_len); 3892 1.1 christos sprintf (prompt, "%s%s%s", failing_p ? _("Failing ") : "", prefix, 3893 1.1 christos p_rep ? p_rep : ""); 3894 1.1 christos 3895 1.1 christos window_message_in_echo_area ("%s", prompt, NULL); 3896 1.1 christos maybe_free (p_rep); 3897 1.1 christos free (prompt); 3898 1.1 christos display_cursor_at_point (active_window); 3899 1.1 christos } 3900 1.1 christos 3901 1.1 christos static void 3902 1.1 christos incremental_search (WINDOW *window, int count, unsigned char ignore) 3903 1.1 christos { 3904 1.1 christos unsigned char key; 3905 1.1 christos int last_search_result, search_result, dir; 3906 1.1 christos SEARCH_STATE mystate, orig_state; 3907 1.1 christos char *p; 3908 1.1 christos int case_sensitive = 0; 3909 1.1 christos 3910 1.1 christos if (count < 0) 3911 1.1 christos dir = -1; 3912 1.1 christos else 3913 1.1 christos dir = 1; 3914 1.1 christos 3915 1.1 christos last_search_result = search_result = 0; 3916 1.1 christos 3917 1.1 christos window_get_state (window, &orig_state); 3918 1.1 christos 3919 1.1 christos isearch_string_index = 0; 3920 1.1 christos if (!isearch_string_size) 3921 1.1 christos isearch_string = (char *)xmalloc (isearch_string_size = 50); 3922 1.1 christos 3923 1.1 christos /* Show the search string in the echo area. */ 3924 1.1 christos isearch_string[isearch_string_index] = '\0'; 3925 1.1 christos show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result); 3926 1.1 christos 3927 1.1 christos isearch_is_active = 1; 3928 1.1 christos 3929 1.1 christos while (isearch_is_active) 3930 1.1 christos { 3931 1.1 christos VFunction *func = (VFunction *)NULL; 3932 1.1 christos int quoted = 0; 3933 1.1 christos 3934 1.1 christos /* If a recent display was interrupted, then do the redisplay now if 3935 1.1 christos it is convenient. */ 3936 1.1 christos if (!info_any_buffered_input_p () && display_was_interrupted_p) 3937 1.1 christos { 3938 1.1 christos display_update_one_window (window); 3939 1.1 christos display_cursor_at_point (active_window); 3940 1.1 christos } 3941 1.1 christos 3942 1.1 christos /* Read a character and dispatch on it. */ 3943 1.1 christos key = info_get_input_char (); 3944 1.1 christos window_get_state (window, &mystate); 3945 1.1 christos 3946 1.1 christos if (key == DEL || key == Control ('h')) 3947 1.1 christos { 3948 1.1 christos /* User wants to delete one level of search? */ 3949 1.1 christos if (!isearch_states_index) 3950 1.1 christos { 3951 1.1 christos terminal_ring_bell (); 3952 1.1 christos continue; 3953 1.1 christos } 3954 1.1 christos else 3955 1.1 christos { 3956 1.1 christos pop_isearch 3957 1.1 christos (window, &isearch_string_index, &dir, &search_result); 3958 1.1 christos isearch_string[isearch_string_index] = '\0'; 3959 1.1 christos show_isearch_prompt (dir, (unsigned char *) isearch_string, 3960 1.1 christos search_result); 3961 1.1 christos goto after_search; 3962 1.1 christos } 3963 1.1 christos } 3964 1.1 christos else if (key == Control ('q')) 3965 1.1 christos { 3966 1.1 christos key = info_get_input_char (); 3967 1.1 christos quoted = 1; 3968 1.1 christos } 3969 1.1 christos 3970 1.1 christos /* We are about to search again, or quit. Save the current search. */ 3971 1.1 christos push_isearch (window, isearch_string_index, dir, search_result); 3972 1.1 christos 3973 1.1 christos if (quoted) 3974 1.1 christos goto insert_and_search; 3975 1.1 christos 3976 1.1 christos if (!Meta_p (key) || key > 32) 3977 1.1 christos { 3978 1.1 christos /* If this key is not a keymap, get its associated function, 3979 1.1 christos if any. If it is a keymap, then it's probably ESC from an 3980 1.1 christos arrow key, and we handle that case below. */ 3981 1.1 christos char type = window->keymap[key].type; 3982 1.1 christos func = type == ISFUNC 3983 1.1 christos ? InfoFunction(window->keymap[key].function) 3984 1.1 christos : NULL; /* function member is a Keymap if ISKMAP */ 3985 1.1 christos 3986 1.1 christos if (isprint (key) || (type == ISFUNC && func == NULL)) 3987 1.1 christos { 3988 1.1 christos insert_and_search: 3989 1.1 christos 3990 1.1 christos if (isearch_string_index + 2 >= isearch_string_size) 3991 1.1 christos isearch_string = (char *)xrealloc 3992 1.1 christos (isearch_string, isearch_string_size += 100); 3993 1.1 christos 3994 1.1 christos isearch_string[isearch_string_index++] = key; 3995 1.1 christos isearch_string[isearch_string_index] = '\0'; 3996 1.1 christos goto search_now; 3997 1.1 christos } 3998 1.1 christos else if (func == (VFunction *) isearch_forward 3999 1.1 christos || func == (VFunction *) isearch_backward) 4000 1.1 christos { 4001 1.1 christos /* If this key invokes an incremental search, then this 4002 1.1 christos means that we will either search again in the same 4003 1.1 christos direction, search again in the reverse direction, or 4004 1.1 christos insert the last search string that was accepted through 4005 1.1 christos incremental searching. */ 4006 1.1 christos if ((func == (VFunction *) isearch_forward && dir > 0) || 4007 1.1 christos (func == (VFunction *) isearch_backward && dir < 0)) 4008 1.1 christos { 4009 1.1 christos /* If the user has typed no characters, then insert the 4010 1.1 christos last successful search into the current search string. */ 4011 1.1 christos if (isearch_string_index == 0) 4012 1.1 christos { 4013 1.1 christos /* Of course, there must be something to insert. */ 4014 1.1 christos if (last_isearch_accepted) 4015 1.1 christos { 4016 1.1 christos if (strlen ((char *) last_isearch_accepted) + 1 4017 1.1 christos >= (unsigned int) isearch_string_size) 4018 1.1 christos isearch_string = (char *) 4019 1.1 christos xrealloc (isearch_string, 4020 1.1 christos isearch_string_size += 10 + 4021 1.1 christos strlen (last_isearch_accepted)); 4022 1.1 christos strcpy (isearch_string, last_isearch_accepted); 4023 1.1 christos isearch_string_index = strlen (isearch_string); 4024 1.1 christos goto search_now; 4025 1.1 christos } 4026 1.1 christos else 4027 1.1 christos continue; 4028 1.1 christos } 4029 1.1 christos else 4030 1.1 christos { 4031 1.1 christos /* Search again in the same direction. This means start 4032 1.1 christos from a new place if the last search was successful. */ 4033 1.1 christos if (search_result == 0) 4034 1.1 christos window->point += dir; 4035 1.1 christos } 4036 1.1 christos } 4037 1.1 christos else 4038 1.1 christos { 4039 1.1 christos /* Reverse the direction of the search. */ 4040 1.1 christos dir = -dir; 4041 1.1 christos } 4042 1.1 christos } 4043 1.1 christos else if (func == (VFunction *) info_abort_key) 4044 1.1 christos { 4045 1.1 christos /* If C-g pressed, and the search is failing, pop the search 4046 1.1 christos stack back to the last unfailed search. */ 4047 1.1 christos if (isearch_states_index && (search_result != 0)) 4048 1.1 christos { 4049 1.1 christos terminal_ring_bell (); 4050 1.1 christos while (isearch_states_index && (search_result != 0)) 4051 1.1 christos pop_isearch 4052 1.1 christos (window, &isearch_string_index, &dir, &search_result); 4053 1.1 christos isearch_string[isearch_string_index] = '\0'; 4054 1.1 christos show_isearch_prompt (dir, (unsigned char *) isearch_string, 4055 1.1 christos search_result); 4056 1.1 christos continue; 4057 1.1 christos } 4058 1.1 christos else 4059 1.1 christos goto exit_search; 4060 1.1 christos } 4061 1.1 christos else 4062 1.1 christos goto exit_search; 4063 1.1 christos } 4064 1.1 christos else 4065 1.1 christos { 4066 1.1 christos exit_search: 4067 1.1 christos /* The character is not printable, or it has a function which is 4068 1.1 christos non-null. Exit the search, remembering the search string. If 4069 1.1 christos the key is not the same as the isearch_terminate_search_key, 4070 1.1 christos then push it into pending input. */ 4071 1.1 christos if (isearch_string_index && func != (VFunction *) info_abort_key) 4072 1.1 christos { 4073 1.1 christos maybe_free (last_isearch_accepted); 4074 1.1 christos last_isearch_accepted = xstrdup (isearch_string); 4075 1.1 christos } 4076 1.1 christos 4077 1.1 christos /* If the key is the isearch_terminate_search_key, but some buffered 4078 1.1 christos input is pending, it is almost invariably because the ESC key is 4079 1.1 christos actually the beginning of an escape sequence, like in case they 4080 1.1 christos pressed an arrow key. So don't gobble the ESC key, push it back 4081 1.1 christos into pending input. */ 4082 1.1 christos /* FIXME: this seems like a kludge! We need a more reliable 4083 1.1 christos mechanism to know when ESC is a separate key and when it is 4084 1.1 christos part of an escape sequence. */ 4085 1.1 christos if (key != RET /* Emacs addicts want RET to get lost */ 4086 1.1 christos && (key != isearch_terminate_search_key 4087 1.1 christos || info_any_buffered_input_p ())) 4088 1.1 christos info_set_pending_input (key); 4089 1.1 christos 4090 1.1 christos if (func == (VFunction *) info_abort_key) 4091 1.1 christos { 4092 1.1 christos if (isearch_states_index) 4093 1.1 christos window_set_state (window, &orig_state); 4094 1.1 christos } 4095 1.1 christos 4096 1.1 christos if (!echo_area_is_active) 4097 1.1 christos window_clear_echo_area (); 4098 1.1 christos 4099 1.1 christos if (auto_footnotes_p) 4100 1.1 christos info_get_or_remove_footnotes (active_window); 4101 1.1 christos 4102 1.1 christos isearch_is_active = 0; 4103 1.1 christos continue; 4104 1.1 christos } 4105 1.1 christos 4106 1.1 christos /* Search for the contents of isearch_string. */ 4107 1.1 christos search_now: 4108 1.1 christos show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result); 4109 1.1 christos 4110 1.1 christos /* If the search string includes upper-case letters, make the 4111 1.1 christos search case-sensitive. */ 4112 1.1 christos for (p = isearch_string; *p; p++) 4113 1.1 christos if (isupper (*p)) 4114 1.1 christos { 4115 1.1 christos case_sensitive = 1; 4116 1.1 christos break; 4117 1.1 christos } 4118 1.1 christos 4119 1.1 christos 4120 1.1 christos if (search_result == 0) 4121 1.1 christos { 4122 1.1 christos /* Check to see if the current search string is right here. If 4123 1.1 christos we are looking at it, then don't bother calling the search 4124 1.1 christos function. */ 4125 1.1 christos if (((dir < 0) && 4126 1.1 christos ((case_sensitive ? strncmp : strncasecmp) 4127 1.1 christos (window->node->contents + window->point, 4128 1.1 christos isearch_string, isearch_string_index) == 0)) || 4129 1.1 christos ((dir > 0) && 4130 1.1 christos ((window->point - isearch_string_index) >= 0) && 4131 1.1 christos ((case_sensitive ? strncmp : strncasecmp) 4132 1.1 christos (window->node->contents + 4133 1.1 christos (window->point - (isearch_string_index - 1)), 4134 1.1 christos isearch_string, isearch_string_index) == 0))) 4135 1.1 christos { 4136 1.1 christos if (dir > 0) 4137 1.1 christos window->point++; 4138 1.1 christos } 4139 1.1 christos else 4140 1.1 christos search_result = info_search_internal (isearch_string, 4141 1.1 christos window, dir, case_sensitive); 4142 1.1 christos } 4143 1.1 christos 4144 1.1 christos /* If this search failed, and we didn't already have a failed search, 4145 1.1 christos then ring the terminal bell. */ 4146 1.1 christos if (search_result != 0 && last_search_result == 0) 4147 1.1 christos terminal_ring_bell (); 4148 1.1 christos 4149 1.1 christos after_search: 4150 1.1 christos show_isearch_prompt (dir, (unsigned char *) isearch_string, search_result); 4151 1.1 christos 4152 1.1 christos if (search_result == 0) 4153 1.1 christos { 4154 1.1 christos if ((mystate.node == window->node) && 4155 1.1 christos (mystate.pagetop != window->pagetop)) 4156 1.1 christos { 4157 1.1 christos int newtop = window->pagetop; 4158 1.1 christos window->pagetop = mystate.pagetop; 4159 1.1 christos set_window_pagetop (window, newtop); 4160 1.1 christos } 4161 1.1 christos display_update_one_window (window); 4162 1.1 christos display_cursor_at_point (window); 4163 1.1 christos } 4164 1.1 christos 4165 1.1 christos last_search_result = search_result; 4166 1.1 christos } 4167 1.1 christos 4168 1.1 christos /* Free the memory used to remember each search state. */ 4169 1.1 christos free_isearch_states (); 4170 1.1 christos 4171 1.1 christos /* Perhaps GC some file buffers. */ 4172 1.1 christos info_gc_file_buffers (); 4173 1.1 christos 4174 1.1 christos /* After searching, leave the window in the correct state. */ 4175 1.1 christos if (!echo_area_is_active) 4176 1.1 christos window_clear_echo_area (); 4177 1.1 christos } 4178 1.1 christos 4179 1.1 christos /* GC some file buffers. A file buffer can be gc-ed if there we have 4180 1.1 christos no nodes in INFO_WINDOWS that reference this file buffer's contents. 4181 1.1 christos Garbage collecting a file buffer means to free the file buffers 4182 1.1 christos contents. */ 4183 1.1 christos static void 4184 1.1 christos info_gc_file_buffers (void) 4185 1.1 christos { 4186 1.1 christos register int fb_index, iw_index, i; 4187 1.1 christos register FILE_BUFFER *fb; 4188 1.1 christos register INFO_WINDOW *iw; 4189 1.1 christos 4190 1.1 christos if (!info_loaded_files) 4191 1.1 christos return; 4192 1.1 christos 4193 1.1 christos for (fb_index = 0; (fb = info_loaded_files[fb_index]); fb_index++) 4194 1.1 christos { 4195 1.1 christos int fb_referenced_p = 0; 4196 1.1 christos 4197 1.1 christos /* If already gc-ed, do nothing. */ 4198 1.1 christos if (!fb->contents) 4199 1.1 christos continue; 4200 1.1 christos 4201 1.1 christos /* If this file had to be uncompressed, check to see if we should 4202 1.1 christos gc it. This means that the user-variable "gc-compressed-files" 4203 1.1 christos is non-zero. */ 4204 1.1 christos if ((fb->flags & N_IsCompressed) && !gc_compressed_files) 4205 1.1 christos continue; 4206 1.1 christos 4207 1.1 christos /* If this file's contents are not gc-able, move on. */ 4208 1.1 christos if (fb->flags & N_CannotGC) 4209 1.1 christos continue; 4210 1.1 christos 4211 1.1 christos /* Check each INFO_WINDOW to see if it has any nodes which reference 4212 1.1 christos this file. */ 4213 1.1 christos for (iw_index = 0; (iw = info_windows[iw_index]); iw_index++) 4214 1.1 christos { 4215 1.1 christos for (i = 0; iw->nodes && iw->nodes[i]; i++) 4216 1.1 christos { 4217 1.1 christos if ((FILENAME_CMP (fb->fullpath, iw->nodes[i]->filename) == 0) || 4218 1.1 christos (FILENAME_CMP (fb->filename, iw->nodes[i]->filename) == 0)) 4219 1.1 christos { 4220 1.1 christos fb_referenced_p = 1; 4221 1.1 christos break; 4222 1.1 christos } 4223 1.1 christos } 4224 1.1 christos } 4225 1.1 christos 4226 1.1 christos /* If this file buffer wasn't referenced, free its contents. */ 4227 1.1 christos if (!fb_referenced_p) 4228 1.1 christos { 4229 1.1 christos free (fb->contents); 4230 1.1 christos fb->contents = (char *)NULL; 4231 1.1 christos } 4232 1.1 christos } 4233 1.1 christos } 4234 1.1 christos 4235 1.1 christos /* **************************************************************** */ 4237 1.1 christos /* */ 4238 1.1 christos /* Traversing and Selecting References */ 4239 1.1 christos /* */ 4240 1.1 christos /* **************************************************************** */ 4241 1.1 christos 4242 1.1 christos /* Move to the next or previous cross reference in this node. */ 4243 1.1 christos static void 4244 1.1 christos info_move_to_xref (WINDOW *window, int count, unsigned char key, int dir) 4245 1.1 christos { 4246 1.1 christos long firstmenu, firstxref; 4247 1.1 christos long nextmenu, nextxref; 4248 1.1 christos long placement = -1; 4249 1.1 christos long start = 0; 4250 1.1 christos NODE *node = window->node; 4251 1.1 christos 4252 1.1 christos if (dir < 0) 4253 1.1 christos start = node->nodelen; 4254 1.1 christos 4255 1.1 christos /* This search is only allowed to fail if there is no menu or cross 4256 1.1 christos reference in the current node. Otherwise, the first menu or xref 4257 1.1 christos found is moved to. */ 4258 1.1 christos 4259 1.1 christos firstmenu = info_search_in_node 4260 1.1 christos (INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir, 0); 4261 1.1 christos 4262 1.1 christos /* FIRSTMENU may point directly to the line defining the menu. Skip that 4263 1.1 christos and go directly to the first item. */ 4264 1.1 christos 4265 1.1 christos if (firstmenu != -1) 4266 1.1 christos { 4267 1.1 christos char *text = node->contents + firstmenu; 4268 1.1 christos 4269 1.1 christos if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0) 4270 1.1 christos firstmenu = info_search_in_node 4271 1.1 christos (INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, (WINDOW *)NULL, dir, 0); 4272 1.1 christos } 4273 1.1 christos 4274 1.1 christos firstxref = 4275 1.1 christos info_search_in_node (INFO_XREF_LABEL, node, start, (WINDOW *)NULL, dir, 0); 4276 1.1 christos 4277 1.1 christos #if defined (HANDLE_MAN_PAGES) 4278 1.1 christos if ((firstxref == -1) && (node->flags & N_IsManPage)) 4279 1.1 christos { 4280 1.1 christos firstxref = locate_manpage_xref (node, start, dir); 4281 1.1 christos } 4282 1.1 christos #endif /* HANDLE_MAN_PAGES */ 4283 1.1 christos 4284 1.1 christos if (firstmenu == -1 && firstxref == -1) 4285 1.1 christos { 4286 1.1 christos info_error ((char *) msg_no_xref_node, NULL, NULL); 4287 1.1 christos return; 4288 1.1 christos } 4289 1.1 christos 4290 1.1 christos /* There is at least one cross reference or menu entry in this node. 4291 1.1 christos Try hard to find the next available one. */ 4292 1.1 christos 4293 1.1 christos nextmenu = info_search_in_node 4294 1.1 christos (INFO_MENU_ENTRY_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0); 4295 1.1 christos 4296 1.1 christos nextxref = info_search_in_node 4297 1.1 christos (INFO_XREF_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0); 4298 1.1 christos 4299 1.1 christos #if defined (HANDLE_MAN_PAGES) 4300 1.1 christos if ((nextxref == -1) && (node->flags & N_IsManPage) && (firstxref != -1)) 4301 1.1 christos nextxref = locate_manpage_xref (node, window->point + dir, dir); 4302 1.1 christos #endif /* HANDLE_MAN_PAGES */ 4303 1.1 christos 4304 1.1 christos /* Ignore "Menu:" as a menu item. */ 4305 1.1 christos if (nextmenu != -1) 4306 1.1 christos { 4307 1.1 christos char *text = node->contents + nextmenu; 4308 1.1 christos 4309 1.1 christos if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0) 4310 1.1 christos nextmenu = info_search_in_node 4311 1.1 christos (INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, (WINDOW *)NULL, dir, 0); 4312 1.1 christos } 4313 1.1 christos 4314 1.1 christos /* If there is both a next menu entry, and a next xref entry, choose the 4315 1.1 christos one which occurs first. Otherwise, select the one which actually 4316 1.1 christos appears in this node following point. */ 4317 1.1 christos if (nextmenu != -1 && nextxref != -1) 4318 1.1 christos { 4319 1.1 christos if (((dir == 1) && (nextmenu < nextxref)) || 4320 1.1 christos ((dir == -1) && (nextmenu > nextxref))) 4321 1.1 christos placement = nextmenu + 1; 4322 1.1 christos else 4323 1.1 christos placement = nextxref; 4324 1.1 christos } 4325 1.1 christos else if (nextmenu != -1) 4326 1.1 christos placement = nextmenu + 1; 4327 1.1 christos else if (nextxref != -1) 4328 1.1 christos placement = nextxref; 4329 1.1 christos 4330 1.1 christos /* If there was neither a menu or xref entry appearing in this node after 4331 1.1 christos point, choose the first menu or xref entry appearing in this node. */ 4332 1.1 christos if (placement == -1) 4333 1.1 christos { 4334 1.1 christos if (firstmenu != -1 && firstxref != -1) 4335 1.1 christos { 4336 1.1 christos if (((dir == 1) && (firstmenu < firstxref)) || 4337 1.1 christos ((dir == -1) && (firstmenu > firstxref))) 4338 1.1 christos placement = firstmenu + 1; 4339 1.1 christos else 4340 1.1 christos placement = firstxref; 4341 1.1 christos } 4342 1.1 christos else if (firstmenu != -1) 4343 1.1 christos placement = firstmenu + 1; 4344 1.1 christos else 4345 1.1 christos placement = firstxref; 4346 1.1 christos } 4347 1.1 christos window->point = placement; 4348 1.1 christos window_adjust_pagetop (window); 4349 1.1 christos window->flags |= W_UpdateWindow; 4350 1.1 christos } 4351 1.1 christos 4352 1.1 christos DECLARE_INFO_COMMAND (info_move_to_prev_xref, 4353 1.1 christos _("Move to the previous cross reference")) 4354 1.1 christos { 4355 1.1 christos if (count < 0) 4356 1.1 christos info_move_to_prev_xref (window, -count, key); 4357 1.1 christos else 4358 1.1 christos info_move_to_xref (window, count, key, -1); 4359 1.1 christos } 4360 1.1 christos 4361 1.1 christos DECLARE_INFO_COMMAND (info_move_to_next_xref, 4362 1.1 christos _("Move to the next cross reference")) 4363 1.1 christos { 4364 1.1 christos if (count < 0) 4365 1.1 christos info_move_to_next_xref (window, -count, key); 4366 1.1 christos else 4367 1.1 christos info_move_to_xref (window, count, key, 1); 4368 1.1 christos } 4369 1.1 christos 4370 1.1 christos /* Select the menu item or reference that appears on this line. */ 4371 1.1 christos DECLARE_INFO_COMMAND (info_select_reference_this_line, 4372 1.1 christos _("Select reference or menu item appearing on this line")) 4373 1.1 christos { 4374 1.1 christos char *line; 4375 1.1 christos 4376 1.1 christos if (window->line_starts) 4377 1.1 christos line = window->line_starts[window_line_of_point (window)]; 4378 1.1 christos else 4379 1.1 christos line = ""; 4380 1.1 christos 4381 1.1 christos /* If this line contains a menu item, select that one. */ 4382 1.1 christos if (strncmp ("* ", line, 2) == 0) 4383 1.1 christos info_menu_or_ref_item (window, count, key, info_menu_of_node, 0); 4384 1.1 christos else 4385 1.1 christos info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 0); 4386 1.1 christos } 4387 1.1 christos 4388 1.1 christos /* **************************************************************** */ 4390 1.1 christos /* */ 4391 1.1 christos /* Miscellaneous Info Commands */ 4392 1.1 christos /* */ 4393 1.1 christos /* **************************************************************** */ 4394 1.1 christos 4395 1.1 christos /* What to do when C-g is pressed in a window. */ 4396 1.1 christos DECLARE_INFO_COMMAND (info_abort_key, _("Cancel current operation")) 4397 1.1 christos { 4398 1.1 christos /* If error printing doesn't oridinarily ring the bell, do it now, 4399 1.1 christos since C-g always rings the bell. Otherwise, let the error printer 4400 1.1 christos do it. */ 4401 1.1 christos if (!info_error_rings_bell_p) 4402 1.1 christos terminal_ring_bell (); 4403 1.1 christos info_error ((char *) _("Quit"), NULL, NULL); 4404 1.1 christos 4405 1.1 christos info_initialize_numeric_arg (); 4406 1.1 christos info_clear_pending_input (); 4407 1.1 christos info_last_executed_command = (VFunction *)NULL; 4408 1.1 christos } 4409 1.1 christos 4410 1.1 christos /* Move the cursor to the desired line of the window. */ 4411 1.1 christos DECLARE_INFO_COMMAND (info_move_to_window_line, 4412 1.1 christos _("Move the cursor to a specific line of the window")) 4413 1.1 christos { 4414 1.1 christos int line; 4415 1.1 christos 4416 1.1 christos /* With no numeric argument of any kind, default to the center line. */ 4417 1.1 christos if (!info_explicit_arg && count == 1) 4418 1.1 christos line = (window->height / 2) + window->pagetop; 4419 1.1 christos else 4420 1.1 christos { 4421 1.1 christos if (count < 0) 4422 1.1 christos line = (window->height + count) + window->pagetop; 4423 1.1 christos else 4424 1.1 christos line = window->pagetop + count; 4425 1.1 christos } 4426 1.1 christos 4427 1.1 christos /* If the line doesn't appear in this window, make it do so. */ 4428 1.1 christos if ((line - window->pagetop) >= window->height) 4429 1.1 christos line = window->pagetop + (window->height - 1); 4430 1.1 christos 4431 1.1 christos /* If the line is too small, make it fit. */ 4432 1.1 christos if (line < window->pagetop) 4433 1.1 christos line = window->pagetop; 4434 1.1 christos 4435 1.1 christos /* If the selected line is past the bottom of the node, force it back. */ 4436 1.1 christos if (line >= window->line_count) 4437 1.1 christos line = window->line_count - 1; 4438 1.1 christos 4439 1.1 christos window->point = (window->line_starts[line] - window->node->contents); 4440 1.1 christos } 4441 1.1 christos 4442 1.1 christos /* Clear the screen and redraw its contents. Given a numeric argument, 4443 1.1 christos move the line the cursor is on to the COUNT'th line of the window. */ 4444 1.1 christos DECLARE_INFO_COMMAND (info_redraw_display, _("Redraw the display")) 4445 1.1 christos { 4446 1.1 christos if ((!info_explicit_arg && count == 1) || echo_area_is_active) 4447 1.1 christos { 4448 1.1 christos terminal_clear_screen (); 4449 1.1 christos display_clear_display (the_display); 4450 1.1 christos window_mark_chain (windows, W_UpdateWindow); 4451 1.1 christos display_update_display (windows); 4452 1.1 christos } 4453 1.1 christos else 4454 1.1 christos { 4455 1.1 christos int desired_line, point_line; 4456 1.1 christos int new_pagetop; 4457 1.1 christos 4458 1.1 christos point_line = window_line_of_point (window) - window->pagetop; 4459 1.1 christos 4460 1.1 christos if (count < 0) 4461 1.1 christos desired_line = window->height + count; 4462 1.1 christos else 4463 1.1 christos desired_line = count; 4464 1.1 christos 4465 1.1 christos if (desired_line < 0) 4466 1.1 christos desired_line = 0; 4467 1.1 christos 4468 1.1 christos if (desired_line >= window->height) 4469 1.1 christos desired_line = window->height - 1; 4470 1.1 christos 4471 1.1 christos if (desired_line == point_line) 4472 1.1 christos return; 4473 1.1 christos 4474 1.1 christos new_pagetop = window->pagetop + (point_line - desired_line); 4475 1.1 christos 4476 1.1 christos set_window_pagetop (window, new_pagetop); 4477 1.1 christos } 4478 1.1 christos } 4479 1.1 christos /* This command does nothing. It is the fact that a key is bound to it 4480 1.1 christos that has meaning. See the code at the top of info_session (). */ 4481 1.1 christos DECLARE_INFO_COMMAND (info_quit, _("Quit using Info")) 4482 1.1 christos {} 4483 1.1 christos 4484 1.1 christos 4485 1.1 christos /* **************************************************************** */ 4487 1.1 christos /* */ 4488 1.1 christos /* Reading Keys and Dispatching on Them */ 4489 1.1 christos /* */ 4490 1.1 christos /* **************************************************************** */ 4491 1.1 christos 4492 1.1 christos /* Declaration only. Special cased in info_dispatch_on_key (). 4493 1.1 christos Doc string is to avoid ugly results with describe_key etc. */ 4494 1.1 christos DECLARE_INFO_COMMAND (info_do_lowercase_version, 4495 1.1 christos _("Run command bound to this key's lowercase variant")) 4496 1.1 christos {} 4497 1.1 christos 4498 1.1 christos static void 4499 1.1 christos dispatch_error (char *keyseq) 4500 1.1 christos { 4501 1.1 christos char *rep; 4502 1.1 christos 4503 1.1 christos rep = pretty_keyseq (keyseq); 4504 1.1 christos 4505 1.1 christos if (!echo_area_is_active) 4506 1.1 christos info_error ((char *) _("Unknown command (%s)."), rep, NULL); 4507 1.1 christos else 4508 1.1 christos { 4509 1.1 christos char *temp = xmalloc (1 + strlen (rep) + strlen (_("\"%s\" is invalid"))); 4510 1.1 christos sprintf (temp, _("`%s' is invalid"), rep); 4511 1.1 christos terminal_ring_bell (); 4512 1.1 christos inform_in_echo_area (temp); 4513 1.1 christos free (temp); 4514 1.1 christos } 4515 1.1 christos } 4516 1.1 christos 4517 1.1 christos /* Keeping track of key sequences. */ 4518 1.1 christos static char *info_keyseq = (char *)NULL; 4519 1.1 christos static int info_keyseq_index = 0; 4520 1.1 christos static int info_keyseq_size = 0; 4521 1.1 christos static int info_keyseq_displayed_p = 0; 4522 1.1 christos 4523 1.1 christos /* Initialize the length of the current key sequence. */ 4524 1.1 christos void 4525 1.1 christos initialize_keyseq (void) 4526 1.1 christos { 4527 1.1 christos info_keyseq_index = 0; 4528 1.1 christos info_keyseq_displayed_p = 0; 4529 1.1 christos } 4530 1.1 christos 4531 1.1 christos /* Add CHARACTER to the current key sequence. */ 4532 1.1 christos void 4533 1.1 christos add_char_to_keyseq (char character) 4534 1.1 christos { 4535 1.1 christos if (info_keyseq_index + 2 >= info_keyseq_size) 4536 1.1 christos info_keyseq = (char *)xrealloc (info_keyseq, info_keyseq_size += 10); 4537 1.1 christos 4538 1.1 christos info_keyseq[info_keyseq_index++] = character; 4539 1.1 christos info_keyseq[info_keyseq_index] = '\0'; 4540 1.1 christos } 4541 1.1 christos 4542 1.1 christos /* Display the current value of info_keyseq. If argument EXPECTING is 4543 1.1 christos non-zero, input is expected to be read after the key sequence is 4544 1.1 christos displayed, so add an additional prompting character to the sequence. */ 4545 1.1 christos static void 4546 1.1 christos display_info_keyseq (int expecting_future_input) 4547 1.1 christos { 4548 1.1 christos char *rep; 4549 1.1 christos 4550 1.1 christos rep = pretty_keyseq (info_keyseq); 4551 1.1 christos if (expecting_future_input) 4552 1.1 christos strcat (rep, "-"); 4553 1.1 christos 4554 1.1 christos if (echo_area_is_active) 4555 1.1 christos inform_in_echo_area (rep); 4556 1.1 christos else 4557 1.1 christos { 4558 1.1 christos window_message_in_echo_area (rep, NULL, NULL); 4559 1.1 christos display_cursor_at_point (active_window); 4560 1.1 christos } 4561 1.1 christos info_keyseq_displayed_p = 1; 4562 1.1 christos } 4563 1.1 christos 4564 1.1 christos /* Called by interactive commands to read a keystroke. */ 4565 1.1 christos unsigned char 4566 1.1 christos info_get_another_input_char (void) 4567 1.1 christos { 4568 1.1 christos int ready = !info_keyseq_displayed_p; /* ready if new and pending key */ 4569 1.1 christos 4570 1.1 christos /* If there isn't any input currently available, then wait a 4571 1.1 christos moment looking for input. If we don't get it fast enough, 4572 1.1 christos prompt a little bit with the current key sequence. */ 4573 1.1 christos if (!info_keyseq_displayed_p) 4574 1.1 christos { 4575 1.1 christos ready = 1; 4576 1.1 christos if (!info_any_buffered_input_p () && 4577 1.1 christos !info_input_pending_p ()) 4578 1.1 christos { 4579 1.1 christos #if defined (FD_SET) 4580 1.1 christos struct timeval timer; 4581 1.1 christos fd_set readfds; 4582 1.1 christos 4583 1.1 christos FD_ZERO (&readfds); 4584 1.1 christos FD_SET (fileno (info_input_stream), &readfds); 4585 1.1 christos timer.tv_sec = 1; 4586 1.1 christos timer.tv_usec = 750; 4587 1.1 christos ready = select (fileno(info_input_stream)+1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer); 4588 1.1 christos #else 4589 1.1 christos ready = 0; 4590 1.1 christos #endif /* FD_SET */ 4591 1.1 christos } 4592 1.1 christos } 4593 1.1 christos 4594 1.1 christos if (!ready) 4595 1.1 christos display_info_keyseq (1); 4596 1.1 christos 4597 1.1 christos return (info_get_input_char ()); 4598 1.1 christos } 4599 1.1 christos 4600 1.1 christos /* Do the command associated with KEY in MAP. If the associated command is 4601 1.1 christos really a keymap, then read another key, and dispatch into that map. */ 4602 1.1 christos void 4603 1.1 christos info_dispatch_on_key (unsigned char key, Keymap map) 4604 1.1 christos { 4605 1.1 christos #if !defined(INFOKEY) 4606 1.1 christos if (Meta_p (key) && (!ISO_Latin_p || map[key].function != ea_insert)) 4607 1.1 christos { 4608 1.1 christos if (map[ESC].type == ISKMAP) 4609 1.1 christos { 4610 1.1 christos map = (Keymap)map[ESC].function; 4611 1.1 christos add_char_to_keyseq (ESC); 4612 1.1 christos key = UnMeta (key); 4613 1.1 christos info_dispatch_on_key (key, map); 4614 1.1 christos } 4615 1.1 christos else 4616 1.1 christos { 4617 1.1 christos dispatch_error (info_keyseq); 4618 1.1 christos } 4619 1.1 christos return; 4620 1.1 christos } 4621 1.1 christos #endif /* INFOKEY */ 4622 1.1 christos 4623 1.1 christos switch (map[key].type) 4624 1.1 christos { 4625 1.1 christos case ISFUNC: 4626 1.1 christos { 4627 1.1 christos VFunction *func; 4628 1.1 christos 4629 1.1 christos func = InfoFunction(map[key].function); 4630 1.1 christos if (func != (VFunction *)NULL) 4631 1.1 christos { 4632 1.1 christos /* Special case info_do_lowercase_version (). */ 4633 1.1 christos if (func == (VFunction *) info_do_lowercase_version) 4634 1.1 christos { 4635 1.1 christos #if defined(INFOKEY) 4636 1.1 christos unsigned char lowerkey; 4637 1.1 christos 4638 1.1 christos lowerkey = Meta_p(key) ? Meta (tolower (UnMeta (key))) : tolower (key); 4639 1.1 christos if (lowerkey == key) 4640 1.1 christos { 4641 1.1 christos add_char_to_keyseq (key); 4642 1.1 christos dispatch_error (info_keyseq); 4643 1.1 christos return; 4644 1.1 christos } 4645 1.1 christos info_dispatch_on_key (lowerkey, map); 4646 1.1 christos #else /* !INFOKEY */ 4647 1.1 christos info_dispatch_on_key (tolower (key), map); 4648 1.1 christos #endif /* INFOKEY */ 4649 1.1 christos return; 4650 1.1 christos } 4651 1.1 christos 4652 1.1 christos add_char_to_keyseq (key); 4653 1.1 christos 4654 1.1 christos if (info_keyseq_displayed_p) 4655 1.1 christos display_info_keyseq (0); 4656 1.1 christos 4657 1.1 christos { 4658 1.1 christos WINDOW *where; 4659 1.1 christos 4660 1.1 christos where = active_window; 4661 1.1 christos (*InfoFunction(map[key].function)) 4662 1.1 christos (active_window, info_numeric_arg * info_numeric_arg_sign, key); 4663 1.1 christos 4664 1.1 christos /* If we have input pending, then the last command was a prefix 4665 1.1 christos command. Don't change the value of the last function vars. 4666 1.1 christos Otherwise, remember the last command executed in the var 4667 1.1 christos appropriate to the window in which it was executed. */ 4668 1.1 christos if (!info_input_pending_p ()) 4669 1.1 christos { 4670 1.1 christos if (where == the_echo_area) 4671 1.1 christos ea_last_executed_command = InfoFunction(map[key].function); 4672 1.1 christos else 4673 1.1 christos info_last_executed_command = InfoFunction(map[key].function); 4674 1.1 christos } 4675 1.1 christos } 4676 1.1 christos } 4677 1.1 christos else 4678 1.1 christos { 4679 1.1 christos add_char_to_keyseq (key); 4680 1.1 christos dispatch_error (info_keyseq); 4681 1.1 christos return; 4682 1.1 christos } 4683 1.1 christos } 4684 1.1 christos break; 4685 1.1 christos 4686 1.1 christos case ISKMAP: 4687 1.1 christos add_char_to_keyseq (key); 4688 1.1 christos if (map[key].function != (InfoCommand *)NULL) 4689 1.1 christos { 4690 1.1 christos unsigned char newkey; 4691 1.1 christos 4692 1.1 christos newkey = info_get_another_input_char (); 4693 1.1 christos info_dispatch_on_key (newkey, (Keymap)map[key].function); 4694 1.1 christos } 4695 1.1 christos else 4696 1.1 christos { 4697 1.1 christos dispatch_error (info_keyseq); 4698 1.1 christos return; 4699 1.1 christos } 4700 1.1 christos break; 4701 1.1 christos } 4702 1.1 christos } 4703 1.1 christos 4704 1.1 christos /* **************************************************************** */ 4706 1.1 christos /* */ 4707 1.1 christos /* Numeric Arguments */ 4708 1.1 christos /* */ 4709 1.1 christos /* **************************************************************** */ 4710 1.1 christos 4711 1.1 christos /* Handle C-u style numeric args, as well as M--, and M-digits. */ 4712 1.1 christos 4713 1.1 christos /* Non-zero means that an explicit argument has been passed to this 4714 1.1 christos command, as in C-u C-v. */ 4715 1.1 christos int info_explicit_arg = 0; 4716 1.1 christos 4717 1.1 christos /* The sign of the numeric argument. */ 4718 1.1 christos int info_numeric_arg_sign = 1; 4719 1.1 christos 4720 1.1 christos /* The value of the argument itself. */ 4721 1.1 christos int info_numeric_arg = 1; 4722 1.1 christos 4723 1.1 christos /* Add the current digit to the argument in progress. */ 4724 1.1 christos DECLARE_INFO_COMMAND (info_add_digit_to_numeric_arg, 4725 1.1 christos _("Add this digit to the current numeric argument")) 4726 1.1 christos { 4727 1.1 christos info_numeric_arg_digit_loop (window, 0, key); 4728 1.1 christos } 4729 1.1 christos 4730 1.1 christos /* C-u, universal argument. Multiply the current argument by 4. 4731 1.1 christos Read a key. If the key has nothing to do with arguments, then 4732 1.1 christos dispatch on it. If the key is the abort character then abort. */ 4733 1.1 christos DECLARE_INFO_COMMAND (info_universal_argument, 4734 1.1 christos _("Start (or multiply by 4) the current numeric argument")) 4735 1.1 christos { 4736 1.1 christos info_numeric_arg *= 4; 4737 1.1 christos info_numeric_arg_digit_loop (window, 0, 0); 4738 1.1 christos } 4739 1.1 christos 4740 1.1 christos /* Create a default argument. */ 4741 1.1 christos void 4742 1.1 christos info_initialize_numeric_arg (void) 4743 1.1 christos { 4744 1.1 christos info_numeric_arg = info_numeric_arg_sign = 1; 4745 1.1 christos info_explicit_arg = 0; 4746 1.1 christos } 4747 1.1 christos 4748 1.1 christos DECLARE_INFO_COMMAND (info_numeric_arg_digit_loop, 4749 1.1 christos _("Internally used by \\[universal-argument]")) 4750 1.1 christos { 4751 1.1 christos unsigned char pure_key; 4752 1.1 christos Keymap keymap = window->keymap; 4753 1.1 christos 4754 1.1 christos while (1) 4755 1.1 christos { 4756 1.1 christos if (key) 4757 1.1 christos pure_key = key; 4758 1.1 christos else 4759 1.1 christos { 4760 1.1 christos if (display_was_interrupted_p && !info_any_buffered_input_p ()) 4761 1.1 christos display_update_display (windows); 4762 1.1 christos 4763 1.1 christos if (active_window != the_echo_area) 4764 1.1 christos display_cursor_at_point (active_window); 4765 1.1 christos 4766 1.1 christos pure_key = key = info_get_another_input_char (); 4767 1.1 christos 4768 1.1 christos #if !defined(INFOKEY) 4769 1.1 christos if (Meta_p (key)) 4770 1.1 christos add_char_to_keyseq (ESC); 4771 1.1 christos 4772 1.1 christos add_char_to_keyseq (UnMeta (key)); 4773 1.1 christos #else /* defined(INFOKEY) */ 4774 1.1 christos add_char_to_keyseq (key); 4775 1.1 christos #endif /* defined(INFOKEY) */ 4776 1.1 christos } 4777 1.1 christos 4778 1.1 christos #if !defined(INFOKEY) 4779 1.1 christos if (Meta_p (key)) 4780 1.1 christos key = UnMeta (key); 4781 1.1 christos #endif /* !defined(INFOKEY) */ 4782 1.1 christos 4783 1.1 christos if (keymap[key].type == ISFUNC 4784 1.1 christos && InfoFunction(keymap[key].function) 4785 1.1 christos == (VFunction *) info_universal_argument) 4786 1.1 christos { 4787 1.1 christos info_numeric_arg *= 4; 4788 1.1 christos key = 0; 4789 1.1 christos continue; 4790 1.1 christos } 4791 1.1 christos 4792 1.1 christos #if defined(INFOKEY) 4793 1.1 christos if (Meta_p (key)) 4794 1.1 christos key = UnMeta (key); 4795 1.1 christos #endif /* !defined(INFOKEY) */ 4796 1.1 christos 4797 1.1 christos 4798 1.1 christos if (isdigit (key)) 4799 1.1 christos { 4800 1.1 christos if (info_explicit_arg) 4801 1.1 christos info_numeric_arg = (info_numeric_arg * 10) + (key - '0'); 4802 1.1 christos else 4803 1.1 christos info_numeric_arg = (key - '0'); 4804 1.1 christos info_explicit_arg = 1; 4805 1.1 christos } 4806 1.1 christos else 4807 1.1 christos { 4808 1.1 christos if (key == '-' && !info_explicit_arg) 4809 1.1 christos { 4810 1.1 christos info_numeric_arg_sign = -1; 4811 1.1 christos info_numeric_arg = 1; 4812 1.1 christos } 4813 1.1 christos else 4814 1.1 christos { 4815 1.1 christos info_keyseq_index--; 4816 1.1 christos info_dispatch_on_key (pure_key, keymap); 4817 1.1 christos return; 4818 1.1 christos } 4819 1.1 christos } 4820 1.1 christos key = 0; 4821 1.1 christos } 4822 1.1 christos } 4823 1.1 christos 4824 1.1 christos /* **************************************************************** */ 4826 1.1 christos /* */ 4827 1.1 christos /* Input Character Buffering */ 4828 1.1 christos /* */ 4829 1.1 christos /* **************************************************************** */ 4830 1.1 christos 4831 1.1 christos /* Character waiting to be read next. */ 4832 1.1 christos static int pending_input_character = 0; 4833 1.1 christos 4834 1.1 christos /* How to make there be no pending input. */ 4835 1.1 christos static void 4836 1.1 christos info_clear_pending_input (void) 4837 1.1 christos { 4838 1.1 christos pending_input_character = 0; 4839 1.1 christos } 4840 1.1 christos 4841 1.1 christos /* How to set the pending input character. */ 4842 1.1 christos static void 4843 1.1 christos info_set_pending_input (unsigned char key) 4844 1.1 christos { 4845 1.1 christos pending_input_character = key; 4846 1.1 christos } 4847 1.1 christos 4848 1.1 christos /* How to see if there is any pending input. */ 4849 1.1 christos unsigned char 4850 1.1 christos info_input_pending_p (void) 4851 1.1 christos { 4852 1.1 christos return (pending_input_character); 4853 1.1 christos } 4854 1.1 christos 4855 1.1 christos /* Largest number of characters that we can read in advance. */ 4856 1.1 christos #define MAX_INFO_INPUT_BUFFERING 512 4857 1.1 christos 4858 1.1 christos static int pop_index = 0, push_index = 0; 4859 1.1 christos static unsigned char info_input_buffer[MAX_INFO_INPUT_BUFFERING]; 4860 1.1 christos 4861 1.1 christos /* Add KEY to the buffer of characters to be read. */ 4862 1.1 christos static void 4863 1.1 christos info_push_typeahead (unsigned char key) 4864 1.1 christos { 4865 1.1 christos /* Flush all pending input in the case of C-g pressed. */ 4866 1.1 christos if (key == Control ('g')) 4867 1.1 christos { 4868 1.1 christos push_index = pop_index; 4869 1.1 christos info_set_pending_input (Control ('g')); 4870 1.1 christos } 4871 1.1 christos else 4872 1.1 christos { 4873 1.1 christos info_input_buffer[push_index++] = key; 4874 1.1 christos if ((unsigned int) push_index >= sizeof (info_input_buffer)) 4875 1.1 christos push_index = 0; 4876 1.1 christos } 4877 1.1 christos } 4878 1.1 christos 4879 1.1 christos /* Return the amount of space available in INFO_INPUT_BUFFER for new chars. */ 4880 1.1 christos static int 4881 1.1 christos info_input_buffer_space_available (void) 4882 1.1 christos { 4883 1.1 christos if (pop_index > push_index) 4884 1.1 christos return (pop_index - push_index); 4885 1.1 christos else 4886 1.1 christos return (sizeof (info_input_buffer) - (push_index - pop_index)); 4887 1.1 christos } 4888 1.1 christos 4889 1.1 christos /* Get a key from the buffer of characters to be read. 4890 1.1 christos Return the key in KEY. 4891 1.1 christos Result is non-zero if there was a key, or 0 if there wasn't. */ 4892 1.1 christos static int 4893 1.1 christos info_get_key_from_typeahead (unsigned char *key) 4894 1.1 christos { 4895 1.1 christos if (push_index == pop_index) 4896 1.1 christos return (0); 4897 1.1 christos 4898 1.1 christos *key = info_input_buffer[pop_index++]; 4899 1.1 christos 4900 1.1 christos if ((unsigned int) pop_index >= sizeof (info_input_buffer)) 4901 1.1 christos pop_index = 0; 4902 1.1 christos 4903 1.1 christos return (1); 4904 1.1 christos } 4905 1.1 christos 4906 1.1 christos int 4907 1.1 christos info_any_buffered_input_p (void) 4908 1.1 christos { 4909 1.1 christos info_gather_typeahead (); 4910 1.1 christos return (push_index != pop_index); 4911 1.1 christos } 4912 1.1 christos 4913 1.1 christos /* If characters are available to be read, then read them and stuff them into 4914 1.1 christos info_input_buffer. Otherwise, do nothing. */ 4915 1.1 christos void 4916 1.1 christos info_gather_typeahead (void) 4917 1.1 christos { 4918 1.1 christos register int i = 0; 4919 1.1 christos int tty, space_avail; 4920 1.1 christos long chars_avail; 4921 1.1 christos unsigned char input[MAX_INFO_INPUT_BUFFERING]; 4922 1.1 christos 4923 1.1 christos tty = fileno (info_input_stream); 4924 1.1 christos chars_avail = 0; 4925 1.1 christos 4926 1.1 christos space_avail = info_input_buffer_space_available (); 4927 1.1 christos 4928 1.1 christos /* If we can just find out how many characters there are to read, do so. */ 4929 1.1 christos #if defined (FIONREAD) 4930 1.1 christos { 4931 1.1 christos ioctl (tty, FIONREAD, &chars_avail); 4932 1.1 christos 4933 1.1 christos if (chars_avail > space_avail) 4934 1.1 christos chars_avail = space_avail; 4935 1.1 christos 4936 1.1 christos if (chars_avail) 4937 1.1 christos chars_avail = read (tty, &input[0], chars_avail); 4938 1.1 christos } 4939 1.1 christos #else /* !FIONREAD */ 4940 1.1 christos # if defined (O_NDELAY) 4941 1.1 christos { 4942 1.1 christos int flags; 4943 1.1 christos 4944 1.1 christos flags = fcntl (tty, F_GETFL, 0); 4945 1.1 christos 4946 1.1 christos fcntl (tty, F_SETFL, (flags | O_NDELAY)); 4947 1.1 christos chars_avail = read (tty, &input[0], space_avail); 4948 1.1 christos fcntl (tty, F_SETFL, flags); 4949 1.1 christos 4950 1.1 christos if (chars_avail == -1) 4951 1.1 christos chars_avail = 0; 4952 1.1 christos } 4953 1.1 christos # else /* !O_NDELAY */ 4954 1.1 christos # ifdef __DJGPP__ 4955 1.1 christos { 4956 1.1 christos extern long pc_term_chars_avail (void); 4957 1.1 christos 4958 1.1 christos if (isatty (tty)) 4959 1.1 christos chars_avail = pc_term_chars_avail (); 4960 1.1 christos else 4961 1.1 christos { 4962 1.1 christos /* We could be more accurate by calling ltell, but we have no idea 4963 1.1 christos whether tty is buffered by stdio functions, and if so, how many 4964 1.1 christos characters are already waiting in the buffer. So we punt. */ 4965 1.1 christos struct stat st; 4966 1.1 christos 4967 1.1 christos if (fstat (tty, &st) < 0) 4968 1.1 christos chars_avail = 1; 4969 1.1 christos else 4970 1.1 christos chars_avail = st.st_size; 4971 1.1 christos } 4972 1.1 christos if (chars_avail > space_avail) 4973 1.1 christos chars_avail = space_avail; 4974 1.1 christos if (chars_avail) 4975 1.1 christos chars_avail = read (tty, &input[0], chars_avail); 4976 1.1 christos } 4977 1.1 christos # endif/* __DJGPP__ */ 4978 1.1 christos # endif /* O_NDELAY */ 4979 1.1 christos #endif /* !FIONREAD */ 4980 1.1 christos 4981 1.1 christos while (i < chars_avail) 4982 1.1 christos { 4983 1.1 christos info_push_typeahead (input[i]); 4984 1.1 christos i++; 4985 1.1 christos } 4986 1.1 christos } 4987 1.1 christos 4988 1.1 christos /* How to read a single character. */ 4989 1.1 christos unsigned char 4990 1.1 christos info_get_input_char (void) 4991 1.1 christos { 4992 1.1 christos unsigned char keystroke; 4993 1.1 christos 4994 1.1 christos info_gather_typeahead (); 4995 1.1 christos 4996 1.1 christos if (pending_input_character) 4997 1.1 christos { 4998 1.1 christos keystroke = pending_input_character; 4999 1.1 christos pending_input_character = 0; 5000 1.1 christos } 5001 1.1 christos else if (info_get_key_from_typeahead (&keystroke) == 0) 5002 1.1 christos { 5003 1.1 christos int rawkey; 5004 1.1 christos unsigned char c; 5005 1.1 christos int tty = fileno (info_input_stream); 5006 1.1 christos 5007 1.1 christos /* Using stream I/O causes FIONREAD etc to fail to work 5008 1.1 christos so unless someone can find a portable way of finding 5009 1.1 christos out how many characters are currently buffered, we 5010 1.1 christos should stay with away from stream I/O. 5011 1.1 christos --Egil Kvaleberg <egilk (at) sn.no>, January 1997. */ 5012 1.1 christos #ifdef EINTR 5013 1.1 christos /* Keep reading if we got EINTR, so that we don't just exit. 5014 1.1 christos --Andreas Schwab <schwab (at) issan.informatik.uni-dortmund.de>, 5015 1.1 christos 22 Dec 1997. */ 5016 1.1 christos { 5017 1.1 christos int n; 5018 1.1 christos do 5019 1.1 christos n = read (tty, &c, 1); 5020 1.1 christos while (n == -1 && errno == EINTR); 5021 1.1 christos rawkey = n == 1 ? c : EOF; 5022 1.1 christos } 5023 1.1 christos #else 5024 1.1 christos rawkey = (read (tty, &c, 1) == 1) ? c : EOF; 5025 1.1 christos #endif 5026 1.1 christos 5027 1.1 christos keystroke = rawkey; 5028 1.1 christos 5029 1.1 christos if (rawkey == EOF) 5030 1.1 christos { 5031 1.1 christos if (info_input_stream != stdin) 5032 1.1 christos { 5033 1.1 christos fclose (info_input_stream); 5034 1.1 christos info_input_stream = stdin; 5035 1.1 christos tty = fileno (info_input_stream); 5036 1.1 christos display_inhibited = 0; 5037 1.1 christos display_update_display (windows); 5038 1.1 christos display_cursor_at_point (active_window); 5039 1.1 christos rawkey = (read (tty, &c, 1) == 1) ? c : EOF; 5040 1.1 christos keystroke = rawkey; 5041 1.1 christos } 5042 5043 if (rawkey == EOF) 5044 { 5045 terminal_unprep_terminal (); 5046 close_dribble_file (); 5047 xexit (0); 5048 } 5049 } 5050 } 5051 5052 if (info_dribble_file) 5053 dribble (keystroke); 5054 5055 return keystroke; 5056 } 5057