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