135209Sbostic /*
235209Sbostic * Copyright (c) 1988 Mark Nudleman
362131Sbostic * Copyright (c) 1988, 1993
462131Sbostic * The Regents of the University of California. All rights reserved.
535209Sbostic *
642742Sbostic * %sccs.include.redist.c%
735209Sbostic */
835209Sbostic
935209Sbostic #ifndef lint
10*67022Sbostic static char sccsid[] = "@(#)screen.c 8.2 (Berkeley) 04/20/94";
1135209Sbostic #endif /* not lint */
1235209Sbostic
1335209Sbostic /*
1435209Sbostic * Routines which deal with the characteristics of the terminal.
1535209Sbostic * Uses termcap to be as terminal-independent as possible.
1635209Sbostic *
1735209Sbostic * {{ Someday this should be rewritten to use curses. }}
1835209Sbostic */
1935209Sbostic
2036253Sbostic #include <stdio.h>
2136253Sbostic #include <less.h>
2235209Sbostic
23*67022Sbostic #define TERMIOS 1
24*67022Sbostic
2535209Sbostic #if TERMIO
2635209Sbostic #include <termio.h>
2735209Sbostic #else
28*67022Sbostic #if TERMIOS
29*67022Sbostic #include <termios.h>
30*67022Sbostic #define TAB3 0
31*67022Sbostic #include <sys/ioctl.h>
32*67022Sbostic #else
3335209Sbostic #include <sgtty.h>
3435209Sbostic #endif
35*67022Sbostic #endif
3635209Sbostic
3735209Sbostic #ifdef TIOCGWINSZ
3835209Sbostic #include <sys/ioctl.h>
3935209Sbostic #else
4035209Sbostic /*
4135209Sbostic * For the Unix PC (ATT 7300 & 3B1):
4235209Sbostic * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
4335209Sbostic * whether to include sys/window.h. Use SIGPHONE from sys/signal.h instead.
4435209Sbostic */
4535209Sbostic #include <sys/signal.h>
4635209Sbostic #ifdef SIGPHONE
4735209Sbostic #include <sys/window.h>
4835209Sbostic #endif
4935209Sbostic #endif
5035209Sbostic
5135209Sbostic /*
5235209Sbostic * Strings passed to tputs() to do various terminal functions.
5335209Sbostic */
5435209Sbostic static char
5535209Sbostic *sc_pad, /* Pad string */
5635209Sbostic *sc_home, /* Cursor home */
5735209Sbostic *sc_addline, /* Add line, scroll down following lines */
5835209Sbostic *sc_lower_left, /* Cursor to last line, first column */
5935209Sbostic *sc_move, /* General cursor positioning */
6035209Sbostic *sc_clear, /* Clear screen */
6135209Sbostic *sc_eol_clear, /* Clear to end of line */
6235209Sbostic *sc_s_in, /* Enter standout (highlighted) mode */
6335209Sbostic *sc_s_out, /* Exit standout mode */
6435209Sbostic *sc_u_in, /* Enter underline mode */
6535209Sbostic *sc_u_out, /* Exit underline mode */
6635209Sbostic *sc_b_in, /* Enter bold mode */
6735209Sbostic *sc_b_out, /* Exit bold mode */
6835209Sbostic *sc_backspace, /* Backspace cursor */
6935209Sbostic *sc_init, /* Startup terminal initialization */
7035209Sbostic *sc_deinit; /* Exit terminal de-intialization */
7135209Sbostic
7236253Sbostic int auto_wrap; /* Terminal does \r\n when write past margin */
7336253Sbostic int ignaw; /* Terminal ignores \n immediately after wrap */
7436253Sbostic /* The user's erase and line-kill chars */
7557940Sedward int retain_below; /* Terminal retains text below the screen */
7636253Sbostic int erase_char, kill_char, werase_char;
7736253Sbostic int sc_width, sc_height = -1; /* Height & width of screen */
7836253Sbostic int sc_window = -1; /* window size for forward and backward */
7936253Sbostic int bo_width, be_width; /* Printing width of boldface sequences */
8036253Sbostic int ul_width, ue_width; /* Printing width of underline sequences */
8136253Sbostic int so_width, se_width; /* Printing width of standout sequences */
8235209Sbostic
8335209Sbostic /*
8435209Sbostic * These two variables are sometimes defined in,
8535209Sbostic * and needed by, the termcap library.
8635209Sbostic * It may be necessary on some systems to declare them extern here.
8735209Sbostic */
8835209Sbostic /*extern*/ short ospeed; /* Terminal output baud rate */
8935209Sbostic /*extern*/ char PC; /* Pad character */
9035209Sbostic
9135209Sbostic extern int back_scroll;
9235209Sbostic char *tgetstr();
9335209Sbostic char *tgoto();
9435209Sbostic
9535209Sbostic /*
9635209Sbostic * Change terminal to "raw mode", or restore to "normal" mode.
9735209Sbostic * "Raw mode" means
9835209Sbostic * 1. An outstanding read will complete on receipt of a single keystroke.
9935209Sbostic * 2. Input is not echoed.
10035209Sbostic * 3. On output, \n is mapped to \r\n.
10135209Sbostic * 4. \t is NOT expanded into spaces.
10235209Sbostic * 5. Signal-causing characters such as ctrl-C (interrupt),
10335209Sbostic * etc. are NOT disabled.
10435209Sbostic * It doesn't matter whether an input \n is mapped to \r, or vice versa.
10535209Sbostic */
raw_mode(on)10635209Sbostic raw_mode(on)
10735209Sbostic int on;
10835209Sbostic {
109*67022Sbostic #if TERMIO || TERMIOS
110*67022Sbostic
11135209Sbostic #if TERMIO
11235209Sbostic struct termio s;
11335209Sbostic static struct termio save_term;
114*67022Sbostic #else
115*67022Sbostic struct termios s;
116*67022Sbostic static struct termios save_term;
117*67022Sbostic #endif
11835209Sbostic
11935209Sbostic if (on)
12035209Sbostic {
12135209Sbostic /*
12235209Sbostic * Get terminal modes.
12335209Sbostic */
124*67022Sbostic #if TERMIO
12536253Sbostic (void)ioctl(2, TCGETA, &s);
126*67022Sbostic #else
127*67022Sbostic tcgetattr(2, &s);
128*67022Sbostic #endif
12935209Sbostic
13035209Sbostic /*
13135209Sbostic * Save modes and set certain variables dependent on modes.
13235209Sbostic */
13335209Sbostic save_term = s;
134*67022Sbostic #if TERMIO
13535209Sbostic ospeed = s.c_cflag & CBAUD;
136*67022Sbostic #else
137*67022Sbostic ospeed = cfgetospeed(&s);
138*67022Sbostic #endif
13935209Sbostic erase_char = s.c_cc[VERASE];
14035209Sbostic kill_char = s.c_cc[VKILL];
14136253Sbostic werase_char = s.c_cc[VWERASE];
14235209Sbostic
14335209Sbostic /*
14435209Sbostic * Set the modes to the way we want them.
14535209Sbostic */
14635209Sbostic s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
14735209Sbostic s.c_oflag |= (OPOST|ONLCR|TAB3);
148*67022Sbostic #if TERMIO
14935209Sbostic s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
150*67022Sbostic #endif
15135209Sbostic s.c_cc[VMIN] = 1;
15235209Sbostic s.c_cc[VTIME] = 0;
15335209Sbostic } else
15435209Sbostic {
15535209Sbostic /*
15635209Sbostic * Restore saved modes.
15735209Sbostic */
15835209Sbostic s = save_term;
15935209Sbostic }
160*67022Sbostic #if TERMIO
16136253Sbostic (void)ioctl(2, TCSETAW, &s);
16235209Sbostic #else
163*67022Sbostic tcsetattr(2, TCSADRAIN, &s);
164*67022Sbostic #endif
165*67022Sbostic #else
16635209Sbostic struct sgttyb s;
16754543Sbostic struct ltchars l;
16835209Sbostic static struct sgttyb save_term;
16935209Sbostic
17035209Sbostic if (on)
17135209Sbostic {
17235209Sbostic /*
17335209Sbostic * Get terminal modes.
17435209Sbostic */
17536253Sbostic (void)ioctl(2, TIOCGETP, &s);
17654543Sbostic (void)ioctl(2, TIOCGLTC, &l);
17735209Sbostic
17835209Sbostic /*
17935209Sbostic * Save modes and set certain variables dependent on modes.
18035209Sbostic */
18135209Sbostic save_term = s;
18235209Sbostic ospeed = s.sg_ospeed;
18335209Sbostic erase_char = s.sg_erase;
18435209Sbostic kill_char = s.sg_kill;
18554543Sbostic werase_char = l.t_werasc;
18635209Sbostic
18735209Sbostic /*
18835209Sbostic * Set the modes to the way we want them.
18935209Sbostic */
19035209Sbostic s.sg_flags |= CBREAK;
19135209Sbostic s.sg_flags &= ~(ECHO|XTABS);
19235209Sbostic } else
19335209Sbostic {
19435209Sbostic /*
19535209Sbostic * Restore saved modes.
19635209Sbostic */
19735209Sbostic s = save_term;
19835209Sbostic }
19936253Sbostic (void)ioctl(2, TIOCSETN, &s);
20035209Sbostic #endif
20135209Sbostic }
20235209Sbostic
20335209Sbostic /*
20435209Sbostic * Get terminal capabilities via termcap.
20535209Sbostic */
get_term()20635209Sbostic get_term()
20735209Sbostic {
20835209Sbostic char termbuf[2048];
20935209Sbostic char *sp;
21035209Sbostic char *term;
21135209Sbostic int hard;
21235209Sbostic #ifdef TIOCGWINSZ
21335209Sbostic struct winsize w;
21435209Sbostic #else
21535209Sbostic #ifdef WIOCGETD
21635209Sbostic struct uwdata w;
21735209Sbostic #endif
21835209Sbostic #endif
21935209Sbostic static char sbuf[1024];
22035209Sbostic
22135240Sbostic char *getenv(), *strcpy();
22235209Sbostic
22335209Sbostic /*
22435209Sbostic * Find out what kind of terminal this is.
22535209Sbostic */
22635209Sbostic if ((term = getenv("TERM")) == NULL)
22735209Sbostic term = "unknown";
22835209Sbostic if (tgetent(termbuf, term) <= 0)
22935240Sbostic (void)strcpy(termbuf, "dumb:co#80:hc:");
23035209Sbostic
23135209Sbostic /*
23235209Sbostic * Get size of the screen.
23335209Sbostic */
23435209Sbostic #ifdef TIOCGWINSZ
23536358Sbostic if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
23636358Sbostic sc_height = w.ws_row;
23735209Sbostic #else
23835209Sbostic #ifdef WIOCGETD
23936358Sbostic if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
24036358Sbostic sc_height = w.uw_height/w.uw_vs;
24135209Sbostic #endif
24235209Sbostic #endif
24336358Sbostic else
24436358Sbostic sc_height = tgetnum("li");
24536253Sbostic hard = (sc_height < 0 || tgetflag("hc"));
24636253Sbostic if (hard) {
24735209Sbostic /* Oh no, this is a hardcopy terminal. */
24835209Sbostic sc_height = 24;
24935209Sbostic }
25035209Sbostic
25135209Sbostic #ifdef TIOCGWINSZ
25235209Sbostic if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
25335209Sbostic sc_width = w.ws_col;
25435209Sbostic else
25535209Sbostic #ifdef WIOCGETD
25635209Sbostic if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
25735209Sbostic sc_width = w.uw_width/w.uw_hs;
25835209Sbostic else
25935209Sbostic #endif
26035209Sbostic #endif
26135209Sbostic sc_width = tgetnum("co");
26235209Sbostic if (sc_width < 0)
26335209Sbostic sc_width = 80;
26435209Sbostic
26535209Sbostic auto_wrap = tgetflag("am");
26635209Sbostic ignaw = tgetflag("xn");
26757940Sedward retain_below = tgetflag("db");
26835209Sbostic
26935209Sbostic /*
27035209Sbostic * Assumes termcap variable "sg" is the printing width of
27135209Sbostic * the standout sequence, the end standout sequence,
27235209Sbostic * the underline sequence, the end underline sequence,
27335209Sbostic * the boldface sequence, and the end boldface sequence.
27435209Sbostic */
27535209Sbostic if ((so_width = tgetnum("sg")) < 0)
27635209Sbostic so_width = 0;
27735209Sbostic be_width = bo_width = ue_width = ul_width = se_width = so_width;
27835209Sbostic
27935209Sbostic /*
28035209Sbostic * Get various string-valued capabilities.
28135209Sbostic */
28235209Sbostic sp = sbuf;
28335209Sbostic
28435209Sbostic sc_pad = tgetstr("pc", &sp);
28535209Sbostic if (sc_pad != NULL)
28635209Sbostic PC = *sc_pad;
28735209Sbostic
28835209Sbostic sc_init = tgetstr("ti", &sp);
28935209Sbostic if (sc_init == NULL)
29035209Sbostic sc_init = "";
29135209Sbostic
29235209Sbostic sc_deinit= tgetstr("te", &sp);
29335209Sbostic if (sc_deinit == NULL)
29435209Sbostic sc_deinit = "";
29535209Sbostic
29635209Sbostic sc_eol_clear = tgetstr("ce", &sp);
29735209Sbostic if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
29835209Sbostic {
29935209Sbostic sc_eol_clear = "";
30035209Sbostic }
30135209Sbostic
30235209Sbostic sc_clear = tgetstr("cl", &sp);
30335209Sbostic if (hard || sc_clear == NULL || *sc_clear == '\0')
30435209Sbostic {
30535209Sbostic sc_clear = "\n\n";
30635209Sbostic }
30735209Sbostic
30835209Sbostic sc_move = tgetstr("cm", &sp);
30935209Sbostic if (hard || sc_move == NULL || *sc_move == '\0')
31035209Sbostic {
31135209Sbostic /*
31235209Sbostic * This is not an error here, because we don't
31335209Sbostic * always need sc_move.
31435209Sbostic * We need it only if we don't have home or lower-left.
31535209Sbostic */
31635209Sbostic sc_move = "";
31735209Sbostic }
31835209Sbostic
31935209Sbostic sc_s_in = tgetstr("so", &sp);
32035209Sbostic if (hard || sc_s_in == NULL)
32135209Sbostic sc_s_in = "";
32235209Sbostic
32335209Sbostic sc_s_out = tgetstr("se", &sp);
32435209Sbostic if (hard || sc_s_out == NULL)
32535209Sbostic sc_s_out = "";
32635209Sbostic
32735209Sbostic sc_u_in = tgetstr("us", &sp);
32835209Sbostic if (hard || sc_u_in == NULL)
32935209Sbostic sc_u_in = sc_s_in;
33035209Sbostic
33135209Sbostic sc_u_out = tgetstr("ue", &sp);
33235209Sbostic if (hard || sc_u_out == NULL)
33335209Sbostic sc_u_out = sc_s_out;
33435209Sbostic
33535209Sbostic sc_b_in = tgetstr("md", &sp);
33635209Sbostic if (hard || sc_b_in == NULL)
33735209Sbostic {
33835209Sbostic sc_b_in = sc_s_in;
33935209Sbostic sc_b_out = sc_s_out;
34035209Sbostic } else
34135209Sbostic {
34235209Sbostic sc_b_out = tgetstr("me", &sp);
34335209Sbostic if (hard || sc_b_out == NULL)
34435209Sbostic sc_b_out = "";
34535209Sbostic }
34635209Sbostic
34735209Sbostic sc_home = tgetstr("ho", &sp);
34835209Sbostic if (hard || sc_home == NULL || *sc_home == '\0')
34935209Sbostic {
35035209Sbostic if (*sc_move == '\0')
35135209Sbostic {
35235209Sbostic /*
35335209Sbostic * This last resort for sc_home is supposed to
35435209Sbostic * be an up-arrow suggesting moving to the
35535209Sbostic * top of the "virtual screen". (The one in
35635209Sbostic * your imagination as you try to use this on
35735209Sbostic * a hard copy terminal.)
35835209Sbostic */
35936265Sbostic sc_home = "|\b^";
36035209Sbostic } else
36135209Sbostic {
36235209Sbostic /*
36335209Sbostic * No "home" string,
36435209Sbostic * but we can use "move(0,0)".
36535209Sbostic */
36635240Sbostic (void)strcpy(sp, tgoto(sc_move, 0, 0));
36735209Sbostic sc_home = sp;
36835209Sbostic sp += strlen(sp) + 1;
36935209Sbostic }
37035209Sbostic }
37135209Sbostic
37235209Sbostic sc_lower_left = tgetstr("ll", &sp);
37335209Sbostic if (hard || sc_lower_left == NULL || *sc_lower_left == '\0')
37435209Sbostic {
37535209Sbostic if (*sc_move == '\0')
37635209Sbostic {
37735209Sbostic sc_lower_left = "\r";
37835209Sbostic } else
37935209Sbostic {
38035209Sbostic /*
38135209Sbostic * No "lower-left" string,
38235209Sbostic * but we can use "move(0,last-line)".
38335209Sbostic */
38435240Sbostic (void)strcpy(sp, tgoto(sc_move, 0, sc_height-1));
38535209Sbostic sc_lower_left = sp;
38635209Sbostic sp += strlen(sp) + 1;
38735209Sbostic }
38835209Sbostic }
38935209Sbostic
39035209Sbostic /*
39135209Sbostic * To add a line at top of screen and scroll the display down,
39235209Sbostic * we use "al" (add line) or "sr" (scroll reverse).
39335209Sbostic */
39435209Sbostic if ((sc_addline = tgetstr("al", &sp)) == NULL ||
39535209Sbostic *sc_addline == '\0')
39635209Sbostic sc_addline = tgetstr("sr", &sp);
39735209Sbostic
39835209Sbostic if (hard || sc_addline == NULL || *sc_addline == '\0')
39935209Sbostic {
40035209Sbostic sc_addline = "";
40135209Sbostic /* Force repaint on any backward movement */
40235209Sbostic back_scroll = 0;
40335209Sbostic }
40435209Sbostic
40535209Sbostic if (tgetflag("bs"))
40635209Sbostic sc_backspace = "\b";
40735209Sbostic else
40835209Sbostic {
40935209Sbostic sc_backspace = tgetstr("bc", &sp);
41035209Sbostic if (sc_backspace == NULL || *sc_backspace == '\0')
41135209Sbostic sc_backspace = "\b";
41235209Sbostic }
41335209Sbostic }
41435209Sbostic
41535209Sbostic
41635209Sbostic /*
41735209Sbostic * Below are the functions which perform all the
41835209Sbostic * terminal-specific screen manipulation.
41935209Sbostic */
42035209Sbostic
42136253Sbostic int putchr();
42235209Sbostic
42335209Sbostic /*
42435209Sbostic * Initialize terminal
42535209Sbostic */
init()42635209Sbostic init()
42735209Sbostic {
42835209Sbostic tputs(sc_init, sc_height, putchr);
42935209Sbostic }
43035209Sbostic
43135209Sbostic /*
43235209Sbostic * Deinitialize terminal
43335209Sbostic */
deinit()43435209Sbostic deinit()
43535209Sbostic {
43635209Sbostic tputs(sc_deinit, sc_height, putchr);
43735209Sbostic }
43835209Sbostic
43935209Sbostic /*
44035209Sbostic * Home cursor (move to upper left corner of screen).
44135209Sbostic */
home()44235209Sbostic home()
44335209Sbostic {
44435209Sbostic tputs(sc_home, 1, putchr);
44535209Sbostic }
44635209Sbostic
44735209Sbostic /*
44835209Sbostic * Add a blank line (called with cursor at home).
44935209Sbostic * Should scroll the display down.
45035209Sbostic */
add_line()45135209Sbostic add_line()
45235209Sbostic {
45335209Sbostic tputs(sc_addline, sc_height, putchr);
45435209Sbostic }
45535209Sbostic
45636358Sbostic int short_file; /* if file less than a screen */
lower_left()45735209Sbostic lower_left()
45835209Sbostic {
45936358Sbostic if (short_file) {
46036358Sbostic putchr('\r');
46136358Sbostic flush();
46236358Sbostic }
46336358Sbostic else
46436358Sbostic tputs(sc_lower_left, 1, putchr);
46535209Sbostic }
46635209Sbostic
46735209Sbostic /*
46835209Sbostic * Ring the terminal bell.
46935209Sbostic */
bell()47035209Sbostic bell()
47135209Sbostic {
47236253Sbostic putchr('\7');
47335209Sbostic }
47435209Sbostic
47535209Sbostic /*
47635209Sbostic * Clear the screen.
47735209Sbostic */
clear()47835209Sbostic clear()
47935209Sbostic {
48035209Sbostic tputs(sc_clear, sc_height, putchr);
48135209Sbostic }
48235209Sbostic
48335209Sbostic /*
48435209Sbostic * Clear from the cursor to the end of the cursor's line.
48535209Sbostic * {{ This must not move the cursor. }}
48635209Sbostic */
clear_eol()48735209Sbostic clear_eol()
48835209Sbostic {
48935209Sbostic tputs(sc_eol_clear, 1, putchr);
49035209Sbostic }
49135209Sbostic
49235209Sbostic /*
49335209Sbostic * Begin "standout" (bold, underline, or whatever).
49435209Sbostic */
so_enter()49535209Sbostic so_enter()
49635209Sbostic {
49735209Sbostic tputs(sc_s_in, 1, putchr);
49835209Sbostic }
49935209Sbostic
50035209Sbostic /*
50135209Sbostic * End "standout".
50235209Sbostic */
so_exit()50335209Sbostic so_exit()
50435209Sbostic {
50535209Sbostic tputs(sc_s_out, 1, putchr);
50635209Sbostic }
50735209Sbostic
50835209Sbostic /*
50935209Sbostic * Begin "underline" (hopefully real underlining,
51035209Sbostic * otherwise whatever the terminal provides).
51135209Sbostic */
ul_enter()51235209Sbostic ul_enter()
51335209Sbostic {
51435209Sbostic tputs(sc_u_in, 1, putchr);
51535209Sbostic }
51635209Sbostic
51735209Sbostic /*
51835209Sbostic * End "underline".
51935209Sbostic */
ul_exit()52035209Sbostic ul_exit()
52135209Sbostic {
52235209Sbostic tputs(sc_u_out, 1, putchr);
52335209Sbostic }
52435209Sbostic
52535209Sbostic /*
52635209Sbostic * Begin "bold"
52735209Sbostic */
bo_enter()52835209Sbostic bo_enter()
52935209Sbostic {
53035209Sbostic tputs(sc_b_in, 1, putchr);
53135209Sbostic }
53235209Sbostic
53335209Sbostic /*
53435209Sbostic * End "bold".
53535209Sbostic */
bo_exit()53635209Sbostic bo_exit()
53735209Sbostic {
53835209Sbostic tputs(sc_b_out, 1, putchr);
53935209Sbostic }
54035209Sbostic
54135209Sbostic /*
54235209Sbostic * Erase the character to the left of the cursor
54335209Sbostic * and move the cursor left.
54435209Sbostic */
backspace()54535209Sbostic backspace()
54635209Sbostic {
54735209Sbostic /*
54835209Sbostic * Try to erase the previous character by overstriking with a space.
54935209Sbostic */
55035209Sbostic tputs(sc_backspace, 1, putchr);
55135209Sbostic putchr(' ');
55235209Sbostic tputs(sc_backspace, 1, putchr);
55335209Sbostic }
55435209Sbostic
55535209Sbostic /*
55635209Sbostic * Output a plain backspace, without erasing the previous char.
55735209Sbostic */
putbs()55835209Sbostic putbs()
55935209Sbostic {
56035209Sbostic tputs(sc_backspace, 1, putchr);
56135209Sbostic }
562