]> git.ipfire.org Git - thirdparty/gcc.git/blob - texinfo/info/terminal.c
Initial revision
[thirdparty/gcc.git] / texinfo / info / terminal.c
1 /* terminal.c -- How to handle the physical terminal for Info. */
2
3 /* This file is part of GNU Info, a program for reading online documentation
4 stored in Info format.
5
6 This file has appeared in prior works by the Free Software Foundation;
7 thus it carries copyright dates from 1988 through 1993.
8
9 Copyright (C) 1988, 89, 90, 91, 92, 93, 96 Free Software Foundation, Inc.
10
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2, or (at your option)
14 any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
25 Written by Brian Fox (bfox@ai.mit.edu). */
26
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include "terminal.h"
30 #include "termdep.h"
31
32 extern void *xmalloc (), *xrealloc ();
33
34 /* The Unix termcap interface code. */
35
36 extern int tgetnum (), tgetflag (), tgetent ();
37 extern char *tgetstr (), *tgoto ();
38 extern char *getenv ();
39 extern void tputs ();
40
41 /* Function "hooks". If you make one of these point to a function, that
42 function is called when appropriate instead of its namesake. Your
43 function is called with exactly the same arguments that were passed
44 to the namesake function. */
45 VFunction *terminal_begin_inverse_hook = (VFunction *)NULL;
46 VFunction *terminal_end_inverse_hook = (VFunction *)NULL;
47 VFunction *terminal_prep_terminal_hook = (VFunction *)NULL;
48 VFunction *terminal_unprep_terminal_hook = (VFunction *)NULL;
49 VFunction *terminal_up_line_hook = (VFunction *)NULL;
50 VFunction *terminal_down_line_hook = (VFunction *)NULL;
51 VFunction *terminal_clear_screen_hook = (VFunction *)NULL;
52 VFunction *terminal_clear_to_eol_hook = (VFunction *)NULL;
53 VFunction *terminal_get_screen_size_hook = (VFunction *)NULL;
54 VFunction *terminal_goto_xy_hook = (VFunction *)NULL;
55 VFunction *terminal_initialize_terminal_hook = (VFunction *)NULL;
56 VFunction *terminal_new_terminal_hook = (VFunction *)NULL;
57 VFunction *terminal_put_text_hook = (VFunction *)NULL;
58 VFunction *terminal_ring_bell_hook = (VFunction *)NULL;
59 VFunction *terminal_write_chars_hook = (VFunction *)NULL;
60 VFunction *terminal_scroll_terminal_hook = (VFunction *)NULL;
61
62 /* **************************************************************** */
63 /* */
64 /* Terminal and Termcap */
65 /* */
66 /* **************************************************************** */
67
68 /* On Solaris2, sys/types.h #includes sys/reg.h, which #defines PC.
69 Unfortunately, PC is a global variable used by the termcap library. */
70 #undef PC
71
72 /* TERMCAP requires these variables, whether we access them or not. */
73 char PC;
74 char *BC, *UP;
75 short ospeed;
76
77 /* A buffer which holds onto the current terminal description, and a pointer
78 used to float within it. */
79 static char *term_buffer = (char *)NULL;
80 static char *term_string_buffer = (char *)NULL;
81
82 /* Some strings to control terminal actions. These are output by tputs (). */
83 static char *term_goto, *term_clreol, *term_cr, *term_clrpag;
84 static char *term_begin_use, *term_end_use;
85 static char *term_AL, *term_DL, *term_al, *term_dl;
86
87 /* How to go up a line. */
88 static char *term_up;
89
90 /* How to go down a line. */
91 static char *term_dn;
92
93 /* An audible bell, if the terminal can be made to make noise. */
94 static char *audible_bell;
95
96 /* A visible bell, if the terminal can be made to flash the screen. */
97 static char *visible_bell;
98
99 /* The string to write to turn on the meta key, if this term has one. */
100 static char *term_mm;
101
102 /* The string to write to turn off the meta key, if this term has one. */
103 static char *term_mo;
104
105 /* The string to turn on inverse mode, if this term has one. */
106 static char *term_invbeg;
107
108 /* The string to turn off inverse mode, if this term has one. */
109 static char *term_invend;
110
111 static void
112 output_character_function (c)
113 int c;
114 {
115 putc (c, stdout);
116 }
117
118 /* Macro to send STRING to the terminal. */
119 #define send_to_terminal(string) \
120 do { \
121 if (string) \
122 tputs (string, 1, output_character_function); \
123 } while (0)
124
125 /* Tell the terminal that we will be doing cursor addressable motion. */
126 static void
127 terminal_begin_using_terminal ()
128 {
129 send_to_terminal (term_begin_use);
130 }
131
132 /* Tell the terminal that we will not be doing any more cursor addressable
133 motion. */
134 static void
135 terminal_end_using_terminal ()
136 {
137 send_to_terminal (term_end_use);
138 }
139 \f
140 /* **************************************************************** */
141 /* */
142 /* Necessary Terminal Functions */
143 /* */
144 /* **************************************************************** */
145
146 /* The functions and variables on this page implement the user visible
147 portion of the terminal interface. */
148
149 /* The width and height of the terminal. */
150 int screenwidth, screenheight;
151
152 /* Non-zero means this terminal can't really do anything. */
153 int terminal_is_dumb_p = 0;
154
155 /* Non-zero means that this terminal has a meta key. */
156 int terminal_has_meta_p = 0;
157
158 /* Non-zero means that this terminal can produce a visible bell. */
159 int terminal_has_visible_bell_p = 0;
160
161 /* Non-zero means to use that visible bell if at all possible. */
162 int terminal_use_visible_bell_p = 0;
163
164 /* Non-zero means that the terminal can do scrolling. */
165 int terminal_can_scroll = 0;
166
167 /* The key sequences output by the arrow keys, if this terminal has any. */
168 char *term_ku = (char *)NULL;
169 char *term_kd = (char *)NULL;
170 char *term_kr = (char *)NULL;
171 char *term_kl = (char *)NULL;
172
173 /* Move the cursor to the terminal location of X and Y. */
174 void
175 terminal_goto_xy (x, y)
176 int x, y;
177 {
178 if (terminal_goto_xy_hook)
179 (*terminal_goto_xy_hook) (x, y);
180 else
181 {
182 if (term_goto)
183 tputs (tgoto (term_goto, x, y), 1, output_character_function);
184 }
185 }
186
187 /* Print STRING to the terminal at the current position. */
188 void
189 terminal_put_text (string)
190 char *string;
191 {
192 if (terminal_put_text_hook)
193 (*terminal_put_text_hook) (string);
194 else
195 {
196 printf ("%s", string);
197 }
198 }
199
200 /* Print NCHARS from STRING to the terminal at the current position. */
201 void
202 terminal_write_chars (string, nchars)
203 char *string;
204 int nchars;
205 {
206 if (terminal_write_chars_hook)
207 (*terminal_write_chars_hook) (string, nchars);
208 else
209 {
210 if (nchars)
211 fwrite (string, 1, nchars, stdout);
212 }
213 }
214
215 /* Clear from the current position of the cursor to the end of the line. */
216 void
217 terminal_clear_to_eol ()
218 {
219 if (terminal_clear_to_eol_hook)
220 (*terminal_clear_to_eol_hook) ();
221 else
222 {
223 send_to_terminal (term_clreol);
224 }
225 }
226
227 /* Clear the entire terminal screen. */
228 void
229 terminal_clear_screen ()
230 {
231 if (terminal_clear_screen_hook)
232 (*terminal_clear_screen_hook) ();
233 else
234 {
235 send_to_terminal (term_clrpag);
236 }
237 }
238
239 /* Move the cursor up one line. */
240 void
241 terminal_up_line ()
242 {
243 if (terminal_up_line_hook)
244 (*terminal_up_line_hook) ();
245 else
246 {
247 send_to_terminal (term_up);
248 }
249 }
250
251 /* Move the cursor down one line. */
252 void
253 terminal_down_line ()
254 {
255 if (terminal_down_line_hook)
256 (*terminal_down_line_hook) ();
257 else
258 {
259 send_to_terminal (term_dn);
260 }
261 }
262
263 /* Turn on reverse video if possible. */
264 void
265 terminal_begin_inverse ()
266 {
267 if (terminal_begin_inverse_hook)
268 (*terminal_begin_inverse_hook) ();
269 else
270 {
271 send_to_terminal (term_invbeg);
272 }
273 }
274
275 /* Turn off reverse video if possible. */
276 void
277 terminal_end_inverse ()
278 {
279 if (terminal_end_inverse_hook)
280 (*terminal_end_inverse_hook) ();
281 else
282 {
283 send_to_terminal (term_invend);
284 }
285 }
286
287 /* Ring the terminal bell. The bell is run visibly if it both has one and
288 terminal_use_visible_bell_p is non-zero. */
289 void
290 terminal_ring_bell ()
291 {
292 if (terminal_ring_bell_hook)
293 (*terminal_ring_bell_hook) ();
294 else
295 {
296 if (terminal_has_visible_bell_p && terminal_use_visible_bell_p)
297 send_to_terminal (visible_bell);
298 else
299 send_to_terminal (audible_bell);
300 }
301 }
302
303 /* At the line START, delete COUNT lines from the terminal display. */
304 static void
305 terminal_delete_lines (start, count)
306 int start, count;
307 {
308 int lines;
309
310 /* Normalize arguments. */
311 if (start < 0)
312 start = 0;
313
314 lines = screenheight - start;
315 terminal_goto_xy (0, start);
316 if (term_DL)
317 tputs (tgoto (term_DL, 0, count), lines, output_character_function);
318 else
319 {
320 while (count--)
321 tputs (term_dl, lines, output_character_function);
322 }
323
324 fflush (stdout);
325 }
326
327 /* At the line START, insert COUNT lines in the terminal display. */
328 static void
329 terminal_insert_lines (start, count)
330 int start, count;
331 {
332 int lines;
333
334 /* Normalize arguments. */
335 if (start < 0)
336 start = 0;
337
338 lines = screenheight - start;
339 terminal_goto_xy (0, start);
340
341 if (term_AL)
342 tputs (tgoto (term_AL, 0, count), lines, output_character_function);
343 else
344 {
345 while (count--)
346 tputs (term_al, lines, output_character_function);
347 }
348
349 fflush (stdout);
350 }
351
352 /* Scroll an area of the terminal, starting with the region from START
353 to END, AMOUNT lines. If AMOUNT is negative, the lines are scrolled
354 towards the top of the screen, else they are scrolled towards the
355 bottom of the screen. */
356 void
357 terminal_scroll_terminal (start, end, amount)
358 int start, end, amount;
359 {
360 if (!terminal_can_scroll)
361 return;
362
363 /* Any scrolling at all? */
364 if (amount == 0)
365 return;
366
367 if (terminal_scroll_terminal_hook)
368 (*terminal_scroll_terminal_hook) (start, end, amount);
369 else
370 {
371 /* If we are scrolling down, delete AMOUNT lines at END. Then insert
372 AMOUNT lines at START. */
373 if (amount > 0)
374 {
375 terminal_delete_lines (end, amount);
376 terminal_insert_lines (start, amount);
377 }
378
379 /* If we are scrolling up, delete AMOUNT lines before START. This
380 actually does the upwards scroll. Then, insert AMOUNT lines
381 after the already scrolled region (i.e., END - AMOUNT). */
382 if (amount < 0)
383 {
384 int abs_amount = -amount;
385 terminal_delete_lines (start - abs_amount, abs_amount);
386 terminal_insert_lines (end - abs_amount, abs_amount);
387 }
388 }
389 }
390
391 /* Re-initialize the terminal considering that the TERM/TERMCAP variable
392 has changed. */
393 void
394 terminal_new_terminal (terminal_name)
395 char *terminal_name;
396 {
397 if (terminal_new_terminal_hook)
398 (*terminal_new_terminal_hook) (terminal_name);
399 else
400 {
401 terminal_initialize_terminal (terminal_name);
402 }
403 }
404
405 /* Set the global variables SCREENWIDTH and SCREENHEIGHT. */
406 void
407 terminal_get_screen_size ()
408 {
409 if (terminal_get_screen_size_hook)
410 (*terminal_get_screen_size_hook) ();
411 else
412 {
413 screenwidth = screenheight = 0;
414
415 #if defined (TIOCGWINSZ)
416 {
417 struct winsize window_size;
418
419 if (ioctl (fileno (stdout), TIOCGWINSZ, &window_size) == 0)
420 {
421 screenwidth = (int) window_size.ws_col;
422 screenheight = (int) window_size.ws_row;
423 }
424 }
425 #endif /* TIOCGWINSZ */
426
427 /* Environment variable COLUMNS overrides setting of "co". */
428 if (screenwidth <= 0)
429 {
430 char *sw = getenv ("COLUMNS");
431
432 if (sw)
433 screenwidth = atoi (sw);
434
435 if (screenwidth <= 0)
436 screenwidth = tgetnum ("co");
437 }
438
439 /* Environment variable LINES overrides setting of "li". */
440 if (screenheight <= 0)
441 {
442 char *sh = getenv ("LINES");
443
444 if (sh)
445 screenheight = atoi (sh);
446
447 if (screenheight <= 0)
448 screenheight = tgetnum ("li");
449 }
450
451 /* If all else fails, default to 80x24 terminal. */
452 if (screenwidth <= 0)
453 screenwidth = 80;
454
455 if (screenheight <= 0)
456 screenheight = 24;
457 }
458 }
459
460 /* Initialize the terminal which is known as TERMINAL_NAME. If this terminal
461 doesn't have cursor addressability, TERMINAL_IS_DUMB_P becomes non-zero.
462 The variables SCREENHEIGHT and SCREENWIDTH are set to the dimensions that
463 this terminal actually has. The variable TERMINAL_HAS_META_P becomes non-
464 zero if this terminal supports a Meta key. Finally, the terminal screen is
465 cleared. */
466 void
467 terminal_initialize_terminal (terminal_name)
468 char *terminal_name;
469 {
470 char *term, *buffer;
471
472 terminal_is_dumb_p = 0;
473
474 if (terminal_initialize_terminal_hook)
475 {
476 (*terminal_initialize_terminal_hook) (terminal_name);
477 return;
478 }
479
480 term = terminal_name ? terminal_name : getenv ("TERM");
481
482 if (!term_string_buffer)
483 term_string_buffer = (char *)xmalloc (2048);
484
485 if (!term_buffer)
486 term_buffer = (char *)xmalloc (2048);
487
488 buffer = term_string_buffer;
489
490 term_clrpag = term_cr = term_clreol = (char *)NULL;
491
492 if (!term)
493 term = "dumb";
494
495 if (tgetent (term_buffer, term) <= 0)
496 {
497 terminal_is_dumb_p = 1;
498 screenwidth = 80;
499 screenheight = 24;
500 term_cr = "\r";
501 term_up = term_dn = audible_bell = visible_bell = (char *)NULL;
502 term_ku = term_kd = term_kl = term_kr = (char *)NULL;
503 return;
504 }
505
506 BC = tgetstr ("pc", &buffer);
507 PC = BC ? *BC : 0;
508
509 #if defined (TIOCGETP)
510 {
511 struct sgttyb sg;
512
513 if (ioctl (fileno (stdout), TIOCGETP, &sg) != -1)
514 ospeed = sg.sg_ospeed;
515 else
516 ospeed = B9600;
517 }
518 #else
519 ospeed = B9600;
520 #endif /* !TIOCGETP */
521
522 term_cr = tgetstr ("cr", &buffer);
523 term_clreol = tgetstr ("ce", &buffer);
524 term_clrpag = tgetstr ("cl", &buffer);
525 term_goto = tgetstr ("cm", &buffer);
526
527 /* Find out about this terminals scrolling capability. */
528 term_AL = tgetstr ("AL", &buffer);
529 term_DL = tgetstr ("DL", &buffer);
530 term_al = tgetstr ("al", &buffer);
531 term_dl = tgetstr ("dl", &buffer);
532
533 terminal_can_scroll = ((term_AL || term_al) && (term_DL || term_dl));
534
535 term_invbeg = tgetstr ("mr", &buffer);
536 if (term_invbeg)
537 term_invend = tgetstr ("me", &buffer);
538 else
539 term_invend = (char *)NULL;
540
541 if (!term_cr)
542 term_cr = "\r";
543
544 terminal_get_screen_size ();
545
546 term_up = tgetstr ("up", &buffer);
547 term_dn = tgetstr ("dn", &buffer);
548 visible_bell = tgetstr ("vb", &buffer);
549 terminal_has_visible_bell_p = (visible_bell != (char *)NULL);
550 audible_bell = tgetstr ("bl", &buffer);
551 if (!audible_bell)
552 audible_bell = "\007";
553 term_begin_use = tgetstr ("ti", &buffer);
554 term_end_use = tgetstr ("te", &buffer);
555
556 /* Check to see if this terminal has a meta key. */
557 terminal_has_meta_p = (tgetflag ("km") || tgetflag ("MT"));
558 if (terminal_has_meta_p)
559 {
560 term_mm = tgetstr ("mm", &buffer);
561 term_mo = tgetstr ("mo", &buffer);
562 }
563 else
564 {
565 term_mm = (char *)NULL;
566 term_mo = (char *)NULL;
567 }
568
569 /* Attempt to find the arrow keys. */
570 term_ku = tgetstr ("ku", &buffer);
571 term_kd = tgetstr ("kd", &buffer);
572 term_kr = tgetstr ("kr", &buffer);
573 term_kl = tgetstr ("kl", &buffer);
574
575 /* If this terminal is not cursor addressable, then it is really dumb. */
576 if (!term_goto)
577 terminal_is_dumb_p = 1;
578
579 terminal_begin_using_terminal ();
580 }
581 \f
582 /* **************************************************************** */
583 /* */
584 /* How to Read Characters From the Terminal */
585 /* */
586 /* **************************************************************** */
587
588 #if defined (TIOCGETC)
589 /* A buffer containing the terminal interrupt characters upon entry
590 to Info. */
591 struct tchars original_tchars;
592 #endif
593
594 #if defined (TIOCGLTC)
595 /* A buffer containing the local terminal mode characters upon entry
596 to Info. */
597 struct ltchars original_ltchars;
598 #endif
599
600 #if defined (HAVE_TERMIOS_H)
601 struct termios original_termios, ttybuff;
602 #else
603 # if defined (HAVE_TERMIO_H)
604 /* A buffer containing the terminal mode flags upon entry to info. */
605 struct termio original_termio, ttybuff;
606 # else /* !HAVE_TERMIO_H */
607 /* Buffers containing the terminal mode flags upon entry to info. */
608 int original_tty_flags = 0;
609 int original_lmode;
610 struct sgttyb ttybuff;
611 # endif /* !HAVE_TERMIO_H */
612 #endif /* !HAVE_TERMIOS_H */
613
614 /* Prepare to start using the terminal to read characters singly. */
615 void
616 terminal_prep_terminal ()
617 {
618 int tty;
619
620 if (terminal_prep_terminal_hook)
621 {
622 (*terminal_prep_terminal_hook) ();
623 return;
624 }
625
626 tty = fileno (stdin);
627
628 #if defined (HAVE_TERMIOS_H)
629 tcgetattr (tty, &original_termios);
630 tcgetattr (tty, &ttybuff);
631 #else
632 # if defined (HAVE_TERMIO_H)
633 ioctl (tty, TCGETA, &original_termio);
634 ioctl (tty, TCGETA, &ttybuff);
635 # endif
636 #endif
637
638 #if defined (HAVE_TERMIOS_H) || defined (HAVE_TERMIO_H)
639 ttybuff.c_iflag &= (~ISTRIP & ~INLCR & ~IGNCR & ~ICRNL & ~IXON);
640 ttybuff.c_oflag &= (~ONLCR & ~OCRNL);
641 ttybuff.c_lflag &= (~ICANON & ~ECHO);
642
643 ttybuff.c_cc[VMIN] = 1;
644 ttybuff.c_cc[VTIME] = 0;
645
646 if (ttybuff.c_cc[VINTR] == '\177')
647 ttybuff.c_cc[VINTR] = -1;
648
649 if (ttybuff.c_cc[VQUIT] == '\177')
650 ttybuff.c_cc[VQUIT] = -1;
651 #endif
652
653 #if defined (HAVE_TERMIOS_H)
654 tcsetattr (tty, TCSANOW, &ttybuff);
655 #else
656 # if defined (HAVE_TERMIO_H)
657 ioctl (tty, TCSETA, &ttybuff);
658 # endif
659 #endif
660
661 #if !defined (HAVE_TERMIOS_H) && !defined (HAVE_TERMIO_H)
662 ioctl (tty, TIOCGETP, &ttybuff);
663
664 if (!original_tty_flags)
665 original_tty_flags = ttybuff.sg_flags;
666
667 /* Make this terminal pass 8 bits around while we are using it. */
668 # if defined (PASS8)
669 ttybuff.sg_flags |= PASS8;
670 # endif /* PASS8 */
671
672 # if defined (TIOCLGET) && defined (LPASS8)
673 {
674 int flags;
675 ioctl (tty, TIOCLGET, &flags);
676 original_lmode = flags;
677 flags |= LPASS8;
678 ioctl (tty, TIOCLSET, &flags);
679 }
680 # endif /* TIOCLGET && LPASS8 */
681
682 # if defined (TIOCGETC)
683 {
684 struct tchars temp;
685
686 ioctl (tty, TIOCGETC, &original_tchars);
687 temp = original_tchars;
688
689 /* C-s and C-q. */
690 temp.t_startc = temp.t_stopc = -1;
691
692 /* Often set to C-d. */
693 temp.t_eofc = -1;
694
695 /* If the a quit or interrupt character conflicts with one of our
696 commands, then make it go away. */
697 if (temp.t_intrc == '\177')
698 temp.t_intrc = -1;
699
700 if (temp.t_quitc == '\177')
701 temp.t_quitc = -1;
702
703 ioctl (tty, TIOCSETC, &temp);
704 }
705 # endif /* TIOCGETC */
706
707 # if defined (TIOCGLTC)
708 {
709 struct ltchars temp;
710
711 ioctl (tty, TIOCGLTC, &original_ltchars);
712 temp = original_ltchars;
713
714 /* Make the interrupt keys go away. Just enough to make people happy. */
715 temp.t_lnextc = -1; /* C-v. */
716 temp.t_dsuspc = -1; /* C-y. */
717 temp.t_flushc = -1; /* C-o. */
718 ioctl (tty, TIOCSLTC, &temp);
719 }
720 # endif /* TIOCGLTC */
721
722 ttybuff.sg_flags &= ~ECHO;
723 ttybuff.sg_flags |= CBREAK;
724 ioctl (tty, TIOCSETN, &ttybuff);
725 #endif /* !HAVE_TERMIOS_H && !HAVE_TERMIO_H */
726 }
727
728 /* Restore the tty settings back to what they were before we started using
729 this terminal. */
730 void
731 terminal_unprep_terminal ()
732 {
733 int tty;
734
735 if (terminal_unprep_terminal_hook)
736 {
737 (*terminal_unprep_terminal_hook) ();
738 return;
739 }
740
741 tty = fileno (stdin);
742
743 #if defined (HAVE_TERMIOS_H)
744 tcsetattr (tty, TCSANOW, &original_termios);
745 #else
746 # if defined (HAVE_TERMIO_H)
747 ioctl (tty, TCSETA, &original_termio);
748 # else /* !HAVE_TERMIO_H */
749 ioctl (tty, TIOCGETP, &ttybuff);
750 ttybuff.sg_flags = original_tty_flags;
751 ioctl (tty, TIOCSETN, &ttybuff);
752
753 # if defined (TIOCGETC)
754 ioctl (tty, TIOCSETC, &original_tchars);
755 # endif /* TIOCGETC */
756
757 # if defined (TIOCGLTC)
758 ioctl (tty, TIOCSLTC, &original_ltchars);
759 # endif /* TIOCGLTC */
760
761 # if defined (TIOCLGET) && defined (LPASS8)
762 ioctl (tty, TIOCLSET, &original_lmode);
763 # endif /* TIOCLGET && LPASS8 */
764
765 # endif /* !HAVE_TERMIO_H */
766 #endif /* !HAVE_TERMIOS_H */
767 terminal_end_using_terminal ();
768 }
769