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