xref: /openbsd/gnu/lib/libreadline/terminal.c (revision 146f3f6a)
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