00001 /* terminal.c -- controlling the terminal with termcap. */ 00002 00003 /* Copyright (C) 1996 Free Software Foundation, Inc. 00004 00005 This file is part of the GNU Readline Library, a library for 00006 reading lines of text with interactive input and history editing. 00007 00008 The GNU Readline Library is free software; you can redistribute it 00009 and/or modify it under the terms of the GNU General Public License 00010 as published by the Free Software Foundation; either version 2, or 00011 (at your option) any later version. 00012 00013 The GNU Readline Library is distributed in the hope that it will be 00014 useful, but WITHOUT ANY WARRANTY; without even the implied warranty 00015 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 GNU General Public License for more details. 00017 00018 The GNU General Public License is often shipped with GNU software, and 00019 is generally kept in a file called COPYING or LICENSE. If you do not 00020 have a copy of the license, write to the Free Software Foundation, 00021 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ 00022 #define READLINE_LIBRARY 00023 00024 #include "config_readline.h" 00025 00026 #include <sys/types.h> 00027 #include "posixstat.h" 00028 #include <fcntl.h> 00029 #if defined (HAVE_SYS_FILE_H) 00030 # include <sys/file.h> 00031 #endif /* HAVE_SYS_FILE_H */ 00032 00033 #if defined (HAVE_UNISTD_H) 00034 # include <unistd.h> 00035 #endif /* HAVE_UNISTD_H */ 00036 00037 #if defined (HAVE_STDLIB_H) 00038 # include <stdlib.h> 00039 #else 00040 # include "ansi_stdlib.h" 00041 #endif /* HAVE_STDLIB_H */ 00042 00043 #if defined (HAVE_LOCALE_H) 00044 # include <locale.h> 00045 #endif 00046 00047 #include <stdio.h> 00048 00049 /* System-specific feature definitions and include files. */ 00050 #include "rldefs.h" 00051 00052 #if defined (GWINSZ_IN_SYS_IOCTL) && !defined (TIOCGWINSZ) 00053 # include <sys/ioctl.h> 00054 #endif /* GWINSZ_IN_SYS_IOCTL && !TIOCGWINSZ */ 00055 00056 #include "rltty.h" 00057 #include "tcap.h" 00058 00059 /* Some standard library routines. */ 00060 #include "readline.h" 00061 #include "history.h" 00062 00063 #include "rlprivate.h" 00064 #include "rlshell.h" 00065 #include "xmalloc.h" 00066 00067 #define CUSTOM_REDISPLAY_FUNC() (rl_redisplay_function != rl_redisplay) 00068 #define CUSTOM_INPUT_FUNC() (rl_getc_function != rl_getc) 00069 00070 /* **************************************************************** */ 00071 /* */ 00072 /* Terminal and Termcap */ 00073 /* */ 00074 /* **************************************************************** */ 00075 00076 static char *term_buffer = (char *)NULL; 00077 static char *term_string_buffer = (char *)NULL; 00078 00079 static int tcap_initialized; 00080 00081 #if !defined (__linux__) 00082 # if defined (__EMX__) || defined (NEED_EXTERN_PC) 00083 extern 00084 # endif /* __EMX__ || NEED_EXTERN_PC */ 00085 char PC, *BC, *UP; 00086 #endif /* __linux__ */ 00087 00088 /* Some strings to control terminal actions. These are output by tputs (). */ 00089 char *_rl_term_clreol; 00090 char *_rl_term_clrpag; 00091 char *_rl_term_cr; 00092 char *_rl_term_backspace; 00093 char *_rl_term_goto; 00094 char *_rl_term_pc; 00095 00096 /* Non-zero if we determine that the terminal can do character insertion. */ 00097 int _rl_terminal_can_insert = 0; 00098 00099 /* How to insert characters. */ 00100 char *_rl_term_im; 00101 char *_rl_term_ei; 00102 char *_rl_term_ic; 00103 char *_rl_term_ip; 00104 char *_rl_term_IC; 00105 00106 /* How to delete characters. */ 00107 char *_rl_term_dc; 00108 char *_rl_term_DC; 00109 00110 #if defined (HACK_TERMCAP_MOTION) 00111 char *_rl_term_forward_char; 00112 #endif /* HACK_TERMCAP_MOTION */ 00113 00114 /* How to go up a line. */ 00115 char *_rl_term_up; 00116 00117 /* A visible bell; char if the terminal can be made to flash the screen. */ 00118 static char *_rl_visible_bell; 00119 00120 /* Non-zero means the terminal can auto-wrap lines. */ 00121 int _rl_term_autowrap; 00122 00123 /* Non-zero means that this terminal has a meta key. */ 00124 static int term_has_meta; 00125 00126 /* The sequences to write to turn on and off the meta key, if this 00127 terminal has one. */ 00128 static char *_rl_term_mm; 00129 static char *_rl_term_mo; 00130 00131 /* The key sequences output by the arrow keys, if this terminal has any. */ 00132 static char *_rl_term_ku; 00133 static char *_rl_term_kd; 00134 static char *_rl_term_kr; 00135 static char *_rl_term_kl; 00136 00137 /* How to initialize and reset the arrow keys, if this terminal has any. */ 00138 static char *_rl_term_ks; 00139 static char *_rl_term_ke; 00140 00141 /* The key sequences sent by the Home and End keys, if any. */ 00142 static char *_rl_term_kh; 00143 static char *_rl_term_kH; 00144 static char *_rl_term_at7; /* @7 */ 00145 00146 /* Insert key */ 00147 static char *_rl_term_kI; 00148 00149 /* Cursor control */ 00150 static char *_rl_term_vs; /* very visible */ 00151 static char *_rl_term_ve; /* normal */ 00152 00153 static void bind_termcap_arrow_keys PARAMS((Keymap)); 00154 00155 /* Variables that hold the screen dimensions, used by the display code. */ 00156 int _rl_screenwidth, _rl_screenheight, _rl_screenchars; 00157 00158 /* Non-zero means the user wants to enable the keypad. */ 00159 int _rl_enable_keypad; 00160 00161 /* Non-zero means the user wants to enable a meta key. */ 00162 int _rl_enable_meta = 1; 00163 00164 #if defined (__EMX__) 00165 static void 00166 _emx_get_screensize (swp, shp) 00167 int *swp, *shp; 00168 { 00169 int sz[2]; 00170 00171 _scrsize (sz); 00172 00173 if (swp) 00174 *swp = sz[0]; 00175 if (shp) 00176 *shp = sz[1]; 00177 } 00178 #endif 00179 00180 /* Get readline's idea of the screen size. TTY is a file descriptor open 00181 to the terminal. If IGNORE_ENV is true, we do not pay attention to the 00182 values of $LINES and $COLUMNS. The tests for TERM_STRING_BUFFER being 00183 non-null serve to check whether or not we have initialized termcap. */ 00184 void 00185 _rl_get_screen_size (tty, ignore_env) 00186 int tty, ignore_env; 00187 { 00188 char *ss; 00189 #if defined (TIOCGWINSZ) 00190 struct winsize window_size; 00191 #endif /* TIOCGWINSZ */ 00192 00193 #if defined (TIOCGWINSZ) 00194 if (ioctl (tty, TIOCGWINSZ, &window_size) == 0) 00195 { 00196 _rl_screenwidth = (int) window_size.ws_col; 00197 _rl_screenheight = (int) window_size.ws_row; 00198 } 00199 #endif /* TIOCGWINSZ */ 00200 00201 #if defined (__EMX__) 00202 _emx_get_screensize (&_rl_screenwidth, &_rl_screenheight); 00203 #endif 00204 00205 /* Environment variable COLUMNS overrides setting of "co" if IGNORE_ENV 00206 is unset. */ 00207 if (_rl_screenwidth <= 0) 00208 { 00209 if (ignore_env == 0 && (ss = sh_get_env_value ("COLUMNS"))) 00210 _rl_screenwidth = atoi (ss); 00211 00212 #if !defined (__DJGPP__) 00213 if (_rl_screenwidth <= 0 && term_string_buffer) 00214 _rl_screenwidth = tgetnum ("co"); 00215 #endif 00216 } 00217 00218 /* Environment variable LINES overrides setting of "li" if IGNORE_ENV 00219 is unset. */ 00220 if (_rl_screenheight <= 0) 00221 { 00222 if (ignore_env == 0 && (ss = sh_get_env_value ("LINES"))) 00223 _rl_screenheight = atoi (ss); 00224 00225 #if !defined (__DJGPP__) 00226 if (_rl_screenheight <= 0 && term_string_buffer) 00227 _rl_screenheight = tgetnum ("li"); 00228 #endif 00229 } 00230 00231 /* If all else fails, default to 80x24 terminal. */ 00232 if (_rl_screenwidth <= 1) 00233 _rl_screenwidth = 80; 00234 00235 if (_rl_screenheight <= 0) 00236 _rl_screenheight = 24; 00237 00238 /* If we're being compiled as part of bash, set the environment 00239 variables $LINES and $COLUMNS to new values. Otherwise, just 00240 do a pair of putenv () or setenv () calls. */ 00241 sh_set_lines_and_columns (_rl_screenheight, _rl_screenwidth); 00242 00243 if (_rl_term_autowrap == 0) 00244 _rl_screenwidth--; 00245 00246 _rl_screenchars = _rl_screenwidth * _rl_screenheight; 00247 } 00248 00249 void 00250 _rl_set_screen_size (rows, cols) 00251 int rows, cols; 00252 { 00253 if (rows == 0 || cols == 0) 00254 return; 00255 00256 _rl_screenheight = rows; 00257 _rl_screenwidth = cols; 00258 00259 if (_rl_term_autowrap == 0) 00260 _rl_screenwidth--; 00261 00262 _rl_screenchars = _rl_screenwidth * _rl_screenheight; 00263 } 00264 00265 void 00266 rl_set_screen_size (rows, cols) 00267 int rows, cols; 00268 { 00269 _rl_set_screen_size (rows, cols); 00270 } 00271 00272 void 00273 rl_get_screen_size (rows, cols) 00274 int *rows, *cols; 00275 { 00276 if (rows) 00277 *rows = _rl_screenheight; 00278 if (cols) 00279 *cols = _rl_screenwidth; 00280 } 00281 00282 void 00283 rl_resize_terminal () 00284 { 00285 if (readline_echoing_p) 00286 { 00287 _rl_get_screen_size (fileno (rl_instream), 1); 00288 if (CUSTOM_REDISPLAY_FUNC ()) 00289 rl_forced_update_display (); 00290 else 00291 _rl_redisplay_after_sigwinch (); 00292 } 00293 } 00294 00295 struct _tc_string { 00296 const char *tc_var; 00297 char **tc_value; 00298 }; 00299 00300 /* This should be kept sorted, just in case we decide to change the 00301 search algorithm to something smarter. */ 00302 static struct _tc_string tc_strings[] = 00303 { 00304 { "@7", &_rl_term_at7 }, 00305 { "DC", &_rl_term_DC }, 00306 { "IC", &_rl_term_IC }, 00307 { "ce", &_rl_term_clreol }, 00308 { "cl", &_rl_term_clrpag }, 00309 { "cr", &_rl_term_cr }, 00310 { "dc", &_rl_term_dc }, 00311 { "ei", &_rl_term_ei }, 00312 { "ic", &_rl_term_ic }, 00313 { "im", &_rl_term_im }, 00314 { "kH", &_rl_term_kH }, /* home down ?? */ 00315 { "kI", &_rl_term_kI }, /* insert */ 00316 { "kd", &_rl_term_kd }, 00317 { "ke", &_rl_term_ke }, /* end keypad mode */ 00318 { "kh", &_rl_term_kh }, /* home */ 00319 { "kl", &_rl_term_kl }, 00320 { "kr", &_rl_term_kr }, 00321 { "ks", &_rl_term_ks }, /* start keypad mode */ 00322 { "ku", &_rl_term_ku }, 00323 { "le", &_rl_term_backspace }, 00324 { "mm", &_rl_term_mm }, 00325 { "mo", &_rl_term_mo }, 00326 #if defined (HACK_TERMCAP_MOTION) 00327 { "nd", &_rl_term_forward_char }, 00328 #endif 00329 { "pc", &_rl_term_pc }, 00330 { "up", &_rl_term_up }, 00331 { "vb", &_rl_visible_bell }, 00332 { "vs", &_rl_term_vs }, 00333 { "ve", &_rl_term_ve }, 00334 }; 00335 00336 #define NUM_TC_STRINGS (sizeof (tc_strings) / sizeof (struct _tc_string)) 00337 00338 /* Read the desired terminal capability strings into BP. The capabilities 00339 are described in the TC_STRINGS table. */ 00340 static void 00341 get_term_capabilities (bp) 00342 char **bp; 00343 { 00344 #if !defined (__DJGPP__) /* XXX - doesn't DJGPP have a termcap library? */ 00345 register int i; 00346 00347 for (i = 0; i < NUM_TC_STRINGS; i++) 00348 *(tc_strings[i].tc_value) = tgetstr ((char *)tc_strings[i].tc_var, bp); 00349 #endif 00350 tcap_initialized = 1; 00351 } 00352 00353 int 00354 _rl_init_terminal_io (terminal_name) 00355 const char *terminal_name; 00356 { 00357 const char *term; 00358 char *buffer; 00359 int tty, tgetent_ret; 00360 00361 term = terminal_name ? terminal_name : sh_get_env_value ("TERM"); 00362 _rl_term_clrpag = _rl_term_cr = _rl_term_clreol = (char *)NULL; 00363 tty = rl_instream ? fileno (rl_instream) : 0; 00364 _rl_screenwidth = _rl_screenheight = 0; 00365 00366 if (term == 0) 00367 term = "dumb"; 00368 00369 /* I've separated this out for later work on not calling tgetent at all 00370 if the calling application has supplied a custom redisplay function, 00371 (and possibly if the application has supplied a custom input function). */ 00372 if (CUSTOM_REDISPLAY_FUNC()) 00373 { 00374 tgetent_ret = -1; 00375 } 00376 else 00377 { 00378 if (term_string_buffer == 0) 00379 term_string_buffer = (char *)xmalloc(2032); 00380 00381 if (term_buffer == 0) 00382 term_buffer = (char *)xmalloc(4080); 00383 00384 buffer = term_string_buffer; 00385 00386 tgetent_ret = tgetent (term_buffer, term); 00387 } 00388 00389 if (tgetent_ret <= 0) 00390 { 00391 FREE (term_string_buffer); 00392 FREE (term_buffer); 00393 buffer = term_buffer = term_string_buffer = (char *)NULL; 00394 00395 _rl_term_autowrap = 0; /* used by _rl_get_screen_size */ 00396 00397 #if defined (__EMX__) 00398 _emx_get_screensize (&_rl_screenwidth, &_rl_screenheight); 00399 _rl_screenwidth--; 00400 #else /* !__EMX__ */ 00401 _rl_get_screen_size (tty, 0); 00402 #endif /* !__EMX__ */ 00403 00404 /* Defaults. */ 00405 if (_rl_screenwidth <= 0 || _rl_screenheight <= 0) 00406 { 00407 _rl_screenwidth = 79; 00408 _rl_screenheight = 24; 00409 } 00410 00411 /* Everything below here is used by the redisplay code (tputs). */ 00412 _rl_screenchars = _rl_screenwidth * _rl_screenheight; 00413 _rl_term_cr = "\r"; 00414 _rl_term_im = _rl_term_ei = _rl_term_ic = _rl_term_IC = (char *)NULL; 00415 _rl_term_up = _rl_term_dc = _rl_term_DC = _rl_visible_bell = (char *)NULL; 00416 _rl_term_ku = _rl_term_kd = _rl_term_kl = _rl_term_kr = (char *)NULL; 00417 _rl_term_kh = _rl_term_kH = _rl_term_kI = (char *)NULL; 00418 _rl_term_ks = _rl_term_ke = _rl_term_at7 = (char *)NULL; 00419 _rl_term_mm = _rl_term_mo = (char *)NULL; 00420 _rl_term_ve = _rl_term_vs = (char *)NULL; 00421 #if defined (HACK_TERMCAP_MOTION) 00422 term_forward_char = (char *)NULL; 00423 #endif 00424 _rl_terminal_can_insert = term_has_meta = 0; 00425 00426 /* Reasonable defaults for tgoto(). Readline currently only uses 00427 tgoto if _rl_term_IC or _rl_term_DC is defined, but just in case we 00428 change that later... */ 00429 PC = '\0'; 00430 BC = _rl_term_backspace = "\b"; 00431 UP = _rl_term_up; 00432 00433 return 0; 00434 } 00435 00436 get_term_capabilities (&buffer); 00437 00438 /* Set up the variables that the termcap library expects the application 00439 to provide. */ 00440 PC = _rl_term_pc ? *_rl_term_pc : 0; 00441 BC = _rl_term_backspace; 00442 UP = _rl_term_up; 00443 00444 if (!_rl_term_cr) 00445 _rl_term_cr = "\r"; 00446 00447 _rl_term_autowrap = tgetflag ("am") && tgetflag ("xn"); 00448 00449 _rl_get_screen_size (tty, 0); 00450 00451 /* "An application program can assume that the terminal can do 00452 character insertion if *any one of* the capabilities `IC', 00453 `im', `ic' or `ip' is provided." But we can't do anything if 00454 only `ip' is provided, so... */ 00455 _rl_terminal_can_insert = (_rl_term_IC || _rl_term_im || _rl_term_ic); 00456 00457 /* Check to see if this terminal has a meta key and clear the capability 00458 variables if there is none. */ 00459 term_has_meta = (tgetflag ("km") || tgetflag ("MT")); 00460 if (!term_has_meta) 00461 _rl_term_mm = _rl_term_mo = (char *)NULL; 00462 00463 /* Attempt to find and bind the arrow keys. Do not override already 00464 bound keys in an overzealous attempt, however. */ 00465 00466 bind_termcap_arrow_keys (emacs_standard_keymap); 00467 00468 #if defined (VI_MODE) 00469 bind_termcap_arrow_keys (vi_movement_keymap); 00470 bind_termcap_arrow_keys (vi_insertion_keymap); 00471 #endif /* VI_MODE */ 00472 00473 return 0; 00474 } 00475 00476 /* Bind the arrow key sequences from the termcap description in MAP. */ 00477 static void 00478 bind_termcap_arrow_keys (map) 00479 Keymap map; 00480 { 00481 Keymap xkeymap; 00482 00483 xkeymap = _rl_keymap; 00484 _rl_keymap = map; 00485 00486 rl_bind_keyseq_if_unbound (_rl_term_ku, rl_get_previous_history); 00487 rl_bind_keyseq_if_unbound (_rl_term_kd, rl_get_next_history); 00488 rl_bind_keyseq_if_unbound (_rl_term_kr, rl_forward_char); 00489 rl_bind_keyseq_if_unbound (_rl_term_kl, rl_backward_char); 00490 00491 rl_bind_keyseq_if_unbound (_rl_term_kh, rl_beg_of_line); /* Home */ 00492 rl_bind_keyseq_if_unbound (_rl_term_at7, rl_end_of_line); /* End */ 00493 00494 _rl_keymap = xkeymap; 00495 } 00496 00497 char * 00498 rl_get_termcap (cap) 00499 const char *cap; 00500 { 00501 register int i; 00502 00503 if (tcap_initialized == 0) 00504 return ((char *)NULL); 00505 for (i = 0; i < NUM_TC_STRINGS; i++) 00506 { 00507 if (tc_strings[i].tc_var[0] == cap[0] && strcmp (tc_strings[i].tc_var, cap) == 0) 00508 return *(tc_strings[i].tc_value); 00509 } 00510 return ((char *)NULL); 00511 } 00512 00513 /* Re-initialize the terminal considering that the TERM/TERMCAP variable 00514 has changed. */ 00515 int 00516 rl_reset_terminal (terminal_name) 00517 const char *terminal_name; 00518 { 00519 _rl_init_terminal_io (terminal_name); 00520 return 0; 00521 } 00522 00523 /* A function for the use of tputs () */ 00524 #ifdef _MINIX 00525 void 00526 _rl_output_character_function (c) 00527 int c; 00528 { 00529 putc (c, _rl_out_stream); 00530 } 00531 #else /* !_MINIX */ 00532 int 00533 _rl_output_character_function (c) 00534 int c; 00535 { 00536 return putc (c, _rl_out_stream); 00537 } 00538 #endif /* !_MINIX */ 00539 00540 /* Write COUNT characters from STRING to the output stream. */ 00541 void 00542 _rl_output_some_chars (string, count) 00543 const char *string; 00544 int count; 00545 { 00546 fwrite (string, 1, count, _rl_out_stream); 00547 } 00548 00549 /* Move the cursor back. */ 00550 int 00551 _rl_backspace (count) 00552 int count; 00553 { 00554 register int i; 00555 00556 if (_rl_term_backspace) 00557 for (i = 0; i < count; i++) 00558 tputs (_rl_term_backspace, 1, _rl_output_character_function); 00559 else 00560 for (i = 0; i < count; i++) 00561 putc ('\b', _rl_out_stream); 00562 return 0; 00563 } 00564 00565 /* Move to the start of the next line. */ 00566 int 00567 rl_crlf () 00568 { 00569 #if defined (NEW_TTY_DRIVER) 00570 if (_rl_term_cr) 00571 tputs (_rl_term_cr, 1, _rl_output_character_function); 00572 #endif /* NEW_TTY_DRIVER */ 00573 putc ('\n', _rl_out_stream); 00574 return 0; 00575 } 00576 00577 /* Ring the terminal bell. */ 00578 int 00579 rl_ding () 00580 { 00581 if (readline_echoing_p) 00582 { 00583 switch (_rl_bell_preference) 00584 { 00585 case NO_BELL: 00586 default: 00587 break; 00588 case VISIBLE_BELL: 00589 if (_rl_visible_bell) 00590 { 00591 tputs (_rl_visible_bell, 1, _rl_output_character_function); 00592 break; 00593 } 00594 /* FALLTHROUGH */ 00595 case AUDIBLE_BELL: 00596 fprintf (stderr, "\007"); 00597 fflush (stderr); 00598 break; 00599 } 00600 return (0); 00601 } 00602 return (-1); 00603 } 00604 00605 /* **************************************************************** */ 00606 /* */ 00607 /* Controlling the Meta Key and Keypad */ 00608 /* */ 00609 /* **************************************************************** */ 00610 00611 void 00612 _rl_enable_meta_key () 00613 { 00614 #if !defined (__DJGPP__) 00615 if (term_has_meta && _rl_term_mm) 00616 tputs (_rl_term_mm, 1, _rl_output_character_function); 00617 #endif 00618 } 00619 00620 void 00621 _rl_control_keypad (on) 00622 int on; 00623 { 00624 #if !defined (__DJGPP__) 00625 if (on && _rl_term_ks) 00626 tputs (_rl_term_ks, 1, _rl_output_character_function); 00627 else if (!on && _rl_term_ke) 00628 tputs (_rl_term_ke, 1, _rl_output_character_function); 00629 #endif 00630 } 00631 00632 /* **************************************************************** */ 00633 /* */ 00634 /* Controlling the Cursor */ 00635 /* */ 00636 /* **************************************************************** */ 00637 00638 /* Set the cursor appropriately depending on IM, which is one of the 00639 insert modes (insert or overwrite). Insert mode gets the normal 00640 cursor. Overwrite mode gets a very visible cursor. Only does 00641 anything if we have both capabilities. */ 00642 void 00643 _rl_set_cursor (im, force) 00644 int im, force; 00645 { 00646 if (_rl_term_ve && _rl_term_vs) 00647 { 00648 if (force || im != rl_insert_mode) 00649 { 00650 if (im == RL_IM_OVERWRITE) 00651 tputs (_rl_term_vs, 1, _rl_output_character_function); 00652 else 00653 tputs (_rl_term_ve, 1, _rl_output_character_function); 00654 } 00655 } 00656 }
1.4.7

