screen.c revision 60812
1/* $FreeBSD: head/contrib/less/screen.c 60812 2000-05-23 05:51:17Z ps $ */
2/*
3 * Copyright (C) 1984-2000  Mark Nudelman
4 *
5 * You may distribute under the terms of either the GNU General Public
6 * License or the Less License, as specified in the README file.
7 *
8 * For more information about less, or for information on how to
9 * contact the author, see the README file.
10 */
11
12
13/*
14 * Routines which deal with the characteristics of the terminal.
15 * Uses termcap to be as terminal-independent as possible.
16 */
17
18#include "less.h"
19#include "cmd.h"
20
21#if MSDOS_COMPILER
22#include "pckeys.h"
23#if MSDOS_COMPILER==MSOFTC
24#include <graph.h>
25#else
26#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
27#include <conio.h>
28#if MSDOS_COMPILER==DJGPPC
29#include <pc.h>
30extern int fd0;
31#endif
32#else
33#if MSDOS_COMPILER==WIN32C
34#include <windows.h>
35#endif
36#endif
37#endif
38#include <time.h>
39
40#else
41
42#if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
43#include <termios.h>
44#if HAVE_SYS_IOCTL_H && !defined(TIOCGWINSZ)
45#include <sys/ioctl.h>
46#endif
47#else
48#if HAVE_TERMIO_H
49#include <termio.h>
50#else
51#if HAVE_SGSTAT_H
52#include <sgstat.h>
53#else
54#include <sgtty.h>
55#endif
56#if HAVE_SYS_IOCTL_H && (defined(TIOCGWINSZ) || defined(TCGETA) || defined(TIOCGETP) || defined(WIOCGETD))
57#include <sys/ioctl.h>
58#endif
59#endif
60#endif
61
62#if HAVE_TERMCAP_H
63#include <termcap.h>
64#endif
65#ifdef _OSK
66#include <signal.h>
67#endif
68#if OS2
69#include <sys/signal.h>
70#endif
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#endif /* MSDOS_COMPILER */
79
80/*
81 * Check for broken termios package that forces you to manually
82 * set the line discipline.
83 */
84#ifdef __ultrix__
85#define MUST_SET_LINE_DISCIPLINE 1
86#else
87#define MUST_SET_LINE_DISCIPLINE 0
88#endif
89
90#if OS2
91#define	DEFAULT_TERM		"ansi"
92#else
93#define	DEFAULT_TERM		"unknown"
94#endif
95
96#if MSDOS_COMPILER==MSOFTC
97static int videopages;
98static long msec_loops;
99static int flash_created = 0;
100#define	SETCOLORS(fg,bg)	{ _settextcolor(fg); _setbkcolor(bg); }
101#endif
102
103#if MSDOS_COMPILER==BORLANDC
104static unsigned short *whitescreen;
105static int flash_created = 0;
106#endif
107#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
108#define _settextposition(y,x)   gotoxy(x,y)
109#define _clearscreen(m)         clrscr()
110#define _outtext(s)             cputs(s)
111#define	SETCOLORS(fg,bg)	{ textcolor(fg); textbackground(bg); }
112extern int sc_height;
113#endif
114
115#if MSDOS_COMPILER==WIN32C
116struct keyRecord
117{
118	int ascii;
119	int scan;
120} currentKey;
121
122static int keyCount = 0;
123static WORD curr_attr;
124static int pending_scancode = 0;
125static WORD *whitescreen;
126
127static HANDLE con_out_save = INVALID_HANDLE_VALUE; /* previous console */
128static HANDLE con_out_ours = INVALID_HANDLE_VALUE; /* our own */
129HANDLE con_out = INVALID_HANDLE_VALUE;             /* current console */
130
131extern int quitting;
132static void win32_init_term();
133static void win32_deinit_term();
134
135#define FG_COLORS       (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY)
136#define BG_COLORS       (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY)
137#define	MAKEATTR(fg,bg)		((WORD)((fg)|((bg)<<4)))
138#define	SETCOLORS(fg,bg)	{ curr_attr = MAKEATTR(fg,bg); \
139				if (SetConsoleTextAttribute(con_out, curr_attr) == 0) \
140				error("SETCOLORS failed"); }
141#endif
142
143#if MSDOS_COMPILER
144public int nm_fg_color;		/* Color of normal text */
145public int nm_bg_color;
146public int bo_fg_color;		/* Color of bold text */
147public int bo_bg_color;
148public int ul_fg_color;		/* Color of underlined text */
149public int ul_bg_color;
150public int so_fg_color;		/* Color of standout text */
151public int so_bg_color;
152public int bl_fg_color;		/* Color of blinking text */
153public int bl_bg_color;
154static int sy_fg_color;		/* Color of system text (before less) */
155static int sy_bg_color;
156
157#else
158
159/*
160 * Strings passed to tputs() to do various terminal functions.
161 */
162static char
163	*sc_pad,		/* Pad string */
164	*sc_home,		/* Cursor home */
165	*sc_addline,		/* Add line, scroll down following lines */
166	*sc_lower_left,		/* Cursor to last line, first column */
167	*sc_move,		/* General cursor positioning */
168	*sc_clear,		/* Clear screen */
169	*sc_eol_clear,		/* Clear to end of line */
170	*sc_eos_clear,		/* Clear to end of screen */
171	*sc_s_in,		/* Enter standout (highlighted) mode */
172	*sc_s_out,		/* Exit standout mode */
173	*sc_u_in,		/* Enter underline mode */
174	*sc_u_out,		/* Exit underline mode */
175	*sc_b_in,		/* Enter bold mode */
176	*sc_b_out,		/* Exit bold mode */
177	*sc_bl_in,		/* Enter blink mode */
178	*sc_bl_out,		/* Exit blink mode */
179	*sc_visual_bell,	/* Visual bell (flash screen) sequence */
180	*sc_backspace,		/* Backspace cursor */
181	*sc_s_keypad,		/* Start keypad mode */
182	*sc_e_keypad,		/* End keypad mode */
183	*sc_init,		/* Startup terminal initialization */
184	*sc_deinit;		/* Exit terminal de-initialization */
185#endif
186
187static int init_done = 0;
188
189public int auto_wrap;		/* Terminal does \r\n when write past margin */
190public int ignaw;		/* Terminal ignores \n immediately after wrap */
191public int erase_char, kill_char; /* The user's erase and line-kill chars */
192public int werase_char;		/* The user's word-erase char */
193public int sc_width, sc_height;	/* Height & width of screen */
194public int bo_s_width, bo_e_width;	/* Printing width of boldface seq */
195public int ul_s_width, ul_e_width;	/* Printing width of underline seq */
196public int so_s_width, so_e_width;	/* Printing width of standout seq */
197public int bl_s_width, bl_e_width;	/* Printing width of blink seq */
198public int above_mem, below_mem;	/* Memory retained above/below screen */
199public int can_goto_line;		/* Can move cursor to any line */
200public int clear_bg;		/* Clear fills with background color */
201public int missing_cap = 0;	/* Some capability is missing */
202
203static int attrmode = AT_NORMAL;
204
205#if !MSDOS_COMPILER
206static char *cheaper();
207static void tmodes();
208#endif
209
210/*
211 * These two variables are sometimes defined in,
212 * and needed by, the termcap library.
213 */
214#if MUST_DEFINE_OSPEED
215extern short ospeed;	/* Terminal output baud rate */
216extern char PC;		/* Pad character */
217#endif
218#ifdef _OSK
219short ospeed;
220char PC_, *UP, *BC;
221#endif
222
223extern int quiet;		/* If VERY_QUIET, use visual bell for bell */
224extern int no_back_scroll;
225extern int swindow;
226extern int no_init;
227extern int quit_at_eof;
228extern int more_mode;
229extern int sigs;
230extern int wscroll;
231extern int screen_trashed;
232#if HILITE_SEARCH
233extern int hilite_search;
234#endif
235
236extern char *tgetstr();
237extern char *tgoto();
238
239
240/*
241 * Change terminal to "raw mode", or restore to "normal" mode.
242 * "Raw mode" means
243 *	1. An outstanding read will complete on receipt of a single keystroke.
244 *	2. Input is not echoed.
245 *	3. On output, \n is mapped to \r\n.
246 *	4. \t is NOT expanded into spaces.
247 *	5. Signal-causing characters such as ctrl-C (interrupt),
248 *	   etc. are NOT disabled.
249 * It doesn't matter whether an input \n is mapped to \r, or vice versa.
250 */
251	public void
252raw_mode(on)
253	int on;
254{
255	static int curr_on = 0;
256
257	if (on == curr_on)
258		return;
259#if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
260    {
261	struct termios s;
262	static struct termios save_term;
263	static int saved_term = 0;
264
265	if (on)
266	{
267		/*
268		 * Get terminal modes.
269		 */
270		tcgetattr(2, &s);
271
272		/*
273		 * Save modes and set certain variables dependent on modes.
274		 */
275		if (!saved_term)
276		{
277			save_term = s;
278			saved_term = 1;
279		}
280#if HAVE_OSPEED
281		switch (cfgetospeed(&s))
282		{
283#ifdef B0
284		case B0: ospeed = 0; break;
285#endif
286#ifdef B50
287		case B50: ospeed = 1; break;
288#endif
289#ifdef B75
290		case B75: ospeed = 2; break;
291#endif
292#ifdef B110
293		case B110: ospeed = 3; break;
294#endif
295#ifdef B134
296		case B134: ospeed = 4; break;
297#endif
298#ifdef B150
299		case B150: ospeed = 5; break;
300#endif
301#ifdef B200
302		case B200: ospeed = 6; break;
303#endif
304#ifdef B300
305		case B300: ospeed = 7; break;
306#endif
307#ifdef B600
308		case B600: ospeed = 8; break;
309#endif
310#ifdef B1200
311		case B1200: ospeed = 9; break;
312#endif
313#ifdef B1800
314		case B1800: ospeed = 10; break;
315#endif
316#ifdef B2400
317		case B2400: ospeed = 11; break;
318#endif
319#ifdef B4800
320		case B4800: ospeed = 12; break;
321#endif
322#ifdef B9600
323		case B9600: ospeed = 13; break;
324#endif
325#ifdef EXTA
326		case EXTA: ospeed = 14; break;
327#endif
328#ifdef EXTB
329		case EXTB: ospeed = 15; break;
330#endif
331#ifdef B57600
332		case B57600: ospeed = 16; break;
333#endif
334#ifdef B115200
335		case B115200: ospeed = 17; break;
336#endif
337		default: ;
338		}
339#endif
340		erase_char = s.c_cc[VERASE];
341		kill_char = s.c_cc[VKILL];
342#ifdef VWERASE
343		werase_char = s.c_cc[VWERASE];
344#else
345		werase_char = CONTROL('W');
346#endif
347
348		/*
349		 * Set the modes to the way we want them.
350		 */
351		s.c_lflag &= ~(0
352#ifdef ICANON
353			| ICANON
354#endif
355#ifdef ECHO
356			| ECHO
357#endif
358#ifdef ECHOE
359			| ECHOE
360#endif
361#ifdef ECHOK
362			| ECHOK
363#endif
364#if ECHONL
365			| ECHONL
366#endif
367		);
368
369		s.c_oflag |= (0
370#ifdef OXTABS
371			| OXTABS
372#else
373#ifdef TAB3
374			| TAB3
375#else
376#ifdef XTABS
377			| XTABS
378#endif
379#endif
380#endif
381#ifdef OPOST
382			| OPOST
383#endif
384#ifdef ONLCR
385			| ONLCR
386#endif
387		);
388
389		s.c_oflag &= ~(0
390#ifdef ONOEOT
391			| ONOEOT
392#endif
393#ifdef OCRNL
394			| OCRNL
395#endif
396#ifdef ONOCR
397			| ONOCR
398#endif
399#ifdef ONLRET
400			| ONLRET
401#endif
402		);
403		s.c_cc[VMIN] = 1;
404		s.c_cc[VTIME] = 0;
405#ifdef VLNEXT
406		s.c_cc[VLNEXT] = 0;
407#endif
408#ifdef VDSUSP
409		s.c_cc[VDSUSP] = 0;
410#endif
411#if MUST_SET_LINE_DISCIPLINE
412		/*
413		 * System's termios is broken; need to explicitly
414		 * request TERMIODISC line discipline.
415		 */
416		s.c_line = TERMIODISC;
417#endif
418	} else
419	{
420		/*
421		 * Restore saved modes.
422		 */
423		s = save_term;
424	}
425	tcsetattr(2, TCSADRAIN, &s);
426#if MUST_SET_LINE_DISCIPLINE
427	if (!on)
428	{
429		/*
430		 * Broken termios *ignores* any line discipline
431		 * except TERMIODISC.  A different old line discipline
432		 * is therefore not restored, yet.  Restore the old
433		 * line discipline by hand.
434		 */
435		ioctl(2, TIOCSETD, &save_term.c_line);
436	}
437#endif
438    }
439#else
440#ifdef TCGETA
441    {
442	struct termio s;
443	static struct termio save_term;
444	static int saved_term = 0;
445
446	if (on)
447	{
448		/*
449		 * Get terminal modes.
450		 */
451		ioctl(2, TCGETA, &s);
452
453		/*
454		 * Save modes and set certain variables dependent on modes.
455		 */
456		if (!saved_term)
457		{
458			save_term = s;
459			saved_term = 1;
460		}
461#if HAVE_OSPEED
462		ospeed = s.c_cflag & CBAUD;
463#endif
464		erase_char = s.c_cc[VERASE];
465		kill_char = s.c_cc[VKILL];
466#ifdef VWERASE
467		werase_char = s.c_cc[VWERASE];
468#else
469		werase_char = CONTROL('W');
470#endif
471
472		/*
473		 * Set the modes to the way we want them.
474		 */
475		s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
476		s.c_oflag |=  (OPOST|ONLCR|TAB3);
477		s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
478		s.c_cc[VMIN] = 1;
479		s.c_cc[VTIME] = 0;
480	} else
481	{
482		/*
483		 * Restore saved modes.
484		 */
485		s = save_term;
486	}
487	ioctl(2, TCSETAW, &s);
488    }
489#else
490#ifdef TIOCGETP
491    {
492	struct sgttyb s;
493	static struct sgttyb save_term;
494	static int saved_term = 0;
495
496	if (on)
497	{
498		/*
499		 * Get terminal modes.
500		 */
501		ioctl(2, TIOCGETP, &s);
502
503		/*
504		 * Save modes and set certain variables dependent on modes.
505		 */
506		if (!saved_term)
507		{
508			save_term = s;
509			saved_term = 1;
510		}
511#if HAVE_OSPEED
512		ospeed = s.sg_ospeed;
513#endif
514		erase_char = s.sg_erase;
515		kill_char = s.sg_kill;
516		werase_char = CONTROL('W');
517
518		/*
519		 * Set the modes to the way we want them.
520		 */
521		s.sg_flags |= CBREAK;
522		s.sg_flags &= ~(ECHO|XTABS);
523	} else
524	{
525		/*
526		 * Restore saved modes.
527		 */
528		s = save_term;
529	}
530	ioctl(2, TIOCSETN, &s);
531    }
532#else
533#ifdef _OSK
534    {
535	struct sgbuf s;
536	static struct sgbuf save_term;
537	static int saved_term = 0;
538
539	if (on)
540	{
541		/*
542		 * Get terminal modes.
543		 */
544		_gs_opt(2, &s);
545
546		/*
547		 * Save modes and set certain variables dependent on modes.
548		 */
549		if (!saved_term)
550		{
551			save_term = s;
552			saved_term = 1;
553		}
554		erase_char = s.sg_bspch;
555		kill_char = s.sg_dlnch;
556		werase_char = CONTROL('W');
557
558		/*
559		 * Set the modes to the way we want them.
560		 */
561		s.sg_echo = 0;
562		s.sg_eofch = 0;
563		s.sg_pause = 0;
564		s.sg_psch = 0;
565	} else
566	{
567		/*
568		 * Restore saved modes.
569		 */
570		s = save_term;
571	}
572	_ss_opt(2, &s);
573    }
574#else
575	/* MS-DOS, Windows, or OS2 */
576#if OS2
577	/* OS2 */
578	LSIGNAL(SIGINT, SIG_IGN);
579#endif
580	erase_char = '\b';
581#if MSDOS_COMPILER==DJGPPC
582	kill_char = CONTROL('U');
583	/*
584	 * So that when we shell out or run another program, its
585	 * stdin is in cooked mode.  We do not switch stdin to binary
586	 * mode if fd0 is zero, since that means we were called before
587	 * tty was reopened in open_getchr, in which case we would be
588	 * changing the original stdin device outside less.
589	 */
590	if (fd0 != 0)
591		setmode(0, on ? O_BINARY : O_TEXT);
592#else
593	kill_char = ESC;
594#endif
595	werase_char = CONTROL('W');
596#endif
597#endif
598#endif
599#endif
600	curr_on = on;
601}
602
603#if !MSDOS_COMPILER
604/*
605 * Some glue to prevent calling termcap functions if tgetent() failed.
606 */
607static int hardcopy;
608
609	static char *
610ltget_env(capname)
611	char *capname;
612{
613	char name[16];
614
615	strcpy(name, "LESS_TERMCAP_");
616	strcat(name, capname);
617	return (lgetenv(name));
618}
619
620	static int
621ltgetflag(capname)
622	char *capname;
623{
624	char *s;
625
626	if ((s = ltget_env(capname)) != NULL)
627		return (*s != '\0' && *s != '0');
628	if (hardcopy)
629		return (0);
630	return (tgetflag(capname));
631}
632
633	static int
634ltgetnum(capname)
635	char *capname;
636{
637	char *s;
638
639	if ((s = ltget_env(capname)) != NULL)
640		return (atoi(s));
641	if (hardcopy)
642		return (-1);
643	return (tgetnum(capname));
644}
645
646	static char *
647ltgetstr(capname, pp)
648	char *capname;
649	char **pp;
650{
651	char *s;
652
653	if ((s = ltget_env(capname)) != NULL)
654		return (s);
655	if (hardcopy)
656		return (NULL);
657	return (tgetstr(capname, pp));
658}
659#endif /* MSDOS_COMPILER */
660
661/*
662 * Get size of the output screen.
663 */
664	public void
665scrsize()
666{
667	register char *s;
668	int sys_height;
669	int sys_width;
670#if !MSDOS_COMPILER
671	int n;
672#endif
673
674#define	DEF_SC_WIDTH	80
675#if MSDOS_COMPILER
676#define	DEF_SC_HEIGHT	25
677#else
678#define	DEF_SC_HEIGHT	24
679#endif
680
681
682	sys_width = sys_height = 0;
683
684#if MSDOS_COMPILER==MSOFTC
685	{
686		struct videoconfig w;
687		_getvideoconfig(&w);
688		sys_height = w.numtextrows;
689		sys_width = w.numtextcols;
690	}
691#else
692#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
693	{
694		struct text_info w;
695		gettextinfo(&w);
696		sys_height = w.screenheight;
697		sys_width = w.screenwidth;
698	}
699#else
700#if MSDOS_COMPILER==WIN32C
701	{
702		CONSOLE_SCREEN_BUFFER_INFO scr;
703		GetConsoleScreenBufferInfo(con_out, &scr);
704		sys_height = scr.srWindow.Bottom - scr.srWindow.Top + 1;
705		sys_width = scr.srWindow.Right - scr.srWindow.Left + 1;
706	}
707#else
708#if OS2
709	{
710		int s[2];
711		_scrsize(s);
712		sys_width = s[0];
713		sys_height = s[1];
714	}
715#else
716#ifdef TIOCGWINSZ
717	{
718		struct winsize w;
719		if (ioctl(2, TIOCGWINSZ, &w) == 0)
720		{
721			if (w.ws_row > 0)
722				sys_height = w.ws_row;
723			if (w.ws_col > 0)
724				sys_width = w.ws_col;
725		}
726	}
727#else
728#ifdef WIOCGETD
729	{
730		struct uwdata w;
731		if (ioctl(2, WIOCGETD, &w) == 0)
732		{
733			if (w.uw_height > 0)
734				sys_height = w.uw_height / w.uw_vs;
735			if (w.uw_width > 0)
736				sys_width = w.uw_width / w.uw_hs;
737		}
738	}
739#endif
740#endif
741#endif
742#endif
743#endif
744#endif
745
746	if (sys_height > 0)
747		sc_height = sys_height;
748	else if ((s = lgetenv("LINES")) != NULL)
749		sc_height = atoi(s);
750#if !MSDOS_COMPILER
751	else if ((n = ltgetnum("li")) > 0)
752 		sc_height = n;
753#endif
754	else
755		sc_height = DEF_SC_HEIGHT;
756
757	if (sys_width > 0)
758		sc_width = sys_width;
759	else if ((s = lgetenv("COLUMNS")) != NULL)
760		sc_width = atoi(s);
761#if !MSDOS_COMPILER
762	else if ((n = ltgetnum("co")) > 0)
763 		sc_width = n;
764#endif
765	else
766		sc_width = DEF_SC_WIDTH;
767}
768
769#if MSDOS_COMPILER==MSOFTC
770/*
771 * Figure out how many empty loops it takes to delay a millisecond.
772 */
773	static void
774get_clock()
775{
776	clock_t start;
777
778	/*
779	 * Get synchronized at the start of a tick.
780	 */
781	start = clock();
782	while (clock() == start)
783		;
784	/*
785	 * Now count loops till the next tick.
786	 */
787	start = clock();
788	msec_loops = 0;
789	while (clock() == start)
790		msec_loops++;
791	/*
792	 * Convert from (loops per clock) to (loops per millisecond).
793	 */
794	msec_loops *= CLOCKS_PER_SEC;
795	msec_loops /= 1000;
796}
797
798/*
799 * Delay for a specified number of milliseconds.
800 */
801	static void
802dummy_func()
803{
804	static long delay_dummy = 0;
805	delay_dummy++;
806}
807
808	static void
809delay(msec)
810	int msec;
811{
812	long i;
813
814	while (msec-- > 0)
815	{
816		for (i = 0;  i < msec_loops;  i++)
817		{
818			/*
819			 * Make it look like we're doing something here,
820			 * so the optimizer doesn't remove the whole loop.
821			 */
822			dummy_func();
823		}
824	}
825}
826#endif
827
828/*
829 * Return the characters actually input by a "special" key.
830 */
831	public char *
832special_key_str(key)
833	int key;
834{
835	static char tbuf[40];
836	char *s;
837#if MSDOS_COMPILER
838	static char k_right[]		= { '\340', PCK_RIGHT, 0 };
839	static char k_left[]		= { '\340', PCK_LEFT, 0  };
840	static char k_ctl_right[]	= { '\340', PCK_CTL_RIGHT, 0  };
841	static char k_ctl_left[]	= { '\340', PCK_CTL_LEFT, 0  };
842	static char k_insert[]		= { '\340', PCK_INSERT, 0  };
843	static char k_delete[]		= { '\340', PCK_DELETE, 0  };
844	static char k_ctl_delete[]	= { '\340', PCK_CTL_DELETE, 0  };
845	static char k_ctl_backspace[]	= { '\177', 0 };
846	static char k_home[]		= { '\340', PCK_HOME, 0 };
847	static char k_end[]		= { '\340', PCK_END, 0 };
848	static char k_up[]		= { '\340', PCK_UP, 0 };
849	static char k_down[]		= { '\340', PCK_DOWN, 0 };
850	static char k_backtab[]		= { '\340', PCK_SHIFT_TAB, 0 };
851	static char k_pagedown[]	= { '\340', PCK_PAGEDOWN, 0 };
852	static char k_pageup[]		= { '\340', PCK_PAGEUP, 0 };
853	static char k_f1[]		= { '\340', PCK_F1, 0 };
854#else
855	char *sp = tbuf;
856#endif
857
858	switch (key)
859	{
860#if MSDOS_COMPILER
861	case SK_RIGHT_ARROW:
862		s = k_right;
863		break;
864	case SK_LEFT_ARROW:
865		s = k_left;
866		break;
867	case SK_UP_ARROW:
868		s = k_up;
869		break;
870	case SK_DOWN_ARROW:
871		s = k_down;
872		break;
873	case SK_PAGE_UP:
874		s = k_pageup;
875		break;
876	case SK_PAGE_DOWN:
877		s = k_pagedown;
878		break;
879	case SK_HOME:
880		s = k_home;
881		break;
882	case SK_END:
883		s = k_end;
884		break;
885	case SK_DELETE:
886		s = k_delete;
887		break;
888	case SK_INSERT:
889		s = k_insert;
890		break;
891	case SK_CTL_LEFT_ARROW:
892		s = k_ctl_left;
893		break;
894	case SK_CTL_RIGHT_ARROW:
895		s = k_ctl_right;
896		break;
897	case SK_CTL_BACKSPACE:
898		s = k_ctl_backspace;
899		break;
900	case SK_CTL_DELETE:
901		s = k_ctl_delete;
902		break;
903	case SK_F1:
904		s = k_f1;
905		break;
906	case SK_BACKTAB:
907		s = k_backtab;
908		break;
909#else
910	case SK_RIGHT_ARROW:
911		s = ltgetstr("kr", &sp);
912		break;
913	case SK_LEFT_ARROW:
914		s = ltgetstr("kl", &sp);
915		break;
916	case SK_UP_ARROW:
917		s = ltgetstr("ku", &sp);
918		break;
919	case SK_DOWN_ARROW:
920		s = ltgetstr("kd", &sp);
921		break;
922	case SK_PAGE_UP:
923		s = ltgetstr("kP", &sp);
924		break;
925	case SK_PAGE_DOWN:
926		s = ltgetstr("kN", &sp);
927		break;
928	case SK_HOME:
929		s = ltgetstr("kh", &sp);
930		break;
931	case SK_END:
932		s = ltgetstr("@7", &sp);
933		break;
934	case SK_DELETE:
935		s = ltgetstr("kD", &sp);
936		if (s == NULL)
937		{
938			tbuf[0] = '\177';
939			tbuf[1] = '\0';
940			s = tbuf;
941		}
942		break;
943#endif
944	case SK_CONTROL_K:
945		tbuf[0] = CONTROL('K');
946		tbuf[1] = '\0';
947		s = tbuf;
948		break;
949	default:
950		return (NULL);
951	}
952	return (s);
953}
954
955/*
956 * Get terminal capabilities via termcap.
957 */
958	public void
959get_term()
960{
961#if MSDOS_COMPILER
962	auto_wrap = 1;
963	ignaw = 0;
964	can_goto_line = 1;
965	clear_bg = 1;
966	/*
967	 * Set up default colors.
968	 * The xx_s_width and xx_e_width vars are already initialized to 0.
969	 */
970#if MSDOS_COMPILER==MSOFTC
971	sy_bg_color = _getbkcolor();
972	sy_fg_color = _gettextcolor();
973	get_clock();
974#else
975#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
976    {
977	struct text_info w;
978	gettextinfo(&w);
979	sy_bg_color = (w.attribute >> 4) & 0x0F;
980	sy_fg_color = (w.attribute >> 0) & 0x0F;
981    }
982#else
983#if MSDOS_COMPILER==WIN32C
984    {
985	DWORD nread;
986	CONSOLE_SCREEN_BUFFER_INFO scr;
987
988	con_out_save = con_out = GetStdHandle(STD_OUTPUT_HANDLE);
989	/*
990	 * Always open stdin in binary. Note this *must* be done
991	 * before any file operations have been done on fd0.
992	 */
993	SET_BINARY(0);
994	GetConsoleScreenBufferInfo(con_out, &scr);
995	ReadConsoleOutputAttribute(con_out, &curr_attr,
996					1, scr.dwCursorPosition, &nread);
997	sy_bg_color = (curr_attr & BG_COLORS) >> 4; /* normalize */
998	sy_fg_color = curr_attr & FG_COLORS;
999    }
1000#endif
1001#endif
1002#endif
1003	nm_fg_color = sy_fg_color;
1004	nm_bg_color = sy_bg_color;
1005	bo_fg_color = 11;
1006	bo_bg_color = 0;
1007	ul_fg_color = 9;
1008	ul_bg_color = 0;
1009	so_fg_color = 15;
1010	so_bg_color = 9;
1011	bl_fg_color = 15;
1012	bl_bg_color = 0;
1013
1014	/*
1015	 * Get size of the screen.
1016	 */
1017	scrsize();
1018	pos_init();
1019
1020
1021#else /* !MSDOS_COMPILER */
1022
1023	char *sp;
1024	register char *t1, *t2;
1025	char *term;
1026	char termbuf[TERMBUF_SIZE];
1027
1028	static char sbuf[TERMSBUF_SIZE];
1029
1030#if OS2
1031	/*
1032	 * Make sure the termcap database is available.
1033	 */
1034	sp = lgetenv("TERMCAP");
1035	if (sp == NULL || *sp == '\0')
1036	{
1037		char *termcap;
1038		if ((sp = homefile("termcap.dat")) != NULL)
1039		{
1040			termcap = (char *) ecalloc(strlen(sp)+9, sizeof(char));
1041			sprintf(termcap, "TERMCAP=%s", sp);
1042			free(sp);
1043			putenv(termcap);
1044		}
1045	}
1046#endif
1047	/*
1048	 * Find out what kind of terminal this is.
1049	 */
1050 	if ((term = lgetenv("TERM")) == NULL)
1051 		term = DEFAULT_TERM;
1052	hardcopy = 0;
1053 	if (tgetent(termbuf, term) <= 0)
1054 		hardcopy = 1;
1055 	if (ltgetflag("hc"))
1056		hardcopy = 1;
1057
1058	/*
1059	 * Get size of the screen.
1060	 */
1061	scrsize();
1062	pos_init();
1063
1064	auto_wrap = ltgetflag("am");
1065	ignaw = ltgetflag("xn");
1066	above_mem = ltgetflag("da");
1067	below_mem = ltgetflag("db");
1068	clear_bg = ltgetflag("ut");
1069
1070	/*
1071	 * Assumes termcap variable "sg" is the printing width of:
1072	 * the standout sequence, the end standout sequence,
1073	 * the underline sequence, the end underline sequence,
1074	 * the boldface sequence, and the end boldface sequence.
1075	 */
1076	if ((so_s_width = ltgetnum("sg")) < 0)
1077		so_s_width = 0;
1078	so_e_width = so_s_width;
1079
1080	bo_s_width = bo_e_width = so_s_width;
1081	ul_s_width = ul_e_width = so_s_width;
1082	bl_s_width = bl_e_width = so_s_width;
1083
1084#if HILITE_SEARCH
1085	if (so_s_width > 0 || so_e_width > 0)
1086		/*
1087		 * Disable highlighting by default on magic cookie terminals.
1088		 * Turning on highlighting might change the displayed width
1089		 * of a line, causing the display to get messed up.
1090		 * The user can turn it back on with -g,
1091		 * but she won't like the results.
1092		 */
1093		hilite_search = 0;
1094#endif
1095
1096	/*
1097	 * Get various string-valued capabilities.
1098	 */
1099	sp = sbuf;
1100
1101#if HAVE_OSPEED
1102	sc_pad = ltgetstr("pc", &sp);
1103	if (sc_pad != NULL)
1104		PC = *sc_pad;
1105#endif
1106
1107	sc_s_keypad = ltgetstr("ks", &sp);
1108	if (sc_s_keypad == NULL)
1109		sc_s_keypad = "";
1110	sc_e_keypad = ltgetstr("ke", &sp);
1111	if (sc_e_keypad == NULL)
1112		sc_e_keypad = "";
1113
1114	/*
1115	 * This loses for terminals with termcap entries with ti/te strings
1116	 * that switch to/from an alternate screen, and we're in quit_at_eof
1117	 * (eg, more(1)).
1118 	 */
1119	if (!quit_at_eof && !more_mode) {
1120		sc_init = ltgetstr("ti", &sp);
1121		sc_deinit = ltgetstr("te", &sp);
1122	}
1123
1124	if (sc_init == NULL)
1125		sc_init = "";
1126
1127	if (sc_deinit == NULL)
1128		sc_deinit = "";
1129
1130	sc_eol_clear = ltgetstr("ce", &sp);
1131	if (sc_eol_clear == NULL || *sc_eol_clear == '\0')
1132	{
1133		missing_cap = 1;
1134		sc_eol_clear = "";
1135	}
1136
1137	sc_eos_clear = ltgetstr("cd", &sp);
1138	if (below_mem && (sc_eos_clear == NULL || *sc_eos_clear == '\0'))
1139	{
1140		missing_cap = 1;
1141		sc_eol_clear = "";
1142	}
1143
1144	sc_clear = ltgetstr("cl", &sp);
1145	if (sc_clear == NULL || *sc_clear == '\0')
1146	{
1147		missing_cap = 1;
1148		sc_clear = "\n\n";
1149	}
1150
1151	sc_move = ltgetstr("cm", &sp);
1152	if (sc_move == NULL || *sc_move == '\0')
1153	{
1154		/*
1155		 * This is not an error here, because we don't
1156		 * always need sc_move.
1157		 * We need it only if we don't have home or lower-left.
1158		 */
1159		sc_move = "";
1160		can_goto_line = 0;
1161	} else
1162		can_goto_line = 1;
1163
1164	tmodes("so", "se", &sc_s_in, &sc_s_out, "", "", &sp);
1165	tmodes("us", "ue", &sc_u_in, &sc_u_out, sc_s_in, sc_s_out, &sp);
1166	tmodes("md", "me", &sc_b_in, &sc_b_out, sc_s_in, sc_s_out, &sp);
1167	tmodes("mb", "me", &sc_bl_in, &sc_bl_out, sc_s_in, sc_s_out, &sp);
1168
1169	sc_visual_bell = ltgetstr("vb", &sp);
1170	if (sc_visual_bell == NULL)
1171		sc_visual_bell = "";
1172
1173	if (ltgetflag("bs"))
1174		sc_backspace = "\b";
1175	else
1176	{
1177		sc_backspace = ltgetstr("bc", &sp);
1178		if (sc_backspace == NULL || *sc_backspace == '\0')
1179			sc_backspace = "\b";
1180	}
1181
1182	/*
1183	 * Choose between using "ho" and "cm" ("home" and "cursor move")
1184	 * to move the cursor to the upper left corner of the screen.
1185	 */
1186	t1 = ltgetstr("ho", &sp);
1187	if (t1 == NULL)
1188		t1 = "";
1189	if (*sc_move == '\0')
1190		t2 = "";
1191	else
1192	{
1193		strcpy(sp, tgoto(sc_move, 0, 0));
1194		t2 = sp;
1195		sp += strlen(sp) + 1;
1196	}
1197	sc_home = cheaper(t1, t2, "|\b^");
1198
1199	/*
1200	 * Choose between using "ll" and "cm"  ("lower left" and "cursor move")
1201	 * to move the cursor to the lower left corner of the screen.
1202	 */
1203	t1 = ltgetstr("ll", &sp);
1204	if (t1 == NULL)
1205		t1 = "";
1206	if (*sc_move == '\0')
1207		t2 = "";
1208	else
1209	{
1210		strcpy(sp, tgoto(sc_move, 0, sc_height-1));
1211		t2 = sp;
1212		sp += strlen(sp) + 1;
1213	}
1214	sc_lower_left = cheaper(t1, t2, "\r");
1215
1216	/*
1217	 * Choose between using "al" or "sr" ("add line" or "scroll reverse")
1218	 * to add a line at the top of the screen.
1219	 */
1220	t1 = ltgetstr("al", &sp);
1221	if (t1 == NULL)
1222		t1 = "";
1223	t2 = ltgetstr("sr", &sp);
1224	if (t2 == NULL)
1225		t2 = "";
1226#if OS2
1227	if (*t1 == '\0' && *t2 == '\0')
1228		sc_addline = "";
1229	else
1230#endif
1231	if (above_mem)
1232		sc_addline = t1;
1233	else
1234		sc_addline = cheaper(t1, t2, "");
1235	if (*sc_addline == '\0')
1236	{
1237		/*
1238		 * Force repaint on any backward movement.
1239		 */
1240		no_back_scroll = 1;
1241	}
1242#endif /* MSDOS_COMPILER */
1243}
1244
1245#if !MSDOS_COMPILER
1246/*
1247 * Return the cost of displaying a termcap string.
1248 * We use the trick of calling tputs, but as a char printing function
1249 * we give it inc_costcount, which just increments "costcount".
1250 * This tells us how many chars would be printed by using this string.
1251 * {{ Couldn't we just use strlen? }}
1252 */
1253static int costcount;
1254
1255/*ARGSUSED*/
1256	static int
1257inc_costcount(c)
1258	int c;
1259{
1260	costcount++;
1261	return (c);
1262}
1263
1264	static int
1265cost(t)
1266	char *t;
1267{
1268	costcount = 0;
1269	tputs(t, sc_height, inc_costcount);
1270	return (costcount);
1271}
1272
1273/*
1274 * Return the "best" of the two given termcap strings.
1275 * The best, if both exist, is the one with the lower
1276 * cost (see cost() function).
1277 */
1278	static char *
1279cheaper(t1, t2, def)
1280	char *t1, *t2;
1281	char *def;
1282{
1283	if (*t1 == '\0' && *t2 == '\0')
1284	{
1285		missing_cap = 1;
1286		return (def);
1287	}
1288	if (*t1 == '\0')
1289		return (t2);
1290	if (*t2 == '\0')
1291		return (t1);
1292	if (cost(t1) < cost(t2))
1293		return (t1);
1294	return (t2);
1295}
1296
1297	static void
1298tmodes(incap, outcap, instr, outstr, def_instr, def_outstr, spp)
1299	char *incap;
1300	char *outcap;
1301	char **instr;
1302	char **outstr;
1303	char *def_instr;
1304	char *def_outstr;
1305	char **spp;
1306{
1307	*instr = ltgetstr(incap, spp);
1308	if (*instr == NULL)
1309	{
1310		/* Use defaults. */
1311		*instr = def_instr;
1312		*outstr = def_outstr;
1313		return;
1314	}
1315
1316	*outstr = ltgetstr(outcap, spp);
1317	if (*outstr == NULL)
1318		/* No specific out capability; use "me". */
1319		*outstr = ltgetstr("me", spp);
1320	if (*outstr == NULL)
1321		/* Don't even have "me"; use a null string. */
1322		*outstr = "";
1323}
1324
1325#endif /* MSDOS_COMPILER */
1326
1327
1328/*
1329 * Below are the functions which perform all the
1330 * terminal-specific screen manipulation.
1331 */
1332
1333
1334#if MSDOS_COMPILER
1335
1336#if MSDOS_COMPILER==WIN32C
1337	static void
1338_settextposition(int row, int col)
1339{
1340	COORD cpos;
1341	CONSOLE_SCREEN_BUFFER_INFO csbi;
1342
1343	GetConsoleScreenBufferInfo(con_out, &csbi);
1344	cpos.X = csbi.srWindow.Left + (col - 1);
1345	cpos.Y = csbi.srWindow.Top + (row - 1);
1346	SetConsoleCursorPosition(con_out, cpos);
1347}
1348#endif
1349
1350/*
1351 * Initialize the screen to the correct color at startup.
1352 */
1353	static void
1354initcolor()
1355{
1356	SETCOLORS(nm_fg_color, nm_bg_color);
1357#if 0
1358	/*
1359	 * This clears the screen at startup.  This is different from
1360	 * the behavior of other versions of less.  Disable it for now.
1361	 */
1362	char *blanks;
1363	int row;
1364	int col;
1365
1366	/*
1367	 * Create a complete, blank screen using "normal" colors.
1368	 */
1369	SETCOLORS(nm_fg_color, nm_bg_color);
1370	blanks = (char *) ecalloc(width+1, sizeof(char));
1371	for (col = 0;  col < sc_width;  col++)
1372		blanks[col] = ' ';
1373	blanks[sc_width] = '\0';
1374	for (row = 0;  row < sc_height;  row++)
1375		_outtext(blanks);
1376	free(blanks);
1377#endif
1378}
1379#endif
1380
1381#if MSDOS_COMPILER==WIN32C
1382
1383/*
1384 * Termcap-like init with a private win32 console.
1385 */
1386	static void
1387win32_init_term()
1388{
1389	CONSOLE_SCREEN_BUFFER_INFO scr;
1390	COORD size;
1391
1392	if (con_out_save == INVALID_HANDLE_VALUE)
1393		return;
1394
1395	GetConsoleScreenBufferInfo(con_out_save, &scr);
1396
1397	if (con_out_ours == INVALID_HANDLE_VALUE)
1398	{
1399		/*
1400		 * Create our own screen buffer, so that we
1401		 * may restore the original when done.
1402		 */
1403		con_out_ours = CreateConsoleScreenBuffer(
1404			GENERIC_WRITE | GENERIC_READ,
1405			FILE_SHARE_WRITE | FILE_SHARE_READ,
1406			(LPSECURITY_ATTRIBUTES) NULL,
1407			CONSOLE_TEXTMODE_BUFFER,
1408			(LPVOID) NULL);
1409	}
1410
1411	size.X = scr.srWindow.Right - scr.srWindow.Left + 1;
1412	size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1;
1413	SetConsoleScreenBufferSize(con_out_ours, size);
1414	SetConsoleActiveScreenBuffer(con_out_ours);
1415	con_out = con_out_ours;
1416}
1417
1418/*
1419 * Restore the startup console.
1420 */
1421static void
1422win32_deinit_term()
1423{
1424	if (con_out_save == INVALID_HANDLE_VALUE)
1425		return;
1426	if (quitting)
1427		(void) CloseHandle(con_out_ours);
1428	SetConsoleActiveScreenBuffer(con_out_save);
1429	con_out = con_out_save;
1430}
1431
1432#endif
1433
1434/*
1435 * Initialize terminal
1436 */
1437	public void
1438init()
1439{
1440	if (no_init)
1441	{
1442#if MSDOS_COMPILER==WIN32C
1443		/* no_init or not, never trash win32 console colors. */
1444		initcolor();
1445		flush();
1446#endif
1447		return;
1448	}
1449#if !MSDOS_COMPILER
1450	tputs(sc_init, sc_height, putchr);
1451	tputs(sc_s_keypad, sc_height, putchr);
1452#else
1453#if MSDOS_COMPILER==WIN32C
1454	win32_init_term();
1455#endif
1456	initcolor();
1457	flush();
1458#endif
1459	init_done = 1;
1460}
1461
1462/*
1463 * Deinitialize terminal
1464 */
1465	public void
1466deinit()
1467{
1468	if (no_init)
1469	{
1470#if MSDOS_COMPILER==WIN32C
1471		/* no_init or not, never trash win32 console colors. */
1472		SETCOLORS(sy_fg_color, sy_bg_color);
1473#endif
1474		return;
1475	}
1476
1477	if (!init_done)
1478		return;
1479#if !MSDOS_COMPILER
1480	tputs(sc_e_keypad, sc_height, putchr);
1481	tputs(sc_deinit, sc_height, putchr);
1482#else
1483	SETCOLORS(sy_fg_color, sy_bg_color);
1484#if MSDOS_COMPILER==WIN32C
1485	win32_deinit_term();
1486#endif
1487#endif
1488	init_done = 0;
1489}
1490
1491/*
1492 * Home cursor (move to upper left corner of screen).
1493 */
1494	public void
1495home()
1496{
1497#if !MSDOS_COMPILER
1498	tputs(sc_home, 1, putchr);
1499#else
1500	flush();
1501	_settextposition(1,1);
1502#endif
1503}
1504
1505/*
1506 * Add a blank line (called with cursor at home).
1507 * Should scroll the display down.
1508 */
1509	public void
1510add_line()
1511{
1512#if !MSDOS_COMPILER
1513	tputs(sc_addline, sc_height, putchr);
1514#else
1515	flush();
1516#if MSDOS_COMPILER==MSOFTC
1517	_scrolltextwindow(_GSCROLLDOWN);
1518	_settextposition(1,1);
1519#else
1520#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
1521	movetext(1,1, sc_width,sc_height-1, 1,2);
1522	gotoxy(1,1);
1523	clreol();
1524#else
1525#if MSDOS_COMPILER==WIN32C
1526    {
1527	CHAR_INFO fillchar;
1528	SMALL_RECT rcSrc, rcClip;
1529	COORD new_org;
1530	CONSOLE_SCREEN_BUFFER_INFO csbi;
1531
1532	GetConsoleScreenBufferInfo(con_out,&csbi);
1533
1534	/* The clip rectangle is the entire visible screen. */
1535	rcClip.Left = csbi.srWindow.Left;
1536	rcClip.Top = csbi.srWindow.Top;
1537	rcClip.Right = csbi.srWindow.Right;
1538	rcClip.Bottom = csbi.srWindow.Bottom;
1539
1540	/* The source rectangle is the visible screen minus the last line. */
1541	rcSrc = rcClip;
1542	rcSrc.Bottom--;
1543
1544	/* Move the top left corner of the source window down one row. */
1545	new_org.X = rcSrc.Left;
1546	new_org.Y = rcSrc.Top + 1;
1547
1548	/* Fill the right character and attributes. */
1549	fillchar.Char.AsciiChar = ' ';
1550	curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
1551	fillchar.Attributes = curr_attr;
1552	ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar);
1553	_settextposition(1,1);
1554    }
1555#endif
1556#endif
1557#endif
1558#endif
1559}
1560
1561/*
1562 * Remove the n topmost lines and scroll everything below it in the
1563 * window upward.  This is needed to stop leaking the topmost line
1564 * into the scrollback buffer when we go down-one-line (in WIN32).
1565 */
1566	public void
1567remove_top(n)
1568	int n;
1569{
1570#if MSDOS_COMPILER==WIN32C
1571	SMALL_RECT rcSrc, rcClip;
1572	CHAR_INFO fillchar;
1573	COORD new_org;
1574	CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */
1575
1576	if (n >= sc_height - 1)
1577	{
1578		clear();
1579		home();
1580		return;
1581	}
1582
1583	flush();
1584
1585	GetConsoleScreenBufferInfo(con_out, &csbi);
1586
1587	/* Get the extent of all-visible-rows-but-the-last. */
1588	rcSrc.Left    = csbi.srWindow.Left;
1589	rcSrc.Top     = csbi.srWindow.Top + n;
1590	rcSrc.Right   = csbi.srWindow.Right;
1591	rcSrc.Bottom  = csbi.srWindow.Bottom;
1592
1593	/* Get the clip rectangle. */
1594	rcClip.Left   = rcSrc.Left;
1595	rcClip.Top    = csbi.srWindow.Top;
1596	rcClip.Right  = rcSrc.Right;
1597	rcClip.Bottom = rcSrc.Bottom ;
1598
1599	/* Move the source window up n rows. */
1600	new_org.X = rcSrc.Left;
1601	new_org.Y = rcSrc.Top - n;
1602
1603	/* Fill the right character and attributes. */
1604	fillchar.Char.AsciiChar = ' ';
1605	curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
1606	fillchar.Attributes = curr_attr;
1607
1608	ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar);
1609
1610	/* Position cursor on first blank line. */
1611	goto_line(sc_height - n - 1);
1612#endif
1613}
1614
1615/*
1616 * Move cursor to lower left corner of screen.
1617 */
1618	public void
1619lower_left()
1620{
1621#if !MSDOS_COMPILER
1622	tputs(sc_lower_left, 1, putchr);
1623#else
1624	flush();
1625	_settextposition(sc_height, 1);
1626#endif
1627}
1628
1629/*
1630 * Check if the console size has changed and reset internals
1631 * (in lieu of SIGWINCH for WIN32).
1632 */
1633	public void
1634check_winch()
1635{
1636#if MSDOS_COMPILER==WIN32C
1637	CONSOLE_SCREEN_BUFFER_INFO scr;
1638	COORD size;
1639
1640	if (con_out == INVALID_HANDLE_VALUE)
1641		return;
1642
1643	flush();
1644	GetConsoleScreenBufferInfo(con_out, &scr);
1645	size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1;
1646	size.X = scr.srWindow.Right - scr.srWindow.Left + 1;
1647	if (size.Y != sc_height || size.X != sc_width)
1648	{
1649		sc_height = size.Y;
1650		sc_width = size.X;
1651		if (!no_init && con_out_ours == con_out)
1652			SetConsoleScreenBufferSize(con_out, size);
1653		pos_init();
1654		wscroll = (sc_height + 1) / 2;
1655		screen_trashed = 1;
1656	}
1657#endif
1658}
1659
1660/*
1661 * Goto a specific line on the screen.
1662 */
1663	public void
1664goto_line(slinenum)
1665	int slinenum;
1666{
1667#if !MSDOS_COMPILER
1668	tputs(tgoto(sc_move, 0, slinenum), 1, putchr);
1669#else
1670	flush();
1671	_settextposition(slinenum+1, 1);
1672#endif
1673}
1674
1675#if MSDOS_COMPILER==MSOFTC || MSDOS_COMPILER==BORLANDC
1676/*
1677 * Create an alternate screen which is all white.
1678 * This screen is used to create a "flash" effect, by displaying it
1679 * briefly and then switching back to the normal screen.
1680 * {{ Yuck!  There must be a better way to get a visual bell. }}
1681 */
1682	static void
1683create_flash()
1684{
1685#if MSDOS_COMPILER==MSOFTC
1686	struct videoconfig w;
1687	char *blanks;
1688	int row, col;
1689
1690	_getvideoconfig(&w);
1691	videopages = w.numvideopages;
1692	if (videopages < 2)
1693	{
1694		so_enter();
1695		so_exit();
1696	} else
1697	{
1698		_setactivepage(1);
1699		so_enter();
1700		blanks = (char *) ecalloc(w.numtextcols, sizeof(char));
1701		for (col = 0;  col < w.numtextcols;  col++)
1702			blanks[col] = ' ';
1703		for (row = w.numtextrows;  row > 0;  row--)
1704			_outmem(blanks, w.numtextcols);
1705		_setactivepage(0);
1706		_setvisualpage(0);
1707		free(blanks);
1708		so_exit();
1709	}
1710#else
1711#if MSDOS_COMPILER==BORLANDC
1712	register int n;
1713
1714	whitescreen = (unsigned short *)
1715		malloc(sc_width * sc_height * sizeof(short));
1716	if (whitescreen == NULL)
1717		return;
1718	for (n = 0;  n < sc_width * sc_height;  n++)
1719		whitescreen[n] = 0x7020;
1720#else
1721#if MSDOS_COMPILER==WIN32C
1722	register int n;
1723
1724	whitescreen = (WORD *)
1725		malloc(sc_height * sc_width * sizeof(WORD));
1726	if (whitescreen == NULL)
1727		return;
1728	/* Invert the standard colors. */
1729	for (n = 0;  n < sc_width * sc_height;  n++)
1730		whitescreen[n] = (WORD)((nm_fg_color << 4) | nm_bg_color);
1731#endif
1732#endif
1733#endif
1734	flash_created = 1;
1735}
1736#endif /* MSDOS_COMPILER */
1737
1738/*
1739 * Output the "visual bell", if there is one.
1740 */
1741	public void
1742vbell()
1743{
1744#if !MSDOS_COMPILER
1745	if (*sc_visual_bell == '\0')
1746		return;
1747	tputs(sc_visual_bell, sc_height, putchr);
1748#else
1749#if MSDOS_COMPILER==DJGPPC
1750	ScreenVisualBell();
1751#else
1752#if MSDOS_COMPILER==MSOFTC
1753	/*
1754	 * Create a flash screen on the second video page.
1755	 * Switch to that page, then switch back.
1756	 */
1757	if (!flash_created)
1758		create_flash();
1759	if (videopages < 2)
1760		return;
1761	_setvisualpage(1);
1762	delay(100);
1763	_setvisualpage(0);
1764#else
1765#if MSDOS_COMPILER==BORLANDC
1766	unsigned short *currscreen;
1767
1768	/*
1769	 * Get a copy of the current screen.
1770	 * Display the flash screen.
1771	 * Then restore the old screen.
1772	 */
1773	if (!flash_created)
1774		create_flash();
1775	if (whitescreen == NULL)
1776		return;
1777	currscreen = (unsigned short *)
1778		malloc(sc_width * sc_height * sizeof(short));
1779	if (currscreen == NULL) return;
1780	gettext(1, 1, sc_width, sc_height, currscreen);
1781	puttext(1, 1, sc_width, sc_height, whitescreen);
1782	delay(100);
1783	puttext(1, 1, sc_width, sc_height, currscreen);
1784	free(currscreen);
1785#else
1786#if MSDOS_COMPILER==WIN32C
1787	/* paint screen with an inverse color */
1788	clear();
1789
1790	/* leave it displayed for 100 msec. */
1791	Sleep(100);
1792
1793	/* restore with a redraw */
1794	repaint();
1795#endif
1796#endif
1797#endif
1798#endif
1799#endif
1800}
1801
1802/*
1803 * Make a noise.
1804 */
1805	static void
1806beep()
1807{
1808#if !MSDOS_COMPILER
1809	putchr('\7');
1810#else
1811#if MSDOS_COMPILER==WIN32C
1812	MessageBeep(0);
1813#else
1814	write(1, "\7", 1);
1815#endif
1816#endif
1817}
1818
1819/*
1820 * Ring the terminal bell.
1821 */
1822	public void
1823bell()
1824{
1825	if (quiet == VERY_QUIET)
1826		vbell();
1827	else
1828		beep();
1829}
1830
1831/*
1832 * Clear the screen.
1833 */
1834	public void
1835clear()
1836{
1837#if !MSDOS_COMPILER
1838	tputs(sc_clear, sc_height, putchr);
1839#else
1840	flush();
1841#if MSDOS_COMPILER==WIN32C
1842	/*
1843	 * This will clear only the currently visible rows of the NT
1844	 * console buffer, which means none of the precious scrollback
1845	 * rows are touched making for faster scrolling.  Note that, if
1846	 * the window has fewer columns than the console buffer (i.e.
1847	 * there is a horizontal scrollbar as well), the entire width
1848	 * of the visible rows will be cleared.
1849	 */
1850    {
1851	COORD topleft;
1852	DWORD nchars;
1853	DWORD winsz;
1854	CONSOLE_SCREEN_BUFFER_INFO csbi;
1855
1856	/* get the number of cells in the current buffer */
1857	GetConsoleScreenBufferInfo(con_out, &csbi);
1858	winsz = csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1);
1859	topleft.X = 0;
1860	topleft.Y = csbi.srWindow.Top;
1861
1862	curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
1863	FillConsoleOutputCharacter(con_out, ' ', winsz, topleft, &nchars);
1864	FillConsoleOutputAttribute(con_out, curr_attr, winsz, topleft, &nchars);
1865    }
1866#else
1867	_clearscreen(_GCLEARSCREEN);
1868#endif
1869#endif
1870}
1871
1872/*
1873 * Clear from the cursor to the end of the cursor's line.
1874 * {{ This must not move the cursor. }}
1875 */
1876	public void
1877clear_eol()
1878{
1879#if !MSDOS_COMPILER
1880	tputs(sc_eol_clear, 1, putchr);
1881#else
1882#if MSDOS_COMPILER==MSOFTC
1883	short top, left;
1884	short bot, right;
1885	struct rccoord tpos;
1886
1887	flush();
1888	/*
1889	 * Save current state.
1890	 */
1891	tpos = _gettextposition();
1892	_gettextwindow(&top, &left, &bot, &right);
1893	/*
1894	 * Set a temporary window to the current line,
1895	 * from the cursor's position to the right edge of the screen.
1896	 * Then clear that window.
1897	 */
1898	_settextwindow(tpos.row, tpos.col, tpos.row, sc_width);
1899	_clearscreen(_GWINDOW);
1900	/*
1901	 * Restore state.
1902	 */
1903	_settextwindow(top, left, bot, right);
1904	_settextposition(tpos.row, tpos.col);
1905#else
1906#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
1907	flush();
1908	clreol();
1909#else
1910#if MSDOS_COMPILER==WIN32C
1911	DWORD           nchars;
1912	COORD           cpos;
1913	CONSOLE_SCREEN_BUFFER_INFO scr;
1914
1915	flush();
1916	memset(&scr, 0, sizeof(scr));
1917	GetConsoleScreenBufferInfo(con_out, &scr);
1918	cpos.X = scr.dwCursorPosition.X;
1919	cpos.Y = scr.dwCursorPosition.Y;
1920	curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
1921	FillConsoleOutputAttribute(con_out, curr_attr,
1922		scr.dwSize.X - cpos.X, cpos, &nchars);
1923	FillConsoleOutputCharacter(con_out, ' ',
1924		scr.dwSize.X - cpos.X, cpos, &nchars);
1925#endif
1926#endif
1927#endif
1928#endif
1929}
1930
1931/*
1932 * Clear the current line.
1933 * Clear the screen if there's off-screen memory below the display.
1934 */
1935	static void
1936clear_eol_bot()
1937{
1938#if MSDOS_COMPILER
1939	clear_eol();
1940#else
1941	if (below_mem)
1942		tputs(sc_eos_clear, 1, putchr);
1943	else
1944		tputs(sc_eol_clear, 1, putchr);
1945#endif
1946}
1947
1948/*
1949 * Clear the bottom line of the display.
1950 * Leave the cursor at the beginning of the bottom line.
1951 */
1952	public void
1953clear_bot()
1954{
1955	/*
1956	 * If we're in a non-normal attribute mode, temporarily exit
1957	 * the mode while we do the clear.  Some terminals fill the
1958	 * cleared area with the current attribute.
1959	 */
1960	lower_left();
1961	switch (attrmode)
1962	{
1963	case AT_STANDOUT:
1964		so_exit();
1965		clear_eol_bot();
1966		so_enter();
1967		break;
1968	case AT_UNDERLINE:
1969		ul_exit();
1970		clear_eol_bot();
1971		ul_enter();
1972		break;
1973	case AT_BOLD:
1974		bo_exit();
1975		clear_eol_bot();
1976		bo_enter();
1977		break;
1978	case AT_BLINK:
1979		bl_exit();
1980		clear_eol_bot();
1981		bl_enter();
1982		break;
1983	default:
1984		clear_eol_bot();
1985		break;
1986	}
1987}
1988
1989/*
1990 * Begin "standout" (bold, underline, or whatever).
1991 */
1992	public void
1993so_enter()
1994{
1995#if !MSDOS_COMPILER
1996	tputs(sc_s_in, 1, putchr);
1997#else
1998	flush();
1999	SETCOLORS(so_fg_color, so_bg_color);
2000#endif
2001	attrmode = AT_STANDOUT;
2002}
2003
2004/*
2005 * End "standout".
2006 */
2007	public void
2008so_exit()
2009{
2010#if !MSDOS_COMPILER
2011	tputs(sc_s_out, 1, putchr);
2012#else
2013	flush();
2014	SETCOLORS(nm_fg_color, nm_bg_color);
2015#endif
2016	attrmode = AT_NORMAL;
2017}
2018
2019/*
2020 * Begin "underline" (hopefully real underlining,
2021 * otherwise whatever the terminal provides).
2022 */
2023	public void
2024ul_enter()
2025{
2026#if !MSDOS_COMPILER
2027	tputs(sc_u_in, 1, putchr);
2028#else
2029	flush();
2030	SETCOLORS(ul_fg_color, ul_bg_color);
2031#endif
2032	attrmode = AT_UNDERLINE;
2033}
2034
2035/*
2036 * End "underline".
2037 */
2038	public void
2039ul_exit()
2040{
2041#if !MSDOS_COMPILER
2042	tputs(sc_u_out, 1, putchr);
2043#else
2044	flush();
2045	SETCOLORS(nm_fg_color, nm_bg_color);
2046#endif
2047	attrmode = AT_NORMAL;
2048}
2049
2050/*
2051 * Begin "bold"
2052 */
2053	public void
2054bo_enter()
2055{
2056#if !MSDOS_COMPILER
2057	tputs(sc_b_in, 1, putchr);
2058#else
2059	flush();
2060	SETCOLORS(bo_fg_color, bo_bg_color);
2061#endif
2062	attrmode = AT_BOLD;
2063}
2064
2065/*
2066 * End "bold".
2067 */
2068	public void
2069bo_exit()
2070{
2071#if !MSDOS_COMPILER
2072	tputs(sc_b_out, 1, putchr);
2073#else
2074	flush();
2075	SETCOLORS(nm_fg_color, nm_bg_color);
2076#endif
2077	attrmode = AT_NORMAL;
2078}
2079
2080/*
2081 * Begin "blink"
2082 */
2083	public void
2084bl_enter()
2085{
2086#if !MSDOS_COMPILER
2087	tputs(sc_bl_in, 1, putchr);
2088#else
2089	flush();
2090	SETCOLORS(bl_fg_color, bl_bg_color);
2091#endif
2092	attrmode = AT_BLINK;
2093}
2094
2095/*
2096 * End "blink".
2097 */
2098	public void
2099bl_exit()
2100{
2101#if !MSDOS_COMPILER
2102	tputs(sc_bl_out, 1, putchr);
2103#else
2104	flush();
2105	SETCOLORS(nm_fg_color, nm_bg_color);
2106#endif
2107	attrmode = AT_NORMAL;
2108}
2109
2110#if 0 /* No longer used */
2111/*
2112 * Erase the character to the left of the cursor
2113 * and move the cursor left.
2114 */
2115	public void
2116backspace()
2117{
2118#if !MSDOS_COMPILER
2119	/*
2120	 * Erase the previous character by overstriking with a space.
2121	 */
2122	tputs(sc_backspace, 1, putchr);
2123	putchr(' ');
2124	tputs(sc_backspace, 1, putchr);
2125#else
2126#if MSDOS_COMPILER==MSOFTC
2127	struct rccoord tpos;
2128
2129	flush();
2130	tpos = _gettextposition();
2131	if (tpos.col <= 1)
2132		return;
2133	_settextposition(tpos.row, tpos.col-1);
2134	_outtext(" ");
2135	_settextposition(tpos.row, tpos.col-1);
2136#else
2137#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
2138	cputs("\b");
2139#else
2140#if MSDOS_COMPILER==WIN32C
2141	COORD cpos;
2142	DWORD cChars;
2143	CONSOLE_SCREEN_BUFFER_INFO scr;
2144
2145	flush();
2146	GetConsoleScreenBufferInfo(con_out, &scr);
2147	cpos = scr.dwCursorPosition;
2148	if (cpos.X <= 0)
2149		return;
2150	cpos.X--;
2151	SetConsoleCursorPosition(con_out, cpos);
2152	FillConsoleOutputCharacter(con_out, (TCHAR)' ', 1, cpos, &cChars);
2153	SetConsoleCursorPosition(con_out, cpos);
2154#endif
2155#endif
2156#endif
2157#endif
2158}
2159#endif /* 0 */
2160
2161/*
2162 * Output a plain backspace, without erasing the previous char.
2163 */
2164	public void
2165putbs()
2166{
2167#if !MSDOS_COMPILER
2168	tputs(sc_backspace, 1, putchr);
2169#else
2170	int row, col;
2171
2172	flush();
2173	{
2174#if MSDOS_COMPILER==MSOFTC
2175		struct rccoord tpos;
2176		tpos = _gettextposition();
2177		row = tpos.row;
2178		col = tpos.col;
2179#else
2180#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
2181		row = wherey();
2182		col = wherex();
2183#else
2184#if MSDOS_COMPILER==WIN32C
2185		CONSOLE_SCREEN_BUFFER_INFO scr;
2186		GetConsoleScreenBufferInfo(con_out, &scr);
2187		row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1;
2188		col = scr.dwCursorPosition.X - scr.srWindow.Left + 1;
2189#endif
2190#endif
2191#endif
2192	}
2193	if (col <= 1)
2194		return;
2195	_settextposition(row, col-1);
2196#endif /* MSDOS_COMPILER */
2197}
2198
2199#if MSDOS_COMPILER==WIN32C
2200/*
2201 * Determine whether an input character is waiting to be read.
2202 */
2203	static int
2204win32_kbhit(tty)
2205	HANDLE tty;
2206{
2207	INPUT_RECORD ip;
2208	DWORD read;
2209
2210	if (keyCount > 0)
2211		return (TRUE);
2212
2213	currentKey.ascii = 0;
2214	currentKey.scan = 0;
2215
2216	/*
2217	 * Wait for a real key-down event, but
2218	 * ignore SHIFT and CONTROL key events.
2219	 */
2220	do
2221	{
2222		PeekConsoleInput(tty, &ip, 1, &read);
2223		if (read == 0)
2224			return (FALSE);
2225		ReadConsoleInput(tty, &ip, 1, &read);
2226	} while (ip.EventType != KEY_EVENT ||
2227		ip.Event.KeyEvent.bKeyDown != TRUE ||
2228		ip.Event.KeyEvent.wVirtualScanCode == 0 ||
2229		ip.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT ||
2230		ip.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL ||
2231		ip.Event.KeyEvent.wVirtualKeyCode == VK_MENU);
2232
2233	currentKey.ascii = ip.Event.KeyEvent.uChar.AsciiChar;
2234	currentKey.scan = ip.Event.KeyEvent.wVirtualScanCode;
2235	keyCount = ip.Event.KeyEvent.wRepeatCount;
2236
2237	if (ip.Event.KeyEvent.dwControlKeyState &
2238		(LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
2239	{
2240		switch (currentKey.scan)
2241		{
2242		case PCK_ALT_E:     /* letter 'E' */
2243			currentKey.ascii = 0;
2244			break;
2245		}
2246	} else if (ip.Event.KeyEvent.dwControlKeyState &
2247		(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
2248	{
2249		switch (currentKey.scan)
2250		{
2251		case PCK_RIGHT: /* right arrow */
2252			currentKey.scan = PCK_CTL_RIGHT;
2253			break;
2254		case PCK_LEFT: /* left arrow */
2255			currentKey.scan = PCK_CTL_LEFT;
2256			break;
2257		case PCK_DELETE: /* delete */
2258			currentKey.scan = PCK_CTL_DELETE;
2259			break;
2260		}
2261	}
2262	return (TRUE);
2263}
2264
2265/*
2266 * Read a character from the keyboard.
2267 */
2268	public char
2269WIN32getch(tty)
2270	int tty;
2271{
2272	int ascii;
2273
2274	if (pending_scancode)
2275	{
2276		pending_scancode = 0;
2277		return ((char)(currentKey.scan & 0x00FF));
2278	}
2279
2280	while (win32_kbhit((HANDLE)tty) == FALSE)
2281	{
2282		Sleep(20);
2283		if (ABORT_SIGS())
2284			return ('\003');
2285		continue;
2286	}
2287	keyCount --;
2288	ascii = currentKey.ascii;
2289	/*
2290	 * On PC's, the extended keys return a 2 byte sequence beginning
2291	 * with '00', so if the ascii code is 00, the next byte will be
2292	 * the lsb of the scan code.
2293	 */
2294	pending_scancode = (ascii == 0x00);
2295	return ((char)ascii);
2296}
2297#endif
2298