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