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