xref: /openbsd/usr.bin/less/screen.c (revision 78b63d65)
1 /*	$OpenBSD: screen.c,v 1.8 2001/11/19 19:02:14 mpech Exp $	*/
2 
3 /*
4  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice in the documentation and/or other materials provided with
14  *    the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
22  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 
30 /*
31  * Routines which deal with the characteristics of the terminal.
32  * Uses termcap to be as terminal-independent as possible.
33  *
34  * {{ Maybe someday this should be rewritten to use curses or terminfo. }}
35  */
36 
37 #include "less.h"
38 #include "cmd.h"
39 
40 #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
41 #include <termios.h>
42 #if HAVE_SYS_IOCTL_H && !defined(TIOCGWINSZ)
43 #include <sys/ioctl.h>
44 #endif
45 #else
46 #if HAVE_TERMIO_H
47 #include <termio.h>
48 #else
49 #include <sgtty.h>
50 #if HAVE_SYS_IOCTL_H && (defined(TIOCGWINSZ) || defined(TCGETA) || defined(TIOCGETP) || defined(WIOCGETD))
51 #include <sys/ioctl.h>
52 #endif
53 #endif
54 #endif
55 #if HAVE_TERMCAP_H
56 #include <termcap.h>
57 #endif
58 
59 #ifndef TIOCGWINSZ
60 /*
61  * For the Unix PC (ATT 7300 & 3B1):
62  * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
63  * whether to include sys/window.h.  Use SIGPHONE from sys/signal.h instead.
64  */
65 #include <sys/signal.h>
66 #ifdef SIGPHONE
67 #include <sys/window.h>
68 #endif
69 #endif
70 
71 #if HAVE_SYS_STREAM_H
72 #include <sys/stream.h>
73 #endif
74 #if HAVE_SYS_PTEM_H
75 #include <sys/ptem.h>
76 #endif
77 
78 #if OS2
79 #define	DEFAULT_TERM		"ansi"
80 #else
81 #define	DEFAULT_TERM		"unknown"
82 #endif
83 
84 /*
85  * Strings passed to tputs() to do various terminal functions.
86  */
87 static char
88 	*sc_pad,		/* Pad string */
89 	*sc_home,		/* Cursor home */
90 	*sc_addline,		/* Add line, scroll down following lines */
91 	*sc_lower_left,		/* Cursor to last line, first column */
92 	*sc_move,		/* General cursor positioning */
93 	*sc_clear,		/* Clear screen */
94 	*sc_eol_clear,		/* Clear to end of line */
95 	*sc_eos_clear,		/* Clear to end of screen */
96 	*sc_s_in,		/* Enter standout (highlighted) mode */
97 	*sc_s_out,		/* Exit standout mode */
98 	*sc_u_in,		/* Enter underline mode */
99 	*sc_u_out,		/* Exit underline mode */
100 	*sc_b_in,		/* Enter bold mode */
101 	*sc_b_out,		/* Exit bold mode */
102 	*sc_bl_in,		/* Enter blink mode */
103 	*sc_bl_out,		/* Exit blink mode */
104 	*sc_visual_bell,	/* Visual bell (flash screen) sequence */
105 	*sc_backspace,		/* Backspace cursor */
106 	*sc_s_keypad,		/* Start keypad mode */
107 	*sc_e_keypad,		/* End keypad mode */
108 	*sc_init,		/* Startup terminal initialization */
109 	*sc_deinit;		/* Exit terminal de-initialization */
110 
111 static int init_done = 0;
112 static int tty_fd = -1;
113 
114 public int auto_wrap;		/* Terminal does \r\n when write past margin */
115 public int ignaw;		/* Terminal ignores \n immediately after wrap */
116 public int erase_char, kill_char; /* The user's erase and line-kill chars */
117 public int werase_char;		/* The user's word-erase char */
118 public int sc_width, sc_height;	/* Height & width of screen */
119 public int bo_s_width, bo_e_width;	/* Printing width of boldface seq */
120 public int ul_s_width, ul_e_width;	/* Printing width of underline seq */
121 public int so_s_width, so_e_width;	/* Printing width of standout seq */
122 public int bl_s_width, bl_e_width;	/* Printing width of blink seq */
123 public int above_mem, below_mem;	/* Memory retained above/below screen */
124 public int can_goto_line;		/* Can move cursor to any line */
125 
126 static char *cheaper();
127 
128 /*
129  * These two variables are sometimes defined in,
130  * and needed by, the termcap library.
131  */
132 #if MUST_DEFINE_OSPEED
133 extern short ospeed;	/* Terminal output baud rate */
134 extern char PC;		/* Pad character */
135 #endif
136 
137 extern int quiet;		/* If VERY_QUIET, use visual bell for bell */
138 extern int know_dumb;		/* Don't complain about a dumb terminal */
139 extern int back_scroll;
140 extern int swindow;
141 extern int no_init;
142 extern int quit_at_eof;
143 extern int more_mode;
144 #if HILITE_SEARCH
145 extern int hilite_search;
146 #endif
147 
148 extern char *tgetstr();
149 extern char *tgoto();
150 
151 
152 /*
153  * Change terminal to "raw mode", or restore to "normal" mode.
154  * "Raw mode" means
155  *	1. An outstanding read will complete on receipt of a single keystroke.
156  *	2. Input is not echoed.
157  *	3. On output, \n is mapped to \r\n.
158  *	4. \t is NOT expanded into spaces.
159  *	5. Signal-causing characters such as ctrl-C (interrupt),
160  *	   etc. are NOT disabled.
161  * It doesn't matter whether an input \n is mapped to \r, or vice versa.
162  */
163 	public void
164 raw_mode(on)
165 	int on;
166 {
167 	static int curr_on = 0;
168 
169 	if (on == curr_on)
170 		return;
171 
172 	if (tty_fd == -1 && (tty_fd = open("/dev/tty", O_RDWR)) < 0)
173 		tty_fd = 2;
174 
175 #if OS2
176 	signal(SIGINT, SIG_IGN);
177 	erase_char = '\b';
178 	kill_char = '\033';
179 #else
180 #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
181     {
182 	struct termios s;
183 	static struct termios save_term;
184 
185 	if (on)
186 	{
187 		/*
188 		 * Get terminal modes.
189 		 */
190 		if (tcgetattr(tty_fd, &s) == -1)
191 			return;
192 
193 		/*
194 		 * Save modes and set certain variables dependent on modes.
195 		 */
196 		save_term = s;
197 #if HAVE_OSPEED
198 		switch (cfgetospeed(&s))
199 		{
200 #ifdef B0
201 		case B0: ospeed = 0; break;
202 #endif
203 #ifdef B50
204 		case B50: ospeed = 1; break;
205 #endif
206 #ifdef B75
207 		case B75: ospeed = 2; break;
208 #endif
209 #ifdef B110
210 		case B110: ospeed = 3; break;
211 #endif
212 #ifdef B134
213 		case B134: ospeed = 4; break;
214 #endif
215 #ifdef B150
216 		case B150: ospeed = 5; break;
217 #endif
218 #ifdef B200
219 		case B200: ospeed = 6; break;
220 #endif
221 #ifdef B300
222 		case B300: ospeed = 7; break;
223 #endif
224 #ifdef B600
225 		case B600: ospeed = 8; break;
226 #endif
227 #ifdef B1200
228 		case B1200: ospeed = 9; break;
229 #endif
230 #ifdef B1800
231 		case B1800: ospeed = 10; break;
232 #endif
233 #ifdef B2400
234 		case B2400: ospeed = 11; break;
235 #endif
236 #ifdef B4800
237 		case B4800: ospeed = 12; break;
238 #endif
239 #ifdef B9600
240 		case B9600: ospeed = 13; break;
241 #endif
242 #ifdef EXTA
243 		case EXTA: ospeed = 14; break;
244 #endif
245 #ifdef EXTB
246 		case EXTB: ospeed = 15; break;
247 #endif
248 #ifdef B57600
249 		case B57600: ospeed = 16; break;
250 #endif
251 #ifdef B115200
252 		case B115200: ospeed = 17; break;
253 #endif
254 		default: ;
255 		}
256 #endif
257 		erase_char = s.c_cc[VERASE];
258 		kill_char = s.c_cc[VKILL];
259 #ifdef VWERASE
260 		werase_char = s.c_cc[VWERASE];
261 #else
262 		werase_char = 0;
263 #endif
264 
265 		/*
266 		 * Set the modes to the way we want them.
267 		 */
268 		s.c_lflag &= ~(0
269 #ifdef ICANON
270 			| ICANON
271 #endif
272 #ifdef ECHO
273 			| ECHO
274 #endif
275 #ifdef ECHOE
276 			| ECHOE
277 #endif
278 #ifdef ECHOK
279 			| ECHOK
280 #endif
281 #if ECHONL
282 			| ECHONL
283 #endif
284 		);
285 
286 		s.c_oflag |= (0
287 #ifdef XTABS
288 			| XTABS
289 #else
290 #ifdef TAB3
291 			| TAB3
292 #else
293 #ifdef OXTABS
294 			| OXTABS
295 #endif
296 #endif
297 #endif
298 #ifdef OPOST
299 			| OPOST
300 #endif
301 #ifdef ONLCR
302 			| ONLCR
303 #endif
304 		);
305 
306 		s.c_oflag &= ~(0
307 #ifdef ONOEOT
308 			| ONOEOT
309 #endif
310 #ifdef OCRNL
311 			| OCRNL
312 #endif
313 #ifdef ONOCR
314 			| ONOCR
315 #endif
316 #ifdef ONLRET
317 			| ONLRET
318 #endif
319 		);
320 		s.c_cc[VMIN] = 1;
321 		s.c_cc[VTIME] = 0;
322 	} else
323 	{
324 		/*
325 		 * Restore saved modes.
326 		 */
327 		s = save_term;
328 	}
329 	if (tcsetattr(tty_fd, TCSANOW, &s) == -1)
330 		return;
331     }
332 #else
333 #ifdef TCGETA
334     {
335 	struct termio s;
336 	static struct termio save_term;
337 
338 	if (on)
339 	{
340 		/*
341 		 * Get terminal modes.
342 		 */
343 		ioctl(tty_fd, TCGETA, &s);
344 
345 		/*
346 		 * Save modes and set certain variables dependent on modes.
347 		 */
348 		save_term = s;
349 #if HAVE_OSPEED
350 		ospeed = s.c_cflag & CBAUD;
351 #endif
352 		erase_char = s.c_cc[VERASE];
353 		kill_char = s.c_cc[VKILL];
354 #ifdef VWERASE
355 		werase_char = s.c_cc[VWERASE];
356 #else
357 		werase_char = 0;
358 #endif
359 
360 		/*
361 		 * Set the modes to the way we want them.
362 		 */
363 		s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
364 		s.c_oflag |=  (OPOST|ONLCR|TAB3);
365 		s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
366 		s.c_cc[VMIN] = 1;
367 		s.c_cc[VTIME] = 0;
368 	} else
369 	{
370 		/*
371 		 * Restore saved modes.
372 		 */
373 		s = save_term;
374 	}
375 	ioctl(tty_fd, TCSETAW, &s);
376     }
377 #else
378     {
379 	struct sgttyb s;
380 	static struct sgttyb save_term;
381 
382 	if (on)
383 	{
384 		/*
385 		 * Get terminal modes.
386 		 */
387 		ioctl(tty_fd, TIOCGETP, &s);
388 
389 		/*
390 		 * Save modes and set certain variables dependent on modes.
391 		 */
392 		save_term = s;
393 #if HAVE_OSPEED
394 		ospeed = s.sg_ospeed;
395 #endif
396 		erase_char = s.sg_erase;
397 		kill_char = s.sg_kill;
398 		werase_char = 0;
399 
400 		/*
401 		 * Set the modes to the way we want them.
402 		 */
403 		s.sg_flags |= CBREAK;
404 		s.sg_flags &= ~(ECHO|XTABS);
405 	} else
406 	{
407 		/*
408 		 * Restore saved modes.
409 		 */
410 		s = save_term;
411 	}
412 	ioctl(tty_fd, TIOCSETN, &s);
413     }
414 #endif
415 #endif
416 #endif
417 	curr_on = on;
418 }
419 
420 	static void
421 cannot(s)
422 	char *s;
423 {
424 	PARG parg;
425 
426 	if (know_dumb || more_mode)
427 		/*
428 		 * User knows this is a dumb terminal, so don't tell him.
429 		 * more doesn't complain about these, either.
430 		 */
431 		return;
432 
433 	parg.p_string = s;
434 	error("WARNING: terminal cannot %s", &parg);
435 }
436 
437 /*
438  * Get size of the output screen.
439  */
440 #if OS2
441 	public void
442 scrsize()
443 {
444 	int s[2];
445 
446 	_scrsize(s);
447 	sc_width = s[0];
448 	sc_height = s[1];
449 }
450 
451 #else
452 
453 	public void
454 scrsize()
455 {
456 	char *s;
457 #ifdef TIOCGWINSZ
458 	struct winsize w;
459 #else
460 #ifdef WIOCGETD
461 	struct uwdata w;
462 #endif
463 #endif
464 
465 	if (tty_fd == -1 && (tty_fd = open("/dev/tty", O_RDWR)) < 0)
466 		tty_fd = 2;
467 
468 #ifdef TIOCGWINSZ
469 	if (ioctl(tty_fd, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
470 		sc_height = w.ws_row;
471 	else
472 #else
473 #ifdef WIOCGETD
474 	if (ioctl(tty_fd, WIOCGETD, &w) == 0 && w.uw_height > 0)
475 		sc_height = w.uw_height/w.uw_vs;
476 	else
477 #endif
478 #endif
479 	if ((s = getenv("LINES")) != NULL)
480 		sc_height = atoi(s);
481 	else
482  		sc_height = tgetnum("li");
483 
484 	if (sc_height <= 0)
485 		sc_height = 24;
486 
487 #ifdef TIOCGWINSZ
488  	if (ioctl(tty_fd, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
489 		sc_width = w.ws_col;
490 	else
491 #ifdef WIOCGETD
492 	if (ioctl(tty_fd, WIOCGETD, &w) == 0 && w.uw_width > 0)
493 		sc_width = w.uw_width/w.uw_hs;
494 	else
495 #endif
496 #endif
497 	if ((s = getenv("COLUMNS")) != NULL)
498 		sc_width = atoi(s);
499 	else
500  		sc_width = tgetnum("co");
501 
502  	if (sc_width <= 0)
503   		sc_width = 80;
504 }
505 #endif /* OS2 */
506 
507 /*
508  * Take care of the "variable" keys.
509  * Certain keys send escape sequences which differ on different terminals
510  * (such as the arrow keys, INSERT, DELETE, etc.)
511  * Construct the commands based on these keys.
512  */
513 	public void
514 get_editkeys()
515 {
516 	char *sp;
517 	char *s;
518 	char tbuf[40];
519 
520 	static char kfcmdtable[400];
521 	int sz_kfcmdtable = 0;
522 	static char kecmdtable[400];
523 	int sz_kecmdtable = 0;
524 
525 #define	put_cmd(str,action,tbl,sz) { \
526 	strcpy(tbl+sz, str);	\
527 	sz += strlen(str) + 1;	\
528 	tbl[sz++] = action; }
529 #define	put_esc_cmd(str,action,tbl,sz) { \
530 	tbl[sz++] = ESC; \
531 	put_cmd(str,action,tbl,sz); }
532 
533 #define	put_fcmd(str,action)	put_cmd(str,action,kfcmdtable,sz_kfcmdtable)
534 #define	put_ecmd(str,action)	put_cmd(str,action,kecmdtable,sz_kecmdtable)
535 #define	put_esc_fcmd(str,action) put_esc_cmd(str,action,kfcmdtable,sz_kfcmdtable)
536 #define	put_esc_ecmd(str,action) put_esc_cmd(str,action,kecmdtable,sz_kecmdtable)
537 
538 	/*
539 	 * Look at some interesting keys and see what strings they send.
540 	 * Create commands (both command keys and line-edit keys).
541 	 */
542 
543 	/* RIGHT ARROW */
544 	sp = tbuf;
545 	if ((s = tgetstr("kr", &sp)) != NULL)
546 	{
547 		put_ecmd(s, EC_RIGHT);
548 		put_esc_ecmd(s, EC_W_RIGHT);
549 	}
550 
551 	/* LEFT ARROW */
552 	sp = tbuf;
553 	if ((s = tgetstr("kl", &sp)) != NULL)
554 	{
555 		put_ecmd(s, EC_LEFT);
556 		put_esc_ecmd(s, EC_W_LEFT);
557 	}
558 
559 	/* UP ARROW */
560 	sp = tbuf;
561 	if ((s = tgetstr("ku", &sp)) != NULL)
562 	{
563 		put_ecmd(s, EC_UP);
564 		put_fcmd(s, A_B_LINE);
565 	}
566 
567 	/* DOWN ARROW */
568 	sp = tbuf;
569 	if ((s = tgetstr("kd", &sp)) != NULL)
570 	{
571 		put_ecmd(s, EC_DOWN);
572 		put_fcmd(s, A_F_LINE);
573 	}
574 
575 	/* PAGE UP */
576 	sp = tbuf;
577 	if ((s = tgetstr("kP", &sp)) != NULL)
578 	{
579 		put_fcmd(s, A_B_SCREEN);
580 	}
581 
582 	/* PAGE DOWN */
583 	sp = tbuf;
584 	if ((s = tgetstr("kN", &sp)) != NULL)
585 	{
586 		put_fcmd(s, A_F_SCREEN);
587 	}
588 
589 	/* HOME */
590 	sp = tbuf;
591 	if ((s = tgetstr("kh", &sp)) != NULL)
592 	{
593 		put_ecmd(s, EC_HOME);
594 	}
595 
596 	/* END */
597 	sp = tbuf;
598 	if ((s = tgetstr("@7", &sp)) != NULL)
599 	{
600 		put_ecmd(s, EC_END);
601 	}
602 
603 	/* DELETE */
604 	sp = tbuf;
605 	if ((s = tgetstr("kD", &sp)) == NULL)
606 	{
607 		/* Use DEL (\177) if no "kD" termcap. */
608 		tbuf[1] = '\177';
609 		tbuf[2] = '\0';
610 		s = tbuf+1;
611 	}
612 	put_ecmd(s, EC_DELETE);
613 	put_esc_ecmd(s, EC_W_DELETE);
614 
615 	/* BACKSPACE */
616 	tbuf[0] = ESC;
617 	tbuf[1] = erase_char;
618 	tbuf[2] = '\0';
619 	put_ecmd(tbuf, EC_W_BACKSPACE);
620 
621 	if (werase_char != 0)
622 	{
623 		tbuf[0] = werase_char;
624 		tbuf[1] = '\0';
625 		put_ecmd(tbuf, EC_W_BACKSPACE);
626 	}
627 
628 	/*
629 	 * Register the two tables.
630 	 */
631 	add_fcmd_table(kfcmdtable, sz_kfcmdtable);
632 	add_ecmd_table(kecmdtable, sz_kecmdtable);
633 }
634 
635 #if DEBUG
636 	static void
637 get_debug_term()
638 {
639 	auto_wrap = 1;
640 	ignaw = 1;
641 	so_s_width = so_e_width = 0;
642 	bo_s_width = bo_e_width = 0;
643 	ul_s_width = ul_e_width = 0;
644 	bl_s_width = bl_e_width = 0;
645 	sc_s_keypad =	"(InitKey)";
646 	sc_e_keypad =	"(DeinitKey)";
647 	sc_init =	"(InitTerm)";
648 	sc_deinit =	"(DeinitTerm)";
649 	sc_eol_clear =	"(ClearEOL)";
650 	sc_eos_clear =	"(ClearEOS)";
651 	sc_clear =	"(ClearScreen)";
652 	sc_move =	"(Move<%d,%d>)";
653 	sc_s_in =	"(SO+)";
654 	sc_s_out =	"(SO-)";
655 	sc_u_in =	"(UL+)";
656 	sc_u_out =	"(UL-)";
657 	sc_b_in =	"(BO+)";
658 	sc_b_out =	"(BO-)";
659 	sc_bl_in =	"(BL+)";
660 	sc_bl_out =	"(BL-)";
661 	sc_visual_bell ="(VBell)";
662 	sc_backspace =	"(BS)";
663 	sc_home =	"(Home)";
664 	sc_lower_left =	"(LL)";
665 	sc_addline =	"(AddLine)";
666 }
667 #endif
668 
669 /*
670  * Get terminal capabilities via termcap.
671  */
672 	public void
673 get_term()
674 {
675 	char *sp;
676 	char *t1, *t2;
677 	int hard;
678 	char *term;
679 	char termbuf[2048];
680 
681 	static char sbuf[1024];
682 
683 #ifdef OS2
684 	/*
685 	 * Make sure the termcap database is available.
686 	 */
687 	sp = getenv("TERMCAP");
688 	if (sp == NULL || *sp == '\0')
689 	{
690 		char *termcap;
691 		if ((sp = homefile("termcap.dat")) != NULL)
692 		{
693 			termcap = (char *) ecalloc(strlen(sp)+9, sizeof(char));
694 			sprintf(termcap, "TERMCAP=%s", sp);
695 			free(sp);
696 			putenv(termcap);
697 		}
698 	}
699 #endif
700 	/*
701 	 * Find out what kind of terminal this is.
702 	 */
703  	if ((term = getenv("TERM")) == NULL)
704  		term = DEFAULT_TERM;
705  	if (tgetent(termbuf, term) <= 0)
706  		strcpy(termbuf, "dumb:hc:");
707 
708  	hard = tgetflag("hc");
709 
710 	/*
711 	 * Get size of the screen.
712 	 */
713 	scrsize();
714 	pos_init();
715 
716 #if DEBUG
717 	if (strncmp(term,"LESSDEBUG",9) == 0)
718 	{
719 		get_debug_term();
720 		return;
721 	}
722 #endif /* DEBUG */
723 
724 	auto_wrap = tgetflag("am");
725 	ignaw = tgetflag("xn");
726 	above_mem = tgetflag("da");
727 	below_mem = tgetflag("db");
728 
729 	/*
730 	 * Assumes termcap variable "sg" is the printing width of:
731 	 * the standout sequence, the end standout sequence,
732 	 * the underline sequence, the end underline sequence,
733 	 * the boldface sequence, and the end boldface sequence.
734 	 */
735 	if ((so_s_width = tgetnum("sg")) < 0)
736 		so_s_width = 0;
737 	so_e_width = so_s_width;
738 
739 	bo_s_width = bo_e_width = so_s_width;
740 	ul_s_width = ul_e_width = so_s_width;
741 	bl_s_width = bl_e_width = so_s_width;
742 
743 #if HILITE_SEARCH
744 	if (so_s_width > 0 || so_e_width > 0)
745 		/*
746 		 * Disable highlighting by default on magic cookie terminals.
747 		 * Turning on highlighting might change the displayed width
748 		 * of a line, causing the display to get messed up.
749 		 * The user can turn it back on with -g,
750 		 * but she won't like the results.
751 		 */
752 		hilite_search = 0;
753 #endif
754 
755 	/*
756 	 * Get various string-valued capabilities.
757 	 */
758 	sp = sbuf;
759 
760 #if HAVE_OSPEED
761 	sc_pad = tgetstr("pc", &sp);
762 	if (sc_pad != NULL)
763 		PC = *sc_pad;
764 #endif
765 
766 	sc_s_keypad = tgetstr("ks", &sp);
767 	if (sc_s_keypad == NULL)
768 		sc_s_keypad = "";
769 	sc_e_keypad = tgetstr("ke", &sp);
770 	if (sc_e_keypad == NULL)
771 		sc_e_keypad = "";
772 
773 	/*
774 	 * This loses for terminals with termcap entries with ti/te strings
775 	 * that switch to/from an alternate screen, and we're in quit_at_eof
776 	 * (eg, more(1)).
777 	 */
778 	if (!quit_at_eof && !more_mode) {
779 		sc_init = tgetstr("ti", &sp);
780 		sc_deinit = tgetstr("te", &sp);
781 	}
782 	if (sc_init == NULL)
783 		sc_init = "";
784 	if (sc_deinit == NULL)
785 		sc_deinit = "";
786 
787 	sc_eol_clear = tgetstr("ce", &sp);
788 	if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
789 	{
790 		cannot("clear to end of line");
791 		sc_eol_clear = "";
792 	}
793 
794 	sc_eos_clear = tgetstr("cd", &sp);
795 	if (below_mem &&
796 		(hard || sc_eos_clear == NULL || *sc_eos_clear == '\0'))
797 	{
798 		cannot("clear to end of screen");
799 		sc_eol_clear = "";
800 	}
801 
802 	sc_clear = tgetstr("cl", &sp);
803 	if (hard || sc_clear == NULL || *sc_clear == '\0')
804 	{
805 		cannot("clear screen");
806 		sc_clear = "\n\n";
807 	}
808 
809 	sc_move = tgetstr("cm", &sp);
810 	if (hard || sc_move == NULL || *sc_move == '\0')
811 	{
812 		/*
813 		 * This is not an error here, because we don't
814 		 * always need sc_move.
815 		 * We need it only if we don't have home or lower-left.
816 		 */
817 		sc_move = "";
818 		can_goto_line = 0;
819 	} else
820 		can_goto_line = 1;
821 
822 	sc_s_in = tgetstr("so", &sp);
823 	if (hard || sc_s_in == NULL)
824 		sc_s_in = "";
825 
826 	sc_s_out = tgetstr("se", &sp);
827 	if (hard || sc_s_out == NULL)
828 		sc_s_out = "";
829 
830 	sc_u_in = tgetstr("us", &sp);
831 	if (hard || sc_u_in == NULL)
832 		sc_u_in = sc_s_in;
833 
834 	sc_u_out = tgetstr("ue", &sp);
835 	if (hard || sc_u_out == NULL)
836 		sc_u_out = sc_s_out;
837 
838 	sc_b_in = tgetstr("md", &sp);
839 	if (hard || sc_b_in == NULL)
840 	{
841 		sc_b_in = sc_s_in;
842 		sc_b_out = sc_s_out;
843 	} else
844 	{
845 		sc_b_out = tgetstr("me", &sp);
846 		if (hard || sc_b_out == NULL)
847 			sc_b_out = "";
848 	}
849 
850 	sc_bl_in = tgetstr("mb", &sp);
851 	if (hard || sc_bl_in == NULL)
852 	{
853 		sc_bl_in = sc_s_in;
854 		sc_bl_out = sc_s_out;
855 	} else
856 	{
857 		sc_bl_out = tgetstr("me", &sp);
858 		if (hard || sc_bl_out == NULL)
859 			sc_bl_out = "";
860 	}
861 
862 	sc_visual_bell = tgetstr("vb", &sp);
863 	if (hard || sc_visual_bell == NULL)
864 		sc_visual_bell = "";
865 
866 	if (tgetflag("bs"))
867 		sc_backspace = "\b";
868 	else
869 	{
870 		sc_backspace = tgetstr("bc", &sp);
871 		if (sc_backspace == NULL || *sc_backspace == '\0')
872 			sc_backspace = "\b";
873 	}
874 
875 	/*
876 	 * Choose between using "ho" and "cm" ("home" and "cursor move")
877 	 * to move the cursor to the upper left corner of the screen.
878 	 */
879 	t1 = tgetstr("ho", &sp);
880 	if (hard || t1 == NULL)
881 		t1 = "";
882 	if (*sc_move == '\0')
883 		t2 = "";
884 	else
885 	{
886 		strcpy(sp, tgoto(sc_move, 0, 0));
887 		t2 = sp;
888 		sp += strlen(sp) + 1;
889 	}
890 	sc_home = cheaper(t1, t2, "home cursor", "|\b^");
891 
892 	/*
893 	 * Choose between using "ll" and "cm"  ("lower left" and "cursor move")
894 	 * to move the cursor to the lower left corner of the screen.
895 	 */
896 	t1 = tgetstr("ll", &sp);
897 	if (hard || t1 == NULL)
898 		t1 = "";
899 	if (*sc_move == '\0')
900 		t2 = "";
901 	else
902 	{
903 		strcpy(sp, tgoto(sc_move, 0, sc_height-1));
904 		t2 = sp;
905 		sp += strlen(sp) + 1;
906 	}
907 	sc_lower_left = cheaper(t1, t2,
908 		"move cursor to lower left of screen", "\r");
909 
910 	/*
911 	 * Choose between using "al" or "sr" ("add line" or "scroll reverse")
912 	 * to add a line at the top of the screen.
913 	 */
914 	t1 = tgetstr("al", &sp);
915 	if (hard || t1 == NULL)
916 		t1 = "";
917 	t2 = tgetstr("sr", &sp);
918 	if (hard || t2 == NULL)
919 		t2 = "";
920 #if OS2
921 	if (*t1 == '\0' && *t2 == '\0')
922 		sc_addline = "";
923 	else
924 #endif
925 	if (above_mem)
926 		sc_addline = t1;
927 	else
928 		sc_addline = cheaper(t1, t2, "scroll backwards", "");
929 	if (*sc_addline == '\0')
930 	{
931 		/*
932 		 * Force repaint on any backward movement.
933 		 */
934 		back_scroll = 0;
935 	}
936 }
937 
938 /*
939  * Return the cost of displaying a termcap string.
940  * We use the trick of calling tputs, but as a char printing function
941  * we give it inc_costcount, which just increments "costcount".
942  * This tells us how many chars would be printed by using this string.
943  * {{ Couldn't we just use strlen? }}
944  */
945 static int costcount;
946 
947 /*ARGSUSED*/
948 	static int
949 inc_costcount(c)
950 	int c;
951 {
952 	costcount++;
953 	return (c);
954 }
955 
956 	static int
957 cost(t)
958 	char *t;
959 {
960 	costcount = 0;
961 	tputs(t, sc_height, inc_costcount);
962 	return (costcount);
963 }
964 
965 /*
966  * Return the "best" of the two given termcap strings.
967  * The best, if both exist, is the one with the lower
968  * cost (see cost() function).
969  */
970 	static char *
971 cheaper(t1, t2, doit, def)
972 	char *t1, *t2;
973 	char *doit;
974 	char *def;
975 {
976 	if (*t1 == '\0' && *t2 == '\0')
977 	{
978 		cannot(doit);
979 		return (def);
980 	}
981 	if (*t1 == '\0')
982 		return (t2);
983 	if (*t2 == '\0')
984 		return (t1);
985 	if (cost(t1) < cost(t2))
986 		return (t1);
987 	return (t2);
988 }
989 
990 
991 /*
992  * Below are the functions which perform all the
993  * terminal-specific screen manipulation.
994  */
995 
996 
997 /*
998  * Initialize terminal
999  */
1000 	public void
1001 init()
1002 {
1003 	if (no_init)
1004 		return;
1005 	tputs(sc_init, sc_height, putchr);
1006 	tputs(sc_s_keypad, sc_height, putchr);
1007 	init_done = 1;
1008 }
1009 
1010 /*
1011  * Deinitialize terminal
1012  */
1013 	public void
1014 deinit()
1015 {
1016 	if (no_init)
1017 		return;
1018 	if (!init_done)
1019 		return;
1020 	tputs(sc_e_keypad, sc_height, putchr);
1021 	tputs(sc_deinit, sc_height, putchr);
1022 	init_done = 0;
1023 }
1024 
1025 /*
1026  * Home cursor (move to upper left corner of screen).
1027  */
1028 	public void
1029 home()
1030 {
1031 	tputs(sc_home, 1, putchr);
1032 }
1033 
1034 /*
1035  * Add a blank line (called with cursor at home).
1036  * Should scroll the display down.
1037  */
1038 	public void
1039 add_line()
1040 {
1041 	tputs(sc_addline, sc_height, putchr);
1042 }
1043 
1044 /*
1045  * Move cursor to lower left corner of screen.
1046  */
1047 	public void
1048 lower_left()
1049 {
1050 	tputs(sc_lower_left, 1, putchr);
1051 }
1052 
1053 /*
1054  * Goto a specific line on the screen.
1055  */
1056 	public void
1057 goto_line(slinenum)
1058 	int slinenum;
1059 {
1060 	char *sc_goto;
1061 
1062 	sc_goto = tgoto(sc_move, 0, slinenum);
1063 	tputs(sc_goto, 1, putchr);
1064 }
1065 
1066 /*
1067  * Ring the terminal bell.
1068  */
1069 	public void
1070 bell()
1071 {
1072 	if (quiet == VERY_QUIET)
1073 		vbell();
1074 	else
1075 		putchr('\7');
1076 }
1077 
1078 /*
1079  * Output the "visual bell", if there is one.
1080  */
1081 	public void
1082 vbell()
1083 {
1084 	if (*sc_visual_bell == '\0')
1085 		return;
1086 	tputs(sc_visual_bell, sc_height, putchr);
1087 }
1088 
1089 /*
1090  * Clear the screen.
1091  */
1092 	public void
1093 clear()
1094 {
1095 	tputs(sc_clear, sc_height, putchr);
1096 }
1097 
1098 /*
1099  * Clear from the cursor to the end of the cursor's line.
1100  * {{ This must not move the cursor. }}
1101  */
1102 	public void
1103 clear_eol()
1104 {
1105 	tputs(sc_eol_clear, 1, putchr);
1106 }
1107 
1108 /*
1109  * Clear the bottom line of the display.
1110  * Leave the cursor at the beginning of the bottom line.
1111  */
1112 	public void
1113 clear_bot()
1114 {
1115 	lower_left();
1116 	if (below_mem)
1117 		tputs(sc_eos_clear, 1, putchr);
1118 	else
1119 		tputs(sc_eol_clear, 1, putchr);
1120 }
1121 
1122 /*
1123  * Begin "standout" (bold, underline, or whatever).
1124  */
1125 	public void
1126 so_enter()
1127 {
1128 	tputs(sc_s_in, 1, putchr);
1129 }
1130 
1131 /*
1132  * End "standout".
1133  */
1134 	public void
1135 so_exit()
1136 {
1137 	tputs(sc_s_out, 1, putchr);
1138 }
1139 
1140 /*
1141  * Begin "underline" (hopefully real underlining,
1142  * otherwise whatever the terminal provides).
1143  */
1144 	public void
1145 ul_enter()
1146 {
1147 	tputs(sc_u_in, 1, putchr);
1148 }
1149 
1150 /*
1151  * End "underline".
1152  */
1153 	public void
1154 ul_exit()
1155 {
1156 	tputs(sc_u_out, 1, putchr);
1157 }
1158 
1159 /*
1160  * Begin "bold"
1161  */
1162 	public void
1163 bo_enter()
1164 {
1165 	tputs(sc_b_in, 1, putchr);
1166 }
1167 
1168 /*
1169  * End "bold".
1170  */
1171 	public void
1172 bo_exit()
1173 {
1174 	tputs(sc_b_out, 1, putchr);
1175 }
1176 
1177 /*
1178  * Begin "blink"
1179  */
1180 	public void
1181 bl_enter()
1182 {
1183 	tputs(sc_bl_in, 1, putchr);
1184 }
1185 
1186 /*
1187  * End "blink".
1188  */
1189 	public void
1190 bl_exit()
1191 {
1192 	tputs(sc_bl_out, 1, putchr);
1193 }
1194 
1195 /*
1196  * Erase the character to the left of the cursor
1197  * and move the cursor left.
1198  */
1199 	public void
1200 backspace()
1201 {
1202 	/*
1203 	 * Try to erase the previous character by overstriking with a space.
1204 	 */
1205 	tputs(sc_backspace, 1, putchr);
1206 	putchr(' ');
1207 	tputs(sc_backspace, 1, putchr);
1208 }
1209 
1210 /*
1211  * Output a plain backspace, without erasing the previous char.
1212  */
1213 	public void
1214 putbs()
1215 {
1216 	tputs(sc_backspace, 1, putchr);
1217 }
1218