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