screen.c revision 1.6 1 /* $NetBSD: screen.c,v 1.6 2003/08/07 09:28:01 agc Exp $ */
2
3 /*
4 * Copyright (c) 1988, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /*
33 * Copyright (c) 1988 Mark Nudleman
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice, this list of conditions and the following disclaimer in the
42 * documentation and/or other materials provided with the distribution.
43 * 3. All advertising materials mentioning features or use of this software
44 * must display the following acknowledgement:
45 * This product includes software developed by the University of
46 * California, Berkeley and its contributors.
47 * 4. Neither the name of the University nor the names of its contributors
48 * may be used to endorse or promote products derived from this software
49 * without specific prior written permission.
50 *
51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * SUCH DAMAGE.
62 */
63
64 #include <sys/cdefs.h>
65 #ifndef lint
66 #if 0
67 static char sccsid[] = "@(#)screen.c 8.2 (Berkeley) 4/20/94";
68 #else
69 __RCSID("$NetBSD: screen.c,v 1.6 2003/08/07 09:28:01 agc Exp $");
70 #endif
71 #endif /* not lint */
72
73 /*
74 * Routines which deal with the characteristics of the terminal.
75 * Uses termcap to be as terminal-independent as possible.
76 *
77 * {{ Someday this should be rewritten to use curses. }}
78 */
79
80 #include <stdio.h>
81 #include <string.h>
82 #include <stdlib.h>
83 #include <unistd.h>
84
85 #include "less.h"
86 #include "extern.h"
87
88 #define TERMIOS 1
89
90 #if TERMIO
91 #include <termio.h>
92 #else
93 #if TERMIOS
94 #include <termios.h>
95 #define TAB3 0
96 #include <sys/ioctl.h>
97 #else
98 #include <sgtty.h>
99 #endif
100 #endif
101 #ifdef __NetBSD__
102 #include <termcap.h>
103 #endif
104
105 #ifdef TIOCGWINSZ
106 #include <sys/ioctl.h>
107 #else
108 /*
109 * For the Unix PC (ATT 7300 & 3B1):
110 * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
111 * whether to include sys/window.h. Use SIGPHONE from sys/signal.h instead.
112 */
113 #include <sys/signal.h>
114 #ifdef SIGPHONE
115 #include <sys/window.h>
116 #endif
117 #endif
118
119 /*
120 * Strings passed to tputs() to do various terminal functions.
121 */
122 static char
123 *sc_pad, /* Pad string */
124 *sc_home, /* Cursor home */
125 *sc_addline, /* Add line, scroll down following lines */
126 *sc_lower_left, /* Cursor to last line, first column */
127 *sc_move, /* General cursor positioning */
128 *sc_clear, /* Clear screen */
129 *sc_eol_clear, /* Clear to end of line */
130 *sc_s_in, /* Enter standout (highlighted) mode */
131 *sc_s_out, /* Exit standout mode */
132 *sc_u_in, /* Enter underline mode */
133 *sc_u_out, /* Exit underline mode */
134 *sc_b_in, /* Enter bold mode */
135 *sc_b_out, /* Exit bold mode */
136 *sc_backspace, /* Backspace cursor */
137 *sc_init, /* Startup terminal initialization */
138 *sc_deinit; /* Exit terminal de-intialization */
139
140 int auto_wrap; /* Terminal does \r\n when write past margin */
141 int ignaw; /* Terminal ignores \n immediately after wrap */
142 /* The user's erase and line-kill chars */
143 int retain_below; /* Terminal retains text below the screen */
144 int erase_char, kill_char, werase_char;
145 int sc_width, sc_height = -1; /* Height & width of screen */
146 int sc_window = -1; /* window size for forward and backward */
147 int bo_width, be_width; /* Printing width of boldface sequences */
148 int ul_width, ue_width; /* Printing width of underline sequences */
149 int so_width, se_width; /* Printing width of standout sequences */
150
151 /*
152 * These two variables are sometimes defined in,
153 * and needed by, the termcap library.
154 * It may be necessary on some systems to declare them extern here.
155 */
156 /*extern*/ short ospeed; /* Terminal output baud rate */
157 /*extern*/ char PC; /* Pad character */
158
159 /*
160 * Change terminal to "raw mode", or restore to "normal" mode.
161 * "Raw mode" means
162 * 1. An outstanding read will complete on receipt of a single keystroke.
163 * 2. Input is not echoed.
164 * 3. On output, \n is mapped to \r\n.
165 * 4. \t is NOT expanded into spaces.
166 * 5. Signal-causing characters such as ctrl-C (interrupt),
167 * etc. are NOT disabled.
168 * It doesn't matter whether an input \n is mapped to \r, or vice versa.
169 */
170 void
171 raw_mode(on)
172 int on;
173 {
174 #if TERMIO || TERMIOS
175
176 #if TERMIO
177 struct termio s;
178 static struct termio save_term;
179 #else
180 struct termios s;
181 static struct termios save_term;
182 #endif
183
184 if (on)
185 {
186 /*
187 * Get terminal modes.
188 */
189 #if TERMIO
190 (void)ioctl(2, TCGETA, &s);
191 #else
192 tcgetattr(2, &s);
193 #endif
194
195 /*
196 * Save modes and set certain variables dependent on modes.
197 */
198 save_term = s;
199 #if TERMIO
200 ospeed = s.c_cflag & CBAUD;
201 #else
202 ospeed = cfgetospeed(&s);
203 #endif
204 erase_char = s.c_cc[VERASE];
205 kill_char = s.c_cc[VKILL];
206 werase_char = s.c_cc[VWERASE];
207
208 /*
209 * Set the modes to the way we want them.
210 */
211 s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
212 s.c_oflag |= (OPOST|ONLCR|TAB3);
213 #if TERMIO
214 s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
215 #endif
216 s.c_cc[VMIN] = 1;
217 s.c_cc[VTIME] = 0;
218 } else
219 {
220 /*
221 * Restore saved modes.
222 */
223 s = save_term;
224 }
225 #if TERMIO
226 (void)ioctl(2, TCSETAW, &s);
227 #else
228 tcsetattr(2, TCSADRAIN, &s);
229 #endif
230 #else
231 struct sgttyb s;
232 struct ltchars l;
233 static struct sgttyb save_term;
234
235 if (on)
236 {
237 /*
238 * Get terminal modes.
239 */
240 (void)ioctl(2, TIOCGETP, &s);
241 (void)ioctl(2, TIOCGLTC, &l);
242
243 /*
244 * Save modes and set certain variables dependent on modes.
245 */
246 save_term = s;
247 ospeed = s.sg_ospeed;
248 erase_char = s.sg_erase;
249 kill_char = s.sg_kill;
250 werase_char = l.t_werasc;
251
252 /*
253 * Set the modes to the way we want them.
254 */
255 s.sg_flags |= CBREAK;
256 s.sg_flags &= ~(ECHO|XTABS);
257 } else
258 {
259 /*
260 * Restore saved modes.
261 */
262 s = save_term;
263 }
264 (void)ioctl(2, TIOCSETN, &s);
265 #endif
266 }
267
268 /*
269 * Get terminal capabilities via termcap.
270 */
271 void
272 get_term()
273 {
274 char termbuf[2048];
275 char *sp;
276 char *term;
277 int hard;
278 #ifdef TIOCGWINSZ
279 struct winsize w;
280 #else
281 #ifdef WIOCGETD
282 struct uwdata w;
283 #endif
284 #endif
285 static char sbuf[1024];
286
287 /*
288 * Find out what kind of terminal this is.
289 */
290 if ((term = getenv("TERM")) == NULL)
291 term = "unknown";
292 if (tgetent(termbuf, term) <= 0)
293 (void)strlcpy(termbuf, "dumb:co#80:hc:", sizeof(termbuf));
294
295 /*
296 * Get size of the screen.
297 */
298 #ifdef TIOCGWINSZ
299 if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
300 sc_height = w.ws_row;
301 #else
302 #ifdef WIOCGETD
303 if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
304 sc_height = w.uw_height/w.uw_vs;
305 #endif
306 #endif
307 else
308 sc_height = tgetnum("li");
309 hard = (sc_height < 0 || tgetflag("hc"));
310 if (hard) {
311 /* Oh no, this is a hardcopy terminal. */
312 sc_height = 24;
313 }
314
315 #ifdef TIOCGWINSZ
316 if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
317 sc_width = w.ws_col;
318 else
319 #ifdef WIOCGETD
320 if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
321 sc_width = w.uw_width/w.uw_hs;
322 else
323 #endif
324 #endif
325 sc_width = tgetnum("co");
326 if (sc_width < 0)
327 sc_width = 80;
328
329 auto_wrap = tgetflag("am");
330 ignaw = tgetflag("xn");
331 retain_below = tgetflag("db");
332
333 /*
334 * Assumes termcap variable "sg" is the printing width of
335 * the standout sequence, the end standout sequence,
336 * the underline sequence, the end underline sequence,
337 * the boldface sequence, and the end boldface sequence.
338 */
339 if ((so_width = tgetnum("sg")) < 0)
340 so_width = 0;
341 be_width = bo_width = ue_width = ul_width = se_width = so_width;
342
343 /*
344 * Get various string-valued capabilities.
345 */
346 sp = sbuf;
347
348 sc_pad = tgetstr("pc", &sp);
349 if (sc_pad != NULL)
350 PC = *sc_pad;
351
352 sc_init = tgetstr("ti", &sp);
353 if (sc_init == NULL)
354 sc_init = "";
355
356 sc_deinit= tgetstr("te", &sp);
357 if (sc_deinit == NULL)
358 sc_deinit = "";
359
360 sc_eol_clear = tgetstr("ce", &sp);
361 if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
362 {
363 sc_eol_clear = "";
364 }
365
366 sc_clear = tgetstr("cl", &sp);
367 if (hard || sc_clear == NULL || *sc_clear == '\0')
368 {
369 sc_clear = "\n\n";
370 }
371
372 sc_move = tgetstr("cm", &sp);
373 if (hard || sc_move == NULL || *sc_move == '\0')
374 {
375 /*
376 * This is not an error here, because we don't
377 * always need sc_move.
378 * We need it only if we don't have home or lower-left.
379 */
380 sc_move = "";
381 }
382
383 sc_s_in = tgetstr("so", &sp);
384 if (hard || sc_s_in == NULL)
385 sc_s_in = "";
386
387 sc_s_out = tgetstr("se", &sp);
388 if (hard || sc_s_out == NULL)
389 sc_s_out = "";
390
391 sc_u_in = tgetstr("us", &sp);
392 if (hard || sc_u_in == NULL)
393 sc_u_in = sc_s_in;
394
395 sc_u_out = tgetstr("ue", &sp);
396 if (hard || sc_u_out == NULL)
397 sc_u_out = sc_s_out;
398
399 sc_b_in = tgetstr("md", &sp);
400 if (hard || sc_b_in == NULL)
401 {
402 sc_b_in = sc_s_in;
403 sc_b_out = sc_s_out;
404 } else
405 {
406 sc_b_out = tgetstr("me", &sp);
407 if (hard || sc_b_out == NULL)
408 sc_b_out = "";
409 }
410
411 sc_home = tgetstr("ho", &sp);
412 if (hard || sc_home == NULL || *sc_home == '\0')
413 {
414 if (*sc_move == '\0')
415 {
416 /*
417 * This last resort for sc_home is supposed to
418 * be an up-arrow suggesting moving to the
419 * top of the "virtual screen". (The one in
420 * your imagination as you try to use this on
421 * a hard copy terminal.)
422 */
423 sc_home = "|\b^";
424 } else
425 {
426 /*
427 * No "home" string,
428 * but we can use "move(0,0)".
429 */
430 (void)strlcpy(sp, tgoto(sc_move, 0, 0),
431 sizeof(sbuf) - (sp - sbuf));
432 sc_home = sp;
433 sp += strlen(sp) + 1;
434 }
435 }
436
437 sc_lower_left = tgetstr("ll", &sp);
438 if (hard || sc_lower_left == NULL || *sc_lower_left == '\0')
439 {
440 if (*sc_move == '\0')
441 {
442 sc_lower_left = "\r";
443 } else
444 {
445 /*
446 * No "lower-left" string,
447 * but we can use "move(0,last-line)".
448 */
449 (void)strlcpy(sp, tgoto(sc_move, 0, sc_height-1),
450 sizeof(sbuf) - (sp - sbuf));
451 sc_lower_left = sp;
452 sp += strlen(sp) + 1;
453 }
454 }
455
456 /*
457 * To add a line at top of screen and scroll the display down,
458 * we use "al" (add line) or "sr" (scroll reverse).
459 */
460 if ((sc_addline = tgetstr("al", &sp)) == NULL ||
461 *sc_addline == '\0')
462 sc_addline = tgetstr("sr", &sp);
463
464 if (hard || sc_addline == NULL || *sc_addline == '\0')
465 {
466 sc_addline = "";
467 /* Force repaint on any backward movement */
468 back_scroll = 0;
469 }
470
471 if (tgetflag("bs"))
472 sc_backspace = "\b";
473 else
474 {
475 sc_backspace = tgetstr("bc", &sp);
476 if (sc_backspace == NULL || *sc_backspace == '\0')
477 sc_backspace = "\b";
478 }
479 }
480
481
482 /*
483 * Below are the functions which perform all the
484 * terminal-specific screen manipulation.
485 */
486
487 /*
488 * Initialize terminal
489 */
490 void
491 init()
492 {
493 tputs(sc_init, sc_height, putchr);
494 }
495
496 /*
497 * Deinitialize terminal
498 */
499 void
500 deinit()
501 {
502 tputs(sc_deinit, sc_height, putchr);
503 }
504
505 /*
506 * Home cursor (move to upper left corner of screen).
507 */
508 void
509 home()
510 {
511 tputs(sc_home, 1, putchr);
512 }
513
514 /*
515 * Add a blank line (called with cursor at home).
516 * Should scroll the display down.
517 */
518 void
519 add_line()
520 {
521 tputs(sc_addline, sc_height, putchr);
522 }
523
524 int short_file; /* if file less than a screen */
525 void
526 lower_left()
527 {
528 if (short_file) {
529 putchr('\r');
530 flush();
531 }
532 else
533 tputs(sc_lower_left, 1, putchr);
534 }
535
536 /*
537 * Ring the terminal bell.
538 */
539 void
540 bell()
541 {
542 putchr('\7');
543 }
544
545 /*
546 * Clear the screen.
547 */
548 void
549 clear()
550 {
551 tputs(sc_clear, sc_height, putchr);
552 }
553
554 /*
555 * Clear from the cursor to the end of the cursor's line.
556 * {{ This must not move the cursor. }}
557 */
558 void
559 clear_eol()
560 {
561 tputs(sc_eol_clear, 1, putchr);
562 }
563
564 /*
565 * Begin "standout" (bold, underline, or whatever).
566 */
567 void
568 so_enter()
569 {
570 tputs(sc_s_in, 1, putchr);
571 }
572
573 /*
574 * End "standout".
575 */
576 void
577 so_exit()
578 {
579 tputs(sc_s_out, 1, putchr);
580 }
581
582 /*
583 * Begin "underline" (hopefully real underlining,
584 * otherwise whatever the terminal provides).
585 */
586 void
587 ul_enter()
588 {
589 tputs(sc_u_in, 1, putchr);
590 }
591
592 /*
593 * End "underline".
594 */
595 void
596 ul_exit()
597 {
598 tputs(sc_u_out, 1, putchr);
599 }
600
601 /*
602 * Begin "bold"
603 */
604 void
605 bo_enter()
606 {
607 tputs(sc_b_in, 1, putchr);
608 }
609
610 /*
611 * End "bold".
612 */
613 void
614 bo_exit()
615 {
616 tputs(sc_b_out, 1, putchr);
617 }
618
619 /*
620 * Erase the character to the left of the cursor
621 * and move the cursor left.
622 */
623 void
624 backspace()
625 {
626 /*
627 * Try to erase the previous character by overstriking with a space.
628 */
629 tputs(sc_backspace, 1, putchr);
630 putchr(' ');
631 tputs(sc_backspace, 1, putchr);
632 }
633
634 /*
635 * Output a plain backspace, without erasing the previous char.
636 */
637 void
638 putbs()
639 {
640 tputs(sc_backspace, 1, putchr);
641 }
642