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