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