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