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