terminal.c revision 1.5
1/* terminal.c -- controlling the terminal with termcap. */ 2 3/* Copyright (C) 1996 Free Software Foundation, Inc. 4 5 This file is part of the GNU Readline Library, a library for 6 reading lines of text with interactive input and history editing. 7 8 The GNU Readline Library is free software; you can redistribute it 9 and/or modify it under the terms of the GNU General Public License 10 as published by the Free Software Foundation; either version 2, or 11 (at your option) any later version. 12 13 The GNU Readline Library is distributed in the hope that it will be 14 useful, but WITHOUT ANY WARRANTY; without even the implied warranty 15 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 The GNU General Public License is often shipped with GNU software, and 19 is generally kept in a file called COPYING or LICENSE. If you do not 20 have a copy of the license, write to the Free Software Foundation, 21 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ 22#define READLINE_LIBRARY 23 24#if defined (HAVE_CONFIG_H) 25# include <config.h> 26#endif 27 28#include <sys/types.h> 29#include "posixstat.h" 30#include <fcntl.h> 31#if defined (HAVE_SYS_FILE_H) 32# include <sys/file.h> 33#endif /* HAVE_SYS_FILE_H */ 34 35#if defined (HAVE_UNISTD_H) 36# include <unistd.h> 37#endif /* HAVE_UNISTD_H */ 38 39#if defined (HAVE_STDLIB_H) 40# include <stdlib.h> 41#else 42# include "ansi_stdlib.h" 43#endif /* HAVE_STDLIB_H */ 44 45#if defined (HAVE_LOCALE_H) 46# include <locale.h> 47#endif 48 49#include <stdio.h> 50 51/* System-specific feature definitions and include files. */ 52#include "rldefs.h" 53 54# include <sys/ioctl.h> 55 56#include "rltty.h" 57#include "tcap.h" 58 59/* Some standard library routines. */ 60#include "readline.h" 61#include "history.h" 62 63#include "rlprivate.h" 64#include "rlshell.h" 65#include "xmalloc.h" 66 67#define CUSTOM_REDISPLAY_FUNC() (rl_redisplay_function != rl_redisplay) 68#define CUSTOM_INPUT_FUNC() (rl_getc_function != rl_getc) 69 70/* **************************************************************** */ 71/* */ 72/* Terminal and Termcap */ 73/* */ 74/* **************************************************************** */ 75 76static char *term_buffer = (char *)NULL; 77static char *term_string_buffer = (char *)NULL; 78 79static int tcap_initialized; 80 81#if !defined (__linux__) 82# if defined (__EMX__) || defined (NEED_EXTERN_PC) 83extern 84# endif /* __EMX__ || NEED_EXTERN_PC */ 85char PC, *BC, *UP; 86#endif /* __linux__ */ 87 88/* Some strings to control terminal actions. These are output by tputs (). */ 89char *_rl_term_clreol; 90char *_rl_term_clrpag; 91char *_rl_term_cr; 92char *_rl_term_backspace; 93char *_rl_term_goto; 94char *_rl_term_pc; 95 96/* Non-zero if we determine that the terminal can do character insertion. */ 97int _rl_terminal_can_insert = 0; 98 99/* How to insert characters. */ 100char *_rl_term_im; 101char *_rl_term_ei; 102char *_rl_term_ic; 103char *_rl_term_ip; 104char *_rl_term_IC; 105 106/* How to delete characters. */ 107char *_rl_term_dc; 108char *_rl_term_DC; 109 110#if defined (HACK_TERMCAP_MOTION) 111char *_rl_term_forward_char; 112#endif /* HACK_TERMCAP_MOTION */ 113 114/* How to go up a line. */ 115char *_rl_term_up; 116 117/* A visible bell; char if the terminal can be made to flash the screen. */ 118static char *_rl_visible_bell; 119 120/* Non-zero means the terminal can auto-wrap lines. */ 121int _rl_term_autowrap; 122 123/* Non-zero means that this terminal has a meta key. */ 124static int term_has_meta; 125 126/* The sequences to write to turn on and off the meta key, if this 127 terminal has one. */ 128static char *_rl_term_mm; 129static char *_rl_term_mo; 130 131/* The key sequences output by the arrow keys, if this terminal has any. */ 132static char *_rl_term_ku; 133static char *_rl_term_kd; 134static char *_rl_term_kr; 135static char *_rl_term_kl; 136 137/* How to initialize and reset the arrow keys, if this terminal has any. */ 138static char *_rl_term_ks; 139static char *_rl_term_ke; 140 141/* The key sequences sent by the Home and End keys, if any. */ 142static char *_rl_term_kh; 143static char *_rl_term_kH; 144static char *_rl_term_at7; /* @7 */ 145 146/* Insert key */ 147static char *_rl_term_kI; 148 149/* Cursor control */ 150static char *_rl_term_vs; /* very visible */ 151static char *_rl_term_ve; /* normal */ 152 153static void bind_termcap_arrow_keys PARAMS((Keymap)); 154 155/* Variables that hold the screen dimensions, used by the display code. */ 156int _rl_screenwidth, _rl_screenheight, _rl_screenchars; 157 158/* Non-zero means the user wants to enable the keypad. */ 159int _rl_enable_keypad; 160 161/* Non-zero means the user wants to enable a meta key. */ 162int _rl_enable_meta = 1; 163 164#if defined (__EMX__) 165static void 166_emx_get_screensize (swp, shp) 167 int *swp, *shp; 168{ 169 int sz[2]; 170 171 _scrsize (sz); 172 173 if (swp) 174 *swp = sz[0]; 175 if (shp) 176 *shp = sz[1]; 177} 178#endif 179 180/* Get readline's idea of the screen size. TTY is a file descriptor open 181 to the terminal. If IGNORE_ENV is true, we do not pay attention to the 182 values of $LINES and $COLUMNS. The tests for TERM_STRING_BUFFER being 183 non-null serve to check whether or not we have initialized termcap. */ 184void 185_rl_get_screen_size (tty, ignore_env) 186 int tty, ignore_env; 187{ 188 char *ss; 189#if defined (TIOCGWINSZ) 190 struct winsize window_size; 191#endif /* TIOCGWINSZ */ 192 193#if defined (TIOCGWINSZ) 194 if (ioctl (tty, TIOCGWINSZ, &window_size) == 0) 195 { 196 _rl_screenwidth = (int) window_size.ws_col; 197 _rl_screenheight = (int) window_size.ws_row; 198 } 199#endif /* TIOCGWINSZ */ 200 201#if defined (__EMX__) 202 _emx_get_screensize (&_rl_screenwidth, &_rl_screenheight); 203#endif 204 205 /* Environment variable COLUMNS overrides setting of "co" if IGNORE_ENV 206 is unset. */ 207 if (_rl_screenwidth <= 0) 208 { 209 if (ignore_env == 0 && (ss = sh_get_env_value ("COLUMNS")) && *ss != '\0') 210 _rl_screenwidth = atoi (ss); 211 212#if !defined (__DJGPP__) 213 if (_rl_screenwidth <= 0 && term_string_buffer) 214 _rl_screenwidth = tgetnum ("co"); 215#endif 216 } 217 218 /* Environment variable LINES overrides setting of "li" if IGNORE_ENV 219 is unset. */ 220 if (_rl_screenheight <= 0) 221 { 222 if (ignore_env == 0 && (ss = sh_get_env_value ("LINES")) && *ss != '\0') 223 _rl_screenheight = atoi (ss); 224 225#if !defined (__DJGPP__) 226 if (_rl_screenheight <= 0 && term_string_buffer) 227 _rl_screenheight = tgetnum ("li"); 228#endif 229 } 230 231 /* If all else fails, default to 80x24 terminal. */ 232 if (_rl_screenwidth <= 1) 233 _rl_screenwidth = 80; 234 235 if (_rl_screenheight <= 0) 236 _rl_screenheight = 24; 237 238 /* If we're being compiled as part of bash, set the environment 239 variables $LINES and $COLUMNS to new values. Otherwise, just 240 do a pair of putenv () or setenv () calls. */ 241 sh_set_lines_and_columns (_rl_screenheight, _rl_screenwidth); 242 243 if (_rl_term_autowrap == 0) 244 _rl_screenwidth--; 245 246 _rl_screenchars = _rl_screenwidth * _rl_screenheight; 247} 248 249void 250_rl_set_screen_size (rows, cols) 251 int rows, cols; 252{ 253 if (rows == 0 || cols == 0) 254 return; 255 256 _rl_screenheight = rows; 257 _rl_screenwidth = cols; 258 259 if (_rl_term_autowrap == 0) 260 _rl_screenwidth--; 261 262 _rl_screenchars = _rl_screenwidth * _rl_screenheight; 263} 264 265void 266rl_set_screen_size (rows, cols) 267 int rows, cols; 268{ 269 _rl_set_screen_size (rows, cols); 270} 271 272void 273rl_get_screen_size (rows, cols) 274 int *rows, *cols; 275{ 276 if (rows) 277 *rows = _rl_screenheight; 278 if (cols) 279 *cols = _rl_screenwidth; 280} 281 282void 283rl_resize_terminal () 284{ 285 if (readline_echoing_p) 286 { 287 _rl_get_screen_size (fileno (rl_instream), 1); 288 if (CUSTOM_REDISPLAY_FUNC ()) 289 rl_forced_update_display (); 290 else 291 _rl_redisplay_after_sigwinch (); 292 } 293} 294 295struct _tc_string { 296 const char *tc_var; 297 char **tc_value; 298}; 299 300/* This should be kept sorted, just in case we decide to change the 301 search algorithm to something smarter. */ 302static struct _tc_string tc_strings[] = 303{ 304 { "@7", &_rl_term_at7 }, 305 { "DC", &_rl_term_DC }, 306 { "IC", &_rl_term_IC }, 307 { "ce", &_rl_term_clreol }, 308 { "cl", &_rl_term_clrpag }, 309 { "cr", &_rl_term_cr }, 310 { "dc", &_rl_term_dc }, 311 { "ei", &_rl_term_ei }, 312 { "ic", &_rl_term_ic }, 313 { "im", &_rl_term_im }, 314 { "kH", &_rl_term_kH }, /* home down ?? */ 315 { "kI", &_rl_term_kI }, /* insert */ 316 { "kd", &_rl_term_kd }, 317 { "ke", &_rl_term_ke }, /* end keypad mode */ 318 { "kh", &_rl_term_kh }, /* home */ 319 { "kl", &_rl_term_kl }, 320 { "kr", &_rl_term_kr }, 321 { "ks", &_rl_term_ks }, /* start keypad mode */ 322 { "ku", &_rl_term_ku }, 323 { "le", &_rl_term_backspace }, 324 { "mm", &_rl_term_mm }, 325 { "mo", &_rl_term_mo }, 326#if defined (HACK_TERMCAP_MOTION) 327 { "nd", &_rl_term_forward_char }, 328#endif 329 { "pc", &_rl_term_pc }, 330 { "up", &_rl_term_up }, 331 { "vb", &_rl_visible_bell }, 332 { "vs", &_rl_term_vs }, 333 { "ve", &_rl_term_ve }, 334}; 335 336#define NUM_TC_STRINGS (sizeof (tc_strings) / sizeof (struct _tc_string)) 337 338/* Read the desired terminal capability strings into BP. The capabilities 339 are described in the TC_STRINGS table. */ 340static void 341get_term_capabilities (bp) 342 char **bp; 343{ 344#if !defined (__DJGPP__) /* XXX - doesn't DJGPP have a termcap library? */ 345 register int i; 346 347 for (i = 0; i < NUM_TC_STRINGS; i++) 348# ifdef __LCC__ 349 *(tc_strings[i].tc_value) = tgetstr ((char *)tc_strings[i].tc_var, bp); 350# else 351 *(tc_strings[i].tc_value) = tgetstr (tc_strings[i].tc_var, bp); 352# endif 353#endif 354 tcap_initialized = 1; 355} 356 357int 358_rl_init_terminal_io (terminal_name) 359 const char *terminal_name; 360{ 361 const char *term; 362 char *buffer; 363 int tty, tgetent_ret; 364 365 term = terminal_name ? terminal_name : sh_get_env_value ("TERM"); 366 _rl_term_clrpag = _rl_term_cr = _rl_term_clreol = (char *)NULL; 367 tty = rl_instream ? fileno (rl_instream) : 0; 368 _rl_screenwidth = _rl_screenheight = 0; 369 370 if (term == 0 || *term == '\0') 371 term = "dumb"; 372 373 /* I've separated this out for later work on not calling tgetent at all 374 if the calling application has supplied a custom redisplay function, 375 (and possibly if the application has supplied a custom input function). */ 376 if (CUSTOM_REDISPLAY_FUNC()) 377 { 378 tgetent_ret = -1; 379 } 380 else 381 { 382 if (term_string_buffer == 0) 383 term_string_buffer = (char *)xmalloc(2032); 384 385 if (term_buffer == 0) 386 term_buffer = (char *)xmalloc(4080); 387 388 buffer = term_string_buffer; 389 390 tgetent_ret = tgetent (term_buffer, term); 391 } 392 393 if (tgetent_ret <= 0) 394 { 395 FREE (term_string_buffer); 396 FREE (term_buffer); 397 buffer = term_buffer = term_string_buffer = (char *)NULL; 398 399 _rl_term_autowrap = 0; /* used by _rl_get_screen_size */ 400 401#if defined (__EMX__) 402 _emx_get_screensize (&_rl_screenwidth, &_rl_screenheight); 403 _rl_screenwidth--; 404#else /* !__EMX__ */ 405 _rl_get_screen_size (tty, 0); 406#endif /* !__EMX__ */ 407 408 /* Defaults. */ 409 if (_rl_screenwidth <= 0 || _rl_screenheight <= 0) 410 { 411 _rl_screenwidth = 79; 412 _rl_screenheight = 24; 413 } 414 415 /* Everything below here is used by the redisplay code (tputs). */ 416 _rl_screenchars = _rl_screenwidth * _rl_screenheight; 417 _rl_term_cr = "\r"; 418 _rl_term_im = _rl_term_ei = _rl_term_ic = _rl_term_IC = (char *)NULL; 419 _rl_term_up = _rl_term_dc = _rl_term_DC = _rl_visible_bell = (char *)NULL; 420 _rl_term_ku = _rl_term_kd = _rl_term_kl = _rl_term_kr = (char *)NULL; 421 _rl_term_kh = _rl_term_kH = _rl_term_kI = (char *)NULL; 422 _rl_term_ks = _rl_term_ke = _rl_term_at7 = (char *)NULL; 423 _rl_term_mm = _rl_term_mo = (char *)NULL; 424 _rl_term_ve = _rl_term_vs = (char *)NULL; 425#if defined (HACK_TERMCAP_MOTION) 426 term_forward_char = (char *)NULL; 427#endif 428 _rl_terminal_can_insert = term_has_meta = 0; 429 430 /* Reasonable defaults for tgoto(). Readline currently only uses 431 tgoto if _rl_term_IC or _rl_term_DC is defined, but just in case we 432 change that later... */ 433 PC = '\0'; 434 BC = _rl_term_backspace = "\b"; 435 UP = _rl_term_up; 436 437 return 0; 438 } 439 440 get_term_capabilities (&buffer); 441 442 /* Set up the variables that the termcap library expects the application 443 to provide. */ 444 PC = _rl_term_pc ? *_rl_term_pc : 0; 445 BC = _rl_term_backspace; 446 UP = _rl_term_up; 447 448 if (!_rl_term_cr) 449 _rl_term_cr = "\r"; 450 451 _rl_term_autowrap = tgetflag ("am") && tgetflag ("xn"); 452 453 _rl_get_screen_size (tty, 0); 454 455 /* "An application program can assume that the terminal can do 456 character insertion if *any one of* the capabilities `IC', 457 `im', `ic' or `ip' is provided." But we can't do anything if 458 only `ip' is provided, so... */ 459 _rl_terminal_can_insert = (_rl_term_IC || _rl_term_im || _rl_term_ic); 460 461 /* Check to see if this terminal has a meta key and clear the capability 462 variables if there is none. */ 463 term_has_meta = (tgetflag ("km") || tgetflag ("MT")); 464 if (!term_has_meta) 465 _rl_term_mm = _rl_term_mo = (char *)NULL; 466 467 /* Attempt to find and bind the arrow keys. Do not override already 468 bound keys in an overzealous attempt, however. */ 469 470 bind_termcap_arrow_keys (emacs_standard_keymap); 471 472#if defined (VI_MODE) 473 bind_termcap_arrow_keys (vi_movement_keymap); 474 bind_termcap_arrow_keys (vi_insertion_keymap); 475#endif /* VI_MODE */ 476 477 return 0; 478} 479 480/* Bind the arrow key sequences from the termcap description in MAP. */ 481static void 482bind_termcap_arrow_keys (map) 483 Keymap map; 484{ 485 Keymap xkeymap; 486 487 xkeymap = _rl_keymap; 488 _rl_keymap = map; 489 490 _rl_bind_if_unbound (_rl_term_ku, rl_get_previous_history); 491 _rl_bind_if_unbound (_rl_term_kd, rl_get_next_history); 492 _rl_bind_if_unbound (_rl_term_kr, rl_forward); 493 _rl_bind_if_unbound (_rl_term_kl, rl_backward); 494 495 _rl_bind_if_unbound (_rl_term_kh, rl_beg_of_line); /* Home */ 496 _rl_bind_if_unbound (_rl_term_at7, rl_end_of_line); /* End */ 497 498 _rl_keymap = xkeymap; 499} 500 501char * 502rl_get_termcap (cap) 503 const char *cap; 504{ 505 register int i; 506 507 if (tcap_initialized == 0) 508 return ((char *)NULL); 509 for (i = 0; i < NUM_TC_STRINGS; i++) 510 { 511 if (tc_strings[i].tc_var[0] == cap[0] && strcmp (tc_strings[i].tc_var, cap) == 0) 512 return *(tc_strings[i].tc_value); 513 } 514 return ((char *)NULL); 515} 516 517/* Re-initialize the terminal considering that the TERM/TERMCAP variable 518 has changed. */ 519int 520rl_reset_terminal (terminal_name) 521 const char *terminal_name; 522{ 523 _rl_init_terminal_io (terminal_name); 524 return 0; 525} 526 527/* A function for the use of tputs () */ 528#ifdef _MINIX 529void 530_rl_output_character_function (c) 531 int c; 532{ 533 putc (c, _rl_out_stream); 534} 535#else /* !_MINIX */ 536int 537_rl_output_character_function (c) 538 int c; 539{ 540 return putc (c, _rl_out_stream); 541} 542#endif /* !_MINIX */ 543 544/* Write COUNT characters from STRING to the output stream. */ 545void 546_rl_output_some_chars (string, count) 547 const char *string; 548 int count; 549{ 550 fwrite (string, 1, count, _rl_out_stream); 551} 552 553/* Move the cursor back. */ 554int 555_rl_backspace (count) 556 int count; 557{ 558 register int i; 559 560 if (_rl_term_backspace) 561 for (i = 0; i < count; i++) 562 tputs (_rl_term_backspace, 1, _rl_output_character_function); 563 else 564 for (i = 0; i < count; i++) 565 putc ('\b', _rl_out_stream); 566 return 0; 567} 568 569/* Move to the start of the next line. */ 570int 571rl_crlf () 572{ 573#if defined (NEW_TTY_DRIVER) 574 if (_rl_term_cr) 575 tputs (_rl_term_cr, 1, _rl_output_character_function); 576#endif /* NEW_TTY_DRIVER */ 577 putc ('\n', _rl_out_stream); 578 return 0; 579} 580 581/* Ring the terminal bell. */ 582int 583rl_ding () 584{ 585 if (readline_echoing_p) 586 { 587 switch (_rl_bell_preference) 588 { 589 case NO_BELL: 590 default: 591 break; 592 case VISIBLE_BELL: 593 if (_rl_visible_bell) 594 { 595 tputs (_rl_visible_bell, 1, _rl_output_character_function); 596 break; 597 } 598 /* FALLTHROUGH */ 599 case AUDIBLE_BELL: 600 fprintf (stderr, "\007"); 601 fflush (stderr); 602 break; 603 } 604 return (0); 605 } 606 return (-1); 607} 608 609/* **************************************************************** */ 610/* */ 611/* Controlling the Meta Key and Keypad */ 612/* */ 613/* **************************************************************** */ 614 615void 616_rl_enable_meta_key () 617{ 618#if !defined (__DJGPP__) 619 if (term_has_meta && _rl_term_mm) 620 tputs (_rl_term_mm, 1, _rl_output_character_function); 621#endif 622} 623 624void 625_rl_control_keypad (on) 626 int on; 627{ 628#if !defined (__DJGPP__) 629 if (on && _rl_term_ks) 630 tputs (_rl_term_ks, 1, _rl_output_character_function); 631 else if (!on && _rl_term_ke) 632 tputs (_rl_term_ke, 1, _rl_output_character_function); 633#endif 634} 635 636/* **************************************************************** */ 637/* */ 638/* Controlling the Cursor */ 639/* */ 640/* **************************************************************** */ 641 642/* Set the cursor appropriately depending on IM, which is one of the 643 insert modes (insert or overwrite). Insert mode gets the normal 644 cursor. Overwrite mode gets a very visible cursor. Only does 645 anything if we have both capabilities. */ 646void 647_rl_set_cursor (im, force) 648 int im, force; 649{ 650 if (_rl_term_ve && _rl_term_vs) 651 { 652 if (force || im != rl_insert_mode) 653 { 654 if (im == RL_IM_OVERWRITE) 655 tputs (_rl_term_vs, 1, _rl_output_character_function); 656 else 657 tputs (_rl_term_ve, 1, _rl_output_character_function); 658 } 659 } 660} 661