terminal.c revision 157188
1/* $FreeBSD: head/contrib/libreadline/terminal.c 157188 2006-03-27 23:11:32Z 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;
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 (rows > 0)
279    _rl_screenheight = rows;
280  if (cols > 0)
281    {
282      _rl_screenwidth = cols;
283      if (_rl_term_autowrap == 0)
284	_rl_screenwidth--;
285    }
286
287  if (rows > 0 || cols > 0)
288    _rl_screenchars = _rl_screenwidth * _rl_screenheight;
289}
290
291void
292rl_set_screen_size (rows, cols)
293     int rows, cols;
294{
295  _rl_set_screen_size (rows, cols);
296}
297
298void
299rl_get_screen_size (rows, cols)
300     int *rows, *cols;
301{
302  if (rows)
303    *rows = _rl_screenheight;
304  if (cols)
305    *cols = _rl_screenwidth;
306}
307
308void
309rl_reset_screen_size ()
310{
311  _rl_get_screen_size (fileno (rl_instream), 0);
312}
313
314void
315rl_resize_terminal ()
316{
317  if (readline_echoing_p)
318    {
319      _rl_get_screen_size (fileno (rl_instream), 1);
320      if (CUSTOM_REDISPLAY_FUNC ())
321	rl_forced_update_display ();
322      else
323	_rl_redisplay_after_sigwinch ();
324    }
325}
326
327struct _tc_string {
328     const char *tc_var;
329     char **tc_value;
330};
331
332/* This should be kept sorted, just in case we decide to change the
333   search algorithm to something smarter. */
334static struct _tc_string tc_strings[] =
335{
336  { "@7", &_rl_term_at7 },
337  { "DC", &_rl_term_DC },
338  { "IC", &_rl_term_IC },
339  { "ce", &_rl_term_clreol },
340  { "cl", &_rl_term_clrpag },
341  { "cr", &_rl_term_cr },
342  { "dc", &_rl_term_dc },
343  { "ei", &_rl_term_ei },
344  { "ic", &_rl_term_ic },
345  { "im", &_rl_term_im },
346  { "kD", &_rl_term_kD },	/* delete */
347  { "kH", &_rl_term_kH },	/* home down ?? */
348  { "kI", &_rl_term_kI },	/* insert */
349  { "kd", &_rl_term_kd },
350  { "ke", &_rl_term_ke },	/* end keypad mode */
351  { "kh", &_rl_term_kh },	/* home */
352  { "kl", &_rl_term_kl },
353  { "kr", &_rl_term_kr },
354  { "ks", &_rl_term_ks },	/* start keypad mode */
355  { "ku", &_rl_term_ku },
356  { "le", &_rl_term_backspace },
357  { "mm", &_rl_term_mm },
358  { "mo", &_rl_term_mo },
359#if defined (HACK_TERMCAP_MOTION)
360  { "nd", &_rl_term_forward_char },
361#endif
362  { "pc", &_rl_term_pc },
363  { "up", &_rl_term_up },
364  { "vb", &_rl_visible_bell },
365  { "vs", &_rl_term_vs },
366  { "ve", &_rl_term_ve },
367};
368
369#define NUM_TC_STRINGS (sizeof (tc_strings) / sizeof (struct _tc_string))
370
371/* Read the desired terminal capability strings into BP.  The capabilities
372   are described in the TC_STRINGS table. */
373static void
374get_term_capabilities (bp)
375     char **bp;
376{
377#if !defined (__DJGPP__)	/* XXX - doesn't DJGPP have a termcap library? */
378  register int i;
379
380  for (i = 0; i < NUM_TC_STRINGS; i++)
381    *(tc_strings[i].tc_value) = tgetstr ((char *)tc_strings[i].tc_var, bp);
382#endif
383  tcap_initialized = 1;
384}
385
386int
387_rl_init_terminal_io (terminal_name)
388     const char *terminal_name;
389{
390  const char *term;
391  char *buffer;
392  int tty, tgetent_ret;
393
394  term = terminal_name ? terminal_name : sh_get_env_value ("TERM");
395  _rl_term_clrpag = _rl_term_cr = _rl_term_clreol = (char *)NULL;
396  tty = rl_instream ? fileno (rl_instream) : 0;
397
398  if (term == 0)
399    term = "dumb";
400
401  /* I've separated this out for later work on not calling tgetent at all
402     if the calling application has supplied a custom redisplay function,
403     (and possibly if the application has supplied a custom input function). */
404  if (CUSTOM_REDISPLAY_FUNC())
405    {
406      tgetent_ret = -1;
407    }
408  else
409    {
410      if (term_string_buffer == 0)
411	term_string_buffer = (char *)xmalloc(2032);
412
413      if (term_buffer == 0)
414	term_buffer = (char *)xmalloc(4080);
415
416      buffer = term_string_buffer;
417
418      tgetent_ret = tgetent (term_buffer, term);
419    }
420
421  if (tgetent_ret <= 0)
422    {
423      FREE (term_string_buffer);
424      FREE (term_buffer);
425      buffer = term_buffer = term_string_buffer = (char *)NULL;
426
427      _rl_term_autowrap = 0;	/* used by _rl_get_screen_size */
428
429      /* Allow calling application to set default height and width, using
430	 rl_set_screen_size */
431      if (_rl_screenwidth <= 0 || _rl_screenheight <= 0)
432	{
433#if defined (__EMX__)
434	  _emx_get_screensize (&_rl_screenwidth, &_rl_screenheight);
435	  _rl_screenwidth--;
436#else /* !__EMX__ */
437	  _rl_get_screen_size (tty, 0);
438#endif /* !__EMX__ */
439	}
440
441      /* Defaults. */
442      if (_rl_screenwidth <= 0 || _rl_screenheight <= 0)
443        {
444	  _rl_screenwidth = 79;
445	  _rl_screenheight = 24;
446        }
447
448      /* Everything below here is used by the redisplay code (tputs). */
449      _rl_screenchars = _rl_screenwidth * _rl_screenheight;
450      _rl_term_cr = "\r";
451      _rl_term_im = _rl_term_ei = _rl_term_ic = _rl_term_IC = (char *)NULL;
452      _rl_term_up = _rl_term_dc = _rl_term_DC = _rl_visible_bell = (char *)NULL;
453      _rl_term_ku = _rl_term_kd = _rl_term_kl = _rl_term_kr = (char *)NULL;
454      _rl_term_kh = _rl_term_kH = _rl_term_kI = _rl_term_kD = (char *)NULL;
455      _rl_term_ks = _rl_term_ke = _rl_term_at7 = (char *)NULL;
456      _rl_term_mm = _rl_term_mo = (char *)NULL;
457      _rl_term_ve = _rl_term_vs = (char *)NULL;
458#if defined (HACK_TERMCAP_MOTION)
459      term_forward_char = (char *)NULL;
460#endif
461      _rl_terminal_can_insert = term_has_meta = 0;
462
463      /* Reasonable defaults for tgoto().  Readline currently only uses
464         tgoto if _rl_term_IC or _rl_term_DC is defined, but just in case we
465         change that later... */
466      PC = '\0';
467      BC = _rl_term_backspace = "\b";
468      UP = _rl_term_up;
469
470      return 0;
471    }
472
473  get_term_capabilities (&buffer);
474
475  /* Set up the variables that the termcap library expects the application
476     to provide. */
477  PC = _rl_term_pc ? *_rl_term_pc : 0;
478  BC = _rl_term_backspace;
479  UP = _rl_term_up;
480
481  if (!_rl_term_cr)
482    _rl_term_cr = "\r";
483
484  _rl_term_autowrap = tgetflag ("am") && tgetflag ("xn");
485
486  /* Allow calling application to set default height and width, using
487     rl_set_screen_size */
488  if (_rl_screenwidth <= 0 || _rl_screenheight <= 0)
489    _rl_get_screen_size (tty, 0);
490
491  /* "An application program can assume that the terminal can do
492      character insertion if *any one of* the capabilities `IC',
493      `im', `ic' or `ip' is provided."  But we can't do anything if
494      only `ip' is provided, so... */
495  _rl_terminal_can_insert = (_rl_term_IC || _rl_term_im || _rl_term_ic);
496
497  /* Check to see if this terminal has a meta key and clear the capability
498     variables if there is none. */
499  term_has_meta = (tgetflag ("km") || tgetflag ("MT"));
500  if (!term_has_meta)
501    _rl_term_mm = _rl_term_mo = (char *)NULL;
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_keyseq_if_unbound (_rl_term_ku, rl_get_previous_history);
527  rl_bind_keyseq_if_unbound (_rl_term_kd, rl_get_next_history);
528  rl_bind_keyseq_if_unbound (_rl_term_kr, rl_forward_char);
529  rl_bind_keyseq_if_unbound (_rl_term_kl, rl_backward_char);
530
531  rl_bind_keyseq_if_unbound (_rl_term_kh, rl_beg_of_line);	/* Home */
532  rl_bind_keyseq_if_unbound (_rl_term_at7, rl_end_of_line);	/* End */
533
534  rl_bind_keyseq_if_unbound (_rl_term_kD, rl_delete);
535
536  _rl_keymap = xkeymap;
537}
538
539char *
540rl_get_termcap (cap)
541     const char *cap;
542{
543  register int i;
544
545  if (tcap_initialized == 0)
546    return ((char *)NULL);
547  for (i = 0; i < NUM_TC_STRINGS; i++)
548    {
549      if (tc_strings[i].tc_var[0] == cap[0] && strcmp (tc_strings[i].tc_var, cap) == 0)
550        return *(tc_strings[i].tc_value);
551    }
552  return ((char *)NULL);
553}
554
555/* Re-initialize the terminal considering that the TERM/TERMCAP variable
556   has changed. */
557int
558rl_reset_terminal (terminal_name)
559     const char *terminal_name;
560{
561  _rl_screenwidth = _rl_screenheight = 0;
562  _rl_init_terminal_io (terminal_name);
563  return 0;
564}
565
566/* A function for the use of tputs () */
567#ifdef _MINIX
568void
569_rl_output_character_function (c)
570     int c;
571{
572  putc (c, _rl_out_stream);
573}
574#else /* !_MINIX */
575int
576_rl_output_character_function (c)
577     int c;
578{
579  return putc (c, _rl_out_stream);
580}
581#endif /* !_MINIX */
582
583/* Write COUNT characters from STRING to the output stream. */
584void
585_rl_output_some_chars (string, count)
586     const char *string;
587     int count;
588{
589  fwrite (string, 1, count, _rl_out_stream);
590}
591
592/* Move the cursor back. */
593int
594_rl_backspace (count)
595     int count;
596{
597  register int i;
598
599  if (_rl_term_backspace)
600    for (i = 0; i < count; i++)
601      tputs (_rl_term_backspace, 1, _rl_output_character_function);
602  else
603    for (i = 0; i < count; i++)
604      putc ('\b', _rl_out_stream);
605  return 0;
606}
607
608/* Move to the start of the next line. */
609int
610rl_crlf ()
611{
612#if defined (NEW_TTY_DRIVER)
613  if (_rl_term_cr)
614    tputs (_rl_term_cr, 1, _rl_output_character_function);
615#endif /* NEW_TTY_DRIVER */
616  putc ('\n', _rl_out_stream);
617  return 0;
618}
619
620/* Ring the terminal bell. */
621int
622rl_ding ()
623{
624  if (readline_echoing_p)
625    {
626      switch (_rl_bell_preference)
627        {
628	case NO_BELL:
629	default:
630	  break;
631	case VISIBLE_BELL:
632	  if (_rl_visible_bell)
633	    {
634	      tputs (_rl_visible_bell, 1, _rl_output_character_function);
635	      break;
636	    }
637	  /* FALLTHROUGH */
638	case AUDIBLE_BELL:
639	  fprintf (stderr, "\007");
640	  fflush (stderr);
641	  break;
642        }
643      return (0);
644    }
645  return (-1);
646}
647
648/* **************************************************************** */
649/*								    */
650/*	 	Controlling the Meta Key and Keypad		    */
651/*								    */
652/* **************************************************************** */
653
654void
655_rl_enable_meta_key ()
656{
657#if !defined (__DJGPP__)
658  if (term_has_meta && _rl_term_mm)
659    tputs (_rl_term_mm, 1, _rl_output_character_function);
660#endif
661}
662
663void
664_rl_control_keypad (on)
665     int on;
666{
667#if !defined (__DJGPP__)
668  if (on && _rl_term_ks)
669    tputs (_rl_term_ks, 1, _rl_output_character_function);
670  else if (!on && _rl_term_ke)
671    tputs (_rl_term_ke, 1, _rl_output_character_function);
672#endif
673}
674
675/* **************************************************************** */
676/*								    */
677/*	 		Controlling the Cursor			    */
678/*								    */
679/* **************************************************************** */
680
681/* Set the cursor appropriately depending on IM, which is one of the
682   insert modes (insert or overwrite).  Insert mode gets the normal
683   cursor.  Overwrite mode gets a very visible cursor.  Only does
684   anything if we have both capabilities. */
685void
686_rl_set_cursor (im, force)
687     int im, force;
688{
689  if (_rl_term_ve && _rl_term_vs)
690    {
691      if (force || im != rl_insert_mode)
692	{
693	  if (im == RL_IM_OVERWRITE)
694	    tputs (_rl_term_vs, 1, _rl_output_character_function);
695	  else
696	    tputs (_rl_term_ve, 1, _rl_output_character_function);
697	}
698    }
699}
700