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