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