terminal.c revision 136758
1/* $FreeBSD: head/contrib/libreadline/terminal.c 136758 2004-10-21 20:02:02Z peter $ */
2
3/* terminal.c -- controlling the terminal with termcap. */
4
5/* Copyright (C) 1996 Free Software Foundation, Inc.
6
7   This file is part of the GNU Readline Library, a library for
8   reading lines of text with interactive input and history editing.
9
10   The GNU Readline Library is free software; you can redistribute it
11   and/or modify it under the terms of the GNU General Public License
12   as published by the Free Software Foundation; either version 2, or
13   (at your option) any later version.
14
15   The GNU Readline Library is distributed in the hope that it will be
16   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
17   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License for more details.
19
20   The GNU General Public License is often shipped with GNU software, and
21   is generally kept in a file called COPYING or LICENSE.  If you do not
22   have a copy of the license, write to the Free Software Foundation,
23   59 Temple Place, Suite 330, Boston, MA 02111 USA. */
24#define READLINE_LIBRARY
25
26#if defined (HAVE_CONFIG_H)
27#  include <config.h>
28#endif
29
30#include <sys/types.h>
31#include "posixstat.h"
32#include <fcntl.h>
33#if defined (HAVE_SYS_FILE_H)
34#  include <sys/file.h>
35#endif /* HAVE_SYS_FILE_H */
36
37#if defined (HAVE_UNISTD_H)
38#  include <unistd.h>
39#endif /* HAVE_UNISTD_H */
40
41#if defined (HAVE_STDLIB_H)
42#  include <stdlib.h>
43#else
44#  include "ansi_stdlib.h"
45#endif /* HAVE_STDLIB_H */
46
47#if defined (HAVE_LOCALE_H)
48#  include <locale.h>
49#endif
50
51#include <stdio.h>
52
53/* System-specific feature definitions and include files. */
54#include "rldefs.h"
55
56#if defined (GWINSZ_IN_SYS_IOCTL) && !defined (TIOCGWINSZ)
57#  include <sys/ioctl.h>
58#endif /* GWINSZ_IN_SYS_IOCTL && !TIOCGWINSZ */
59
60#include "rltty.h"
61#include "tcap.h"
62
63/* Some standard library routines. */
64#include "readline.h"
65#include "history.h"
66
67#include "rlprivate.h"
68#include "rlshell.h"
69#include "xmalloc.h"
70
71#define CUSTOM_REDISPLAY_FUNC() (rl_redisplay_function != rl_redisplay)
72#define CUSTOM_INPUT_FUNC() (rl_getc_function != rl_getc)
73
74/* **************************************************************** */
75/*								    */
76/*			Terminal and Termcap			    */
77/*								    */
78/* **************************************************************** */
79
80static char *term_buffer = (char *)NULL;
81static char *term_string_buffer = (char *)NULL;
82
83static int tcap_initialized;
84
85#if !defined (__linux__)
86#  if defined (__EMX__) || defined (NEED_EXTERN_PC)
87extern
88#  endif /* __EMX__ || NEED_EXTERN_PC */
89char PC, *BC, *UP;
90#endif /* __linux__ */
91
92/* Some strings to control terminal actions.  These are output by tputs (). */
93char *_rl_term_clreol;
94char *_rl_term_clrpag;
95char *_rl_term_cr;
96char *_rl_term_backspace;
97char *_rl_term_goto;
98char *_rl_term_pc;
99
100/* Non-zero if we determine that the terminal can do character insertion. */
101int _rl_terminal_can_insert = 0;
102
103/* How to insert characters. */
104char *_rl_term_im;
105char *_rl_term_ei;
106char *_rl_term_ic;
107char *_rl_term_ip;
108char *_rl_term_IC;
109
110/* How to delete characters. */
111char *_rl_term_dc;
112char *_rl_term_DC;
113
114#if defined (HACK_TERMCAP_MOTION)
115char *_rl_term_forward_char;
116#endif  /* HACK_TERMCAP_MOTION */
117
118/* How to go up a line. */
119char *_rl_term_up;
120
121/* A visible bell; char if the terminal can be made to flash the screen. */
122static char *_rl_visible_bell;
123
124/* Non-zero means the terminal can auto-wrap lines. */
125int _rl_term_autowrap;
126
127/* Non-zero means that this terminal has a meta key. */
128static int term_has_meta;
129
130/* The sequences to write to turn on and off the meta key, if this
131   terminal has one. */
132static char *_rl_term_mm;
133static char *_rl_term_mo;
134
135/* The key sequences output by the arrow keys, if this terminal has any. */
136static char *_rl_term_ku;
137static char *_rl_term_kd;
138static char *_rl_term_kr;
139static char *_rl_term_kl;
140
141/* How to initialize and reset the arrow keys, if this terminal has any. */
142static char *_rl_term_ks;
143static char *_rl_term_ke;
144
145/* The key sequences sent by the Home and End keys, if any. */
146static char *_rl_term_kh;
147static char *_rl_term_kH;
148static char *_rl_term_at7;	/* @7 */
149
150/* Insert key */
151static char *_rl_term_kI;
152
153/* Cursor control */
154static char *_rl_term_vs;	/* very visible */
155static char *_rl_term_ve;	/* normal */
156
157static void bind_termcap_arrow_keys PARAMS((Keymap));
158
159/* Variables that hold the screen dimensions, used by the display code. */
160int _rl_screenwidth, _rl_screenheight, _rl_screenchars;
161
162/* Non-zero means the user wants to enable the keypad. */
163int _rl_enable_keypad;
164
165/* Non-zero means the user wants to enable a meta key. */
166int _rl_enable_meta = 1;
167
168#if defined (__EMX__)
169static void
170_emx_get_screensize (swp, shp)
171     int *swp, *shp;
172{
173  int sz[2];
174
175  _scrsize (sz);
176
177  if (swp)
178    *swp = sz[0];
179  if (shp)
180    *shp = sz[1];
181}
182#endif
183
184/* Get readline's idea of the screen size.  TTY is a file descriptor open
185   to the terminal.  If IGNORE_ENV is true, we do not pay attention to the
186   values of $LINES and $COLUMNS.  The tests for TERM_STRING_BUFFER being
187   non-null serve to check whether or not we have initialized termcap. */
188void
189_rl_get_screen_size (tty, ignore_env)
190     int tty, ignore_env;
191{
192  char *ss;
193#if defined (TIOCGWINSZ)
194  struct winsize window_size;
195#endif /* TIOCGWINSZ */
196
197#if defined (TIOCGWINSZ)
198  if (ioctl (tty, TIOCGWINSZ, &window_size) == 0)
199    {
200      _rl_screenwidth = (int) window_size.ws_col;
201      _rl_screenheight = (int) window_size.ws_row;
202    }
203#endif /* TIOCGWINSZ */
204
205#if defined (__EMX__)
206  _emx_get_screensize (&_rl_screenwidth, &_rl_screenheight);
207#endif
208
209  /* Environment variable COLUMNS overrides setting of "co" if IGNORE_ENV
210     is unset. */
211  if (_rl_screenwidth <= 0)
212    {
213      if (ignore_env == 0 && (ss = sh_get_env_value ("COLUMNS")))
214	_rl_screenwidth = atoi (ss);
215
216#if !defined (__DJGPP__)
217      if (_rl_screenwidth <= 0 && term_string_buffer)
218	_rl_screenwidth = tgetnum ("co");
219#endif
220    }
221
222  /* Environment variable LINES overrides setting of "li" if IGNORE_ENV
223     is unset. */
224  if (_rl_screenheight <= 0)
225    {
226      if (ignore_env == 0 && (ss = sh_get_env_value ("LINES")))
227	_rl_screenheight = atoi (ss);
228
229#if !defined (__DJGPP__)
230      if (_rl_screenheight <= 0 && term_string_buffer)
231	_rl_screenheight = tgetnum ("li");
232#endif
233    }
234
235  /* If all else fails, default to 80x24 terminal. */
236  if (_rl_screenwidth <= 1)
237    _rl_screenwidth = 80;
238
239  if (_rl_screenheight <= 0)
240    _rl_screenheight = 24;
241
242  /* If we're being compiled as part of bash, set the environment
243     variables $LINES and $COLUMNS to new values.  Otherwise, just
244     do a pair of putenv () or setenv () calls. */
245  sh_set_lines_and_columns (_rl_screenheight, _rl_screenwidth);
246
247  if (_rl_term_autowrap == 0)
248    _rl_screenwidth--;
249
250  _rl_screenchars = _rl_screenwidth * _rl_screenheight;
251}
252
253void
254_rl_set_screen_size (rows, cols)
255     int rows, cols;
256{
257  if (rows == 0 || cols == 0)
258    return;
259
260  _rl_screenheight = rows;
261  _rl_screenwidth = cols;
262
263  if (_rl_term_autowrap == 0)
264    _rl_screenwidth--;
265
266  _rl_screenchars = _rl_screenwidth * _rl_screenheight;
267}
268
269void
270rl_set_screen_size (rows, cols)
271     int rows, cols;
272{
273  _rl_set_screen_size (rows, cols);
274}
275
276void
277rl_get_screen_size (rows, cols)
278     int *rows, *cols;
279{
280  if (rows)
281    *rows = _rl_screenheight;
282  if (cols)
283    *cols = _rl_screenwidth;
284}
285
286void
287rl_resize_terminal ()
288{
289  if (readline_echoing_p)
290    {
291      _rl_get_screen_size (fileno (rl_instream), 1);
292      if (CUSTOM_REDISPLAY_FUNC ())
293	rl_forced_update_display ();
294      else
295	_rl_redisplay_after_sigwinch ();
296    }
297}
298
299struct _tc_string {
300     const char *tc_var;
301     char **tc_value;
302};
303
304/* This should be kept sorted, just in case we decide to change the
305   search algorithm to something smarter. */
306static struct _tc_string tc_strings[] =
307{
308  { "@7", &_rl_term_at7 },
309  { "DC", &_rl_term_DC },
310  { "IC", &_rl_term_IC },
311  { "ce", &_rl_term_clreol },
312  { "cl", &_rl_term_clrpag },
313  { "cr", &_rl_term_cr },
314  { "dc", &_rl_term_dc },
315  { "ei", &_rl_term_ei },
316  { "ic", &_rl_term_ic },
317  { "im", &_rl_term_im },
318  { "kH", &_rl_term_kH },	/* home down ?? */
319  { "kI", &_rl_term_kI },	/* insert */
320  { "kd", &_rl_term_kd },
321  { "ke", &_rl_term_ke },	/* end keypad mode */
322  { "kh", &_rl_term_kh },	/* home */
323  { "kl", &_rl_term_kl },
324  { "kr", &_rl_term_kr },
325  { "ks", &_rl_term_ks },	/* start keypad mode */
326  { "ku", &_rl_term_ku },
327  { "le", &_rl_term_backspace },
328  { "mm", &_rl_term_mm },
329  { "mo", &_rl_term_mo },
330#if defined (HACK_TERMCAP_MOTION)
331  { "nd", &_rl_term_forward_char },
332#endif
333  { "pc", &_rl_term_pc },
334  { "up", &_rl_term_up },
335  { "vb", &_rl_visible_bell },
336  { "vs", &_rl_term_vs },
337  { "ve", &_rl_term_ve },
338};
339
340#define NUM_TC_STRINGS (sizeof (tc_strings) / sizeof (struct _tc_string))
341
342/* Read the desired terminal capability strings into BP.  The capabilities
343   are described in the TC_STRINGS table. */
344static void
345get_term_capabilities (bp)
346     char **bp;
347{
348#if !defined (__DJGPP__)	/* XXX - doesn't DJGPP have a termcap library? */
349  register int i;
350
351  for (i = 0; i < NUM_TC_STRINGS; i++)
352    *(tc_strings[i].tc_value) = tgetstr ((char *)tc_strings[i].tc_var, bp);
353#endif
354  tcap_initialized = 1;
355}
356
357int
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)
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. */
481static void
482bind_termcap_arrow_keys (map)
483     Keymap map;
484{
485  Keymap xkeymap;
486
487  xkeymap = _rl_keymap;
488  _rl_keymap = map;
489
490  rl_bind_keyseq_if_unbound (_rl_term_ku, rl_get_previous_history);
491  rl_bind_keyseq_if_unbound (_rl_term_kd, rl_get_next_history);
492  rl_bind_keyseq_if_unbound (_rl_term_kr, rl_forward_char);
493  rl_bind_keyseq_if_unbound (_rl_term_kl, rl_backward_char);
494
495  rl_bind_keyseq_if_unbound (_rl_term_kh, rl_beg_of_line);	/* Home */
496  rl_bind_keyseq_if_unbound (_rl_term_at7, rl_end_of_line);	/* End */
497
498  _rl_keymap = xkeymap;
499}
500
501char *
502rl_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. */
519int
520rl_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
529void
530_rl_output_character_function (c)
531     int c;
532{
533  putc (c, _rl_out_stream);
534}
535#else /* !_MINIX */
536int
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. */
545void
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. */
554int
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. */
570int
571rl_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. */
582int
583rl_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
615void
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
624void
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. */
646void
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