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
76 static char *term_buffer = (char *)NULL;
77 static char *term_string_buffer = (char *)NULL;
78
79 static int tcap_initialized;
80
81 #if !defined (__linux__)
82 # if defined (__EMX__) || defined (NEED_EXTERN_PC)
83 extern
84 # endif /* __EMX__ || NEED_EXTERN_PC */
85 char PC, *BC, *UP;
86 #endif /* __linux__ */
87
88 /* Some strings to control terminal actions. These are output by tputs (). */
89 char *_rl_term_clreol;
90 char *_rl_term_clrpag;
91 char *_rl_term_cr;
92 char *_rl_term_backspace;
93 char *_rl_term_goto;
94 char *_rl_term_pc;
95
96 /* Non-zero if we determine that the terminal can do character insertion. */
97 int _rl_terminal_can_insert = 0;
98
99 /* How to insert characters. */
100 char *_rl_term_im;
101 char *_rl_term_ei;
102 char *_rl_term_ic;
103 char *_rl_term_ip;
104 char *_rl_term_IC;
105
106 /* How to delete characters. */
107 char *_rl_term_dc;
108 char *_rl_term_DC;
109
110 #if defined (HACK_TERMCAP_MOTION)
111 char *_rl_term_forward_char;
112 #endif /* HACK_TERMCAP_MOTION */
113
114 /* How to go up a line. */
115 char *_rl_term_up;
116
117 /* A visible bell; char if the terminal can be made to flash the screen. */
118 static char *_rl_visible_bell;
119
120 /* Non-zero means the terminal can auto-wrap lines. */
121 int _rl_term_autowrap;
122
123 /* Non-zero means that this terminal has a meta key. */
124 static int term_has_meta;
125
126 /* The sequences to write to turn on and off the meta key, if this
127 terminal has one. */
128 static char *_rl_term_mm;
129 static char *_rl_term_mo;
130
131 /* The key sequences output by the arrow keys, if this terminal has any. */
132 static char *_rl_term_ku;
133 static char *_rl_term_kd;
134 static char *_rl_term_kr;
135 static char *_rl_term_kl;
136
137 /* How to initialize and reset the arrow keys, if this terminal has any. */
138 static char *_rl_term_ks;
139 static char *_rl_term_ke;
140
141 /* The key sequences sent by the Home and End keys, if any. */
142 static char *_rl_term_kh;
143 static char *_rl_term_kH;
144 static char *_rl_term_at7; /* @7 */
145
146 /* Insert key */
147 static char *_rl_term_kI;
148
149 /* Cursor control */
150 static char *_rl_term_vs; /* very visible */
151 static char *_rl_term_ve; /* normal */
152
153 static void bind_termcap_arrow_keys PARAMS((Keymap));
154
155 /* Variables that hold the screen dimensions, used by the display code. */
156 int _rl_screenwidth, _rl_screenheight, _rl_screenchars;
157
158 /* Non-zero means the user wants to enable the keypad. */
159 int _rl_enable_keypad;
160
161 /* Non-zero means the user wants to enable a meta key. */
162 int _rl_enable_meta = 1;
163
164 #if defined (__EMX__)
165 static void
_emx_get_screensize(swp,shp)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. */
184 void
_rl_get_screen_size(tty,ignore_env)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
249 void
_rl_set_screen_size(rows,cols)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
265 void
rl_set_screen_size(rows,cols)266 rl_set_screen_size (rows, cols)
267 int rows, cols;
268 {
269 _rl_set_screen_size (rows, cols);
270 }
271
272 void
rl_get_screen_size(rows,cols)273 rl_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
282 void
rl_resize_terminal()283 rl_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
295 struct _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. */
302 static 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. */
340 static void
get_term_capabilities(bp)341 get_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
357 int
_rl_init_terminal_io(terminal_name)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. */
481 static void
bind_termcap_arrow_keys(map)482 bind_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
501 char *
rl_get_termcap(cap)502 rl_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. */
519 int
rl_reset_terminal(terminal_name)520 rl_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
529 void
_rl_output_character_function(c)530 _rl_output_character_function (c)
531 int c;
532 {
533 putc (c, _rl_out_stream);
534 }
535 #else /* !_MINIX */
536 int
_rl_output_character_function(c)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. */
545 void
_rl_output_some_chars(string,count)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. */
554 int
_rl_backspace(count)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. */
570 int
rl_crlf()571 rl_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. */
582 int
rl_ding()583 rl_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
615 void
_rl_enable_meta_key()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
624 void
_rl_control_keypad(on)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. */
646 void
_rl_set_cursor(im,force)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