xref: /original-bsd/usr.bin/more/screen.c (revision c3e32dec)
1 /*
2  * Copyright (c) 1988 Mark Nudleman
3  * Copyright (c) 1988, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)screen.c	8.1 (Berkeley) 06/06/93";
11 #endif /* not lint */
12 
13 /*
14  * Routines which deal with the characteristics of the terminal.
15  * Uses termcap to be as terminal-independent as possible.
16  *
17  * {{ Someday this should be rewritten to use curses. }}
18  */
19 
20 #include <stdio.h>
21 #include <less.h>
22 
23 #if TERMIO
24 #include <termio.h>
25 #else
26 #include <sgtty.h>
27 #endif
28 
29 #ifdef TIOCGWINSZ
30 #include <sys/ioctl.h>
31 #else
32 /*
33  * For the Unix PC (ATT 7300 & 3B1):
34  * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
35  * whether to include sys/window.h.  Use SIGPHONE from sys/signal.h instead.
36  */
37 #include <sys/signal.h>
38 #ifdef SIGPHONE
39 #include <sys/window.h>
40 #endif
41 #endif
42 
43 /*
44  * Strings passed to tputs() to do various terminal functions.
45  */
46 static char
47 	*sc_pad,		/* Pad string */
48 	*sc_home,		/* Cursor home */
49 	*sc_addline,		/* Add line, scroll down following lines */
50 	*sc_lower_left,		/* Cursor to last line, first column */
51 	*sc_move,		/* General cursor positioning */
52 	*sc_clear,		/* Clear screen */
53 	*sc_eol_clear,		/* Clear to end of line */
54 	*sc_s_in,		/* Enter standout (highlighted) mode */
55 	*sc_s_out,		/* Exit standout mode */
56 	*sc_u_in,		/* Enter underline mode */
57 	*sc_u_out,		/* Exit underline mode */
58 	*sc_b_in,		/* Enter bold mode */
59 	*sc_b_out,		/* Exit bold mode */
60 	*sc_backspace,		/* Backspace cursor */
61 	*sc_init,		/* Startup terminal initialization */
62 	*sc_deinit;		/* Exit terminal de-intialization */
63 
64 int auto_wrap;			/* Terminal does \r\n when write past margin */
65 int ignaw;			/* Terminal ignores \n immediately after wrap */
66 				/* The user's erase and line-kill chars */
67 int retain_below;		/* Terminal retains text below the screen */
68 int erase_char, kill_char, werase_char;
69 int sc_width, sc_height = -1;	/* Height & width of screen */
70 int sc_window = -1;		/* window size for forward and backward */
71 int bo_width, be_width;		/* Printing width of boldface sequences */
72 int ul_width, ue_width;		/* Printing width of underline sequences */
73 int so_width, se_width;		/* Printing width of standout sequences */
74 
75 /*
76  * These two variables are sometimes defined in,
77  * and needed by, the termcap library.
78  * It may be necessary on some systems to declare them extern here.
79  */
80 /*extern*/ short ospeed;	/* Terminal output baud rate */
81 /*extern*/ char PC;		/* Pad character */
82 
83 extern int back_scroll;
84 char *tgetstr();
85 char *tgoto();
86 
87 /*
88  * Change terminal to "raw mode", or restore to "normal" mode.
89  * "Raw mode" means
90  *	1. An outstanding read will complete on receipt of a single keystroke.
91  *	2. Input is not echoed.
92  *	3. On output, \n is mapped to \r\n.
93  *	4. \t is NOT expanded into spaces.
94  *	5. Signal-causing characters such as ctrl-C (interrupt),
95  *	   etc. are NOT disabled.
96  * It doesn't matter whether an input \n is mapped to \r, or vice versa.
97  */
98 raw_mode(on)
99 	int on;
100 {
101 #if TERMIO
102 	struct termio s;
103 	static struct termio save_term;
104 
105 	if (on)
106 	{
107 		/*
108 		 * Get terminal modes.
109 		 */
110 		(void)ioctl(2, TCGETA, &s);
111 
112 		/*
113 		 * Save modes and set certain variables dependent on modes.
114 		 */
115 		save_term = s;
116 		ospeed = s.c_cflag & CBAUD;
117 		erase_char = s.c_cc[VERASE];
118 		kill_char = s.c_cc[VKILL];
119 		werase_char = s.c_cc[VWERASE];
120 
121 		/*
122 		 * Set the modes to the way we want them.
123 		 */
124 		s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
125 		s.c_oflag |=  (OPOST|ONLCR|TAB3);
126 		s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
127 		s.c_cc[VMIN] = 1;
128 		s.c_cc[VTIME] = 0;
129 	} else
130 	{
131 		/*
132 		 * Restore saved modes.
133 		 */
134 		s = save_term;
135 	}
136 	(void)ioctl(2, TCSETAW, &s);
137 #else
138 	struct sgttyb s;
139 	struct ltchars l;
140 	static struct sgttyb save_term;
141 
142 	if (on)
143 	{
144 		/*
145 		 * Get terminal modes.
146 		 */
147 		(void)ioctl(2, TIOCGETP, &s);
148 		(void)ioctl(2, TIOCGLTC, &l);
149 
150 		/*
151 		 * Save modes and set certain variables dependent on modes.
152 		 */
153 		save_term = s;
154 		ospeed = s.sg_ospeed;
155 		erase_char = s.sg_erase;
156 		kill_char = s.sg_kill;
157 		werase_char = l.t_werasc;
158 
159 		/*
160 		 * Set the modes to the way we want them.
161 		 */
162 		s.sg_flags |= CBREAK;
163 		s.sg_flags &= ~(ECHO|XTABS);
164 	} else
165 	{
166 		/*
167 		 * Restore saved modes.
168 		 */
169 		s = save_term;
170 	}
171 	(void)ioctl(2, TIOCSETN, &s);
172 #endif
173 }
174 
175 /*
176  * Get terminal capabilities via termcap.
177  */
178 get_term()
179 {
180 	char termbuf[2048];
181 	char *sp;
182 	char *term;
183 	int hard;
184 #ifdef TIOCGWINSZ
185 	struct winsize w;
186 #else
187 #ifdef WIOCGETD
188 	struct uwdata w;
189 #endif
190 #endif
191 	static char sbuf[1024];
192 
193 	char *getenv(), *strcpy();
194 
195 	/*
196 	 * Find out what kind of terminal this is.
197 	 */
198  	if ((term = getenv("TERM")) == NULL)
199  		term = "unknown";
200  	if (tgetent(termbuf, term) <= 0)
201  		(void)strcpy(termbuf, "dumb:co#80:hc:");
202 
203 	/*
204 	 * Get size of the screen.
205 	 */
206 #ifdef TIOCGWINSZ
207 	if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
208 		sc_height = w.ws_row;
209 #else
210 #ifdef WIOCGETD
211 	if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
212 		sc_height = w.uw_height/w.uw_vs;
213 #endif
214 #endif
215 	else
216 		sc_height = tgetnum("li");
217 	hard = (sc_height < 0 || tgetflag("hc"));
218 	if (hard) {
219 		/* Oh no, this is a hardcopy terminal. */
220 		sc_height = 24;
221 	}
222 
223 #ifdef TIOCGWINSZ
224  	if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
225 		sc_width = w.ws_col;
226 	else
227 #ifdef WIOCGETD
228 	if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
229 		sc_width = w.uw_width/w.uw_hs;
230 	else
231 #endif
232 #endif
233  		sc_width = tgetnum("co");
234  	if (sc_width < 0)
235   		sc_width = 80;
236 
237 	auto_wrap = tgetflag("am");
238 	ignaw = tgetflag("xn");
239 	retain_below = tgetflag("db");
240 
241 	/*
242 	 * Assumes termcap variable "sg" is the printing width of
243 	 * the standout sequence, the end standout sequence,
244 	 * the underline sequence, the end underline sequence,
245 	 * the boldface sequence, and the end boldface sequence.
246 	 */
247 	if ((so_width = tgetnum("sg")) < 0)
248 		so_width = 0;
249 	be_width = bo_width = ue_width = ul_width = se_width = so_width;
250 
251 	/*
252 	 * Get various string-valued capabilities.
253 	 */
254 	sp = sbuf;
255 
256 	sc_pad = tgetstr("pc", &sp);
257 	if (sc_pad != NULL)
258 		PC = *sc_pad;
259 
260 	sc_init = tgetstr("ti", &sp);
261 	if (sc_init == NULL)
262 		sc_init = "";
263 
264 	sc_deinit= tgetstr("te", &sp);
265 	if (sc_deinit == NULL)
266 		sc_deinit = "";
267 
268 	sc_eol_clear = tgetstr("ce", &sp);
269 	if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
270 	{
271 		sc_eol_clear = "";
272 	}
273 
274 	sc_clear = tgetstr("cl", &sp);
275 	if (hard || sc_clear == NULL || *sc_clear == '\0')
276 	{
277 		sc_clear = "\n\n";
278 	}
279 
280 	sc_move = tgetstr("cm", &sp);
281 	if (hard || sc_move == NULL || *sc_move == '\0')
282 	{
283 		/*
284 		 * This is not an error here, because we don't
285 		 * always need sc_move.
286 		 * We need it only if we don't have home or lower-left.
287 		 */
288 		sc_move = "";
289 	}
290 
291 	sc_s_in = tgetstr("so", &sp);
292 	if (hard || sc_s_in == NULL)
293 		sc_s_in = "";
294 
295 	sc_s_out = tgetstr("se", &sp);
296 	if (hard || sc_s_out == NULL)
297 		sc_s_out = "";
298 
299 	sc_u_in = tgetstr("us", &sp);
300 	if (hard || sc_u_in == NULL)
301 		sc_u_in = sc_s_in;
302 
303 	sc_u_out = tgetstr("ue", &sp);
304 	if (hard || sc_u_out == NULL)
305 		sc_u_out = sc_s_out;
306 
307 	sc_b_in = tgetstr("md", &sp);
308 	if (hard || sc_b_in == NULL)
309 	{
310 		sc_b_in = sc_s_in;
311 		sc_b_out = sc_s_out;
312 	} else
313 	{
314 		sc_b_out = tgetstr("me", &sp);
315 		if (hard || sc_b_out == NULL)
316 			sc_b_out = "";
317 	}
318 
319 	sc_home = tgetstr("ho", &sp);
320 	if (hard || sc_home == NULL || *sc_home == '\0')
321 	{
322 		if (*sc_move == '\0')
323 		{
324 			/*
325 			 * This last resort for sc_home is supposed to
326 			 * be an up-arrow suggesting moving to the
327 			 * top of the "virtual screen". (The one in
328 			 * your imagination as you try to use this on
329 			 * a hard copy terminal.)
330 			 */
331 			sc_home = "|\b^";
332 		} else
333 		{
334 			/*
335 			 * No "home" string,
336 			 * but we can use "move(0,0)".
337 			 */
338 			(void)strcpy(sp, tgoto(sc_move, 0, 0));
339 			sc_home = sp;
340 			sp += strlen(sp) + 1;
341 		}
342 	}
343 
344 	sc_lower_left = tgetstr("ll", &sp);
345 	if (hard || sc_lower_left == NULL || *sc_lower_left == '\0')
346 	{
347 		if (*sc_move == '\0')
348 		{
349 			sc_lower_left = "\r";
350 		} else
351 		{
352 			/*
353 			 * No "lower-left" string,
354 			 * but we can use "move(0,last-line)".
355 			 */
356 			(void)strcpy(sp, tgoto(sc_move, 0, sc_height-1));
357 			sc_lower_left = sp;
358 			sp += strlen(sp) + 1;
359 		}
360 	}
361 
362 	/*
363 	 * To add a line at top of screen and scroll the display down,
364 	 * we use "al" (add line) or "sr" (scroll reverse).
365 	 */
366 	if ((sc_addline = tgetstr("al", &sp)) == NULL ||
367 		 *sc_addline == '\0')
368 		sc_addline = tgetstr("sr", &sp);
369 
370 	if (hard || sc_addline == NULL || *sc_addline == '\0')
371 	{
372 		sc_addline = "";
373 		/* Force repaint on any backward movement */
374 		back_scroll = 0;
375 	}
376 
377 	if (tgetflag("bs"))
378 		sc_backspace = "\b";
379 	else
380 	{
381 		sc_backspace = tgetstr("bc", &sp);
382 		if (sc_backspace == NULL || *sc_backspace == '\0')
383 			sc_backspace = "\b";
384 	}
385 }
386 
387 
388 /*
389  * Below are the functions which perform all the
390  * terminal-specific screen manipulation.
391  */
392 
393 int putchr();
394 
395 /*
396  * Initialize terminal
397  */
398 init()
399 {
400 	tputs(sc_init, sc_height, putchr);
401 }
402 
403 /*
404  * Deinitialize terminal
405  */
406 deinit()
407 {
408 	tputs(sc_deinit, sc_height, putchr);
409 }
410 
411 /*
412  * Home cursor (move to upper left corner of screen).
413  */
414 home()
415 {
416 	tputs(sc_home, 1, putchr);
417 }
418 
419 /*
420  * Add a blank line (called with cursor at home).
421  * Should scroll the display down.
422  */
423 add_line()
424 {
425 	tputs(sc_addline, sc_height, putchr);
426 }
427 
428 int short_file;				/* if file less than a screen */
429 lower_left()
430 {
431 	if (short_file) {
432 		putchr('\r');
433 		flush();
434 	}
435 	else
436 		tputs(sc_lower_left, 1, putchr);
437 }
438 
439 /*
440  * Ring the terminal bell.
441  */
442 bell()
443 {
444 	putchr('\7');
445 }
446 
447 /*
448  * Clear the screen.
449  */
450 clear()
451 {
452 	tputs(sc_clear, sc_height, putchr);
453 }
454 
455 /*
456  * Clear from the cursor to the end of the cursor's line.
457  * {{ This must not move the cursor. }}
458  */
459 clear_eol()
460 {
461 	tputs(sc_eol_clear, 1, putchr);
462 }
463 
464 /*
465  * Begin "standout" (bold, underline, or whatever).
466  */
467 so_enter()
468 {
469 	tputs(sc_s_in, 1, putchr);
470 }
471 
472 /*
473  * End "standout".
474  */
475 so_exit()
476 {
477 	tputs(sc_s_out, 1, putchr);
478 }
479 
480 /*
481  * Begin "underline" (hopefully real underlining,
482  * otherwise whatever the terminal provides).
483  */
484 ul_enter()
485 {
486 	tputs(sc_u_in, 1, putchr);
487 }
488 
489 /*
490  * End "underline".
491  */
492 ul_exit()
493 {
494 	tputs(sc_u_out, 1, putchr);
495 }
496 
497 /*
498  * Begin "bold"
499  */
500 bo_enter()
501 {
502 	tputs(sc_b_in, 1, putchr);
503 }
504 
505 /*
506  * End "bold".
507  */
508 bo_exit()
509 {
510 	tputs(sc_b_out, 1, putchr);
511 }
512 
513 /*
514  * Erase the character to the left of the cursor
515  * and move the cursor left.
516  */
517 backspace()
518 {
519 	/*
520 	 * Try to erase the previous character by overstriking with a space.
521 	 */
522 	tputs(sc_backspace, 1, putchr);
523 	putchr(' ');
524 	tputs(sc_backspace, 1, putchr);
525 }
526 
527 /*
528  * Output a plain backspace, without erasing the previous char.
529  */
530 putbs()
531 {
532 	tputs(sc_backspace, 1, putchr);
533 }
534