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