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