]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libreadline/terminal.c
This commit was generated by cvs2svn to compensate for changes in r147001,
[FreeBSD/FreeBSD.git] / contrib / libreadline / terminal.c
1 /* $FreeBSD$ */
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
80 static char *term_buffer = (char *)NULL;
81 static char *term_string_buffer = (char *)NULL;
82
83 static int tcap_initialized;
84
85 #if !defined (__linux__)
86 #  if defined (__EMX__) || defined (NEED_EXTERN_PC)
87 extern 
88 #  endif /* __EMX__ || NEED_EXTERN_PC */
89 char PC, *BC, *UP;
90 #endif /* __linux__ */
91
92 /* Some strings to control terminal actions.  These are output by tputs (). */
93 char *_rl_term_clreol;
94 char *_rl_term_clrpag;
95 char *_rl_term_cr;
96 char *_rl_term_backspace;
97 char *_rl_term_goto;
98 char *_rl_term_pc;
99
100 /* Non-zero if we determine that the terminal can do character insertion. */
101 int _rl_terminal_can_insert = 0;
102
103 /* How to insert characters. */
104 char *_rl_term_im;
105 char *_rl_term_ei;
106 char *_rl_term_ic;
107 char *_rl_term_ip;
108 char *_rl_term_IC;
109
110 /* How to delete characters. */
111 char *_rl_term_dc;
112 char *_rl_term_DC;
113
114 #if defined (HACK_TERMCAP_MOTION)
115 char *_rl_term_forward_char;
116 #endif  /* HACK_TERMCAP_MOTION */
117
118 /* How to go up a line. */
119 char *_rl_term_up;
120
121 /* A visible bell; char if the terminal can be made to flash the screen. */
122 static char *_rl_visible_bell;
123
124 /* Non-zero means the terminal can auto-wrap lines. */
125 int _rl_term_autowrap;
126
127 /* Non-zero means that this terminal has a meta key. */
128 static int term_has_meta;
129
130 /* The sequences to write to turn on and off the meta key, if this
131    terminal has one. */
132 static char *_rl_term_mm;
133 static char *_rl_term_mo;
134
135 /* The key sequences output by the arrow keys, if this terminal has any. */
136 static char *_rl_term_ku;
137 static char *_rl_term_kd;
138 static char *_rl_term_kr;
139 static char *_rl_term_kl;
140
141 /* How to initialize and reset the arrow keys, if this terminal has any. */
142 static char *_rl_term_ks;
143 static char *_rl_term_ke;
144
145 /* The key sequences sent by the Home and End keys, if any. */
146 static char *_rl_term_kh;
147 static char *_rl_term_kH;
148 static char *_rl_term_at7;      /* @7 */
149
150 /* Insert key */
151 static char *_rl_term_kI;
152
153 /* Cursor control */
154 static char *_rl_term_vs;       /* very visible */
155 static char *_rl_term_ve;       /* normal */
156
157 static void bind_termcap_arrow_keys PARAMS((Keymap));
158
159 /* Variables that hold the screen dimensions, used by the display code. */
160 int _rl_screenwidth, _rl_screenheight, _rl_screenchars;
161
162 /* Non-zero means the user wants to enable the keypad. */
163 int _rl_enable_keypad;
164
165 /* Non-zero means the user wants to enable a meta key. */
166 int _rl_enable_meta = 1;
167
168 #if defined (__EMX__)
169 static 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. */
188 void
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
253 void
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
269 void
270 rl_set_screen_size (rows, cols)
271      int rows, cols;
272 {
273   _rl_set_screen_size (rows, cols);
274 }
275
276 void
277 rl_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      
286 void
287 rl_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
299 struct _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. */
306 static 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. */
344 static void
345 get_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
357 int
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. */
481 static void
482 bind_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
501 char *
502 rl_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. */
519 int
520 rl_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
529 void
530 _rl_output_character_function (c)
531      int c;
532 {
533   putc (c, _rl_out_stream);
534 }
535 #else /* !_MINIX */
536 int
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. */
545 void
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. */
554 int
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. */
570 int
571 rl_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. */
582 int
583 rl_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
615 void
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
624 void
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. */
646 void
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 }