Home | History | Annotate | Line # | Download | only in more
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