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