screen.c revision 221715
1138593Ssam/* $FreeBSD: head/contrib/less/screen.c 221715 2011-05-09 21:51:59Z delphij $ */
2138593Ssam/*
3138593Ssam * Copyright (C) 1984-2011  Mark Nudelman
4138593Ssam *
5138593Ssam * You may distribute under the terms of either the GNU General Public
6138593Ssam * License or the Less License, as specified in the README file.
7138593Ssam *
8138593Ssam * For more information about less, or for information on how to
9138593Ssam * contact the author, see the README file.
10138593Ssam */
11138593Ssam
12138593Ssam
13138593Ssam/*
14138593Ssam * Routines which deal with the characteristics of the terminal.
15138593Ssam * Uses termcap to be as terminal-independent as possible.
16138593Ssam */
17138593Ssam
18138593Ssam#include "less.h"
19138593Ssam#include "cmd.h"
20138593Ssam
21138593Ssam#if MSDOS_COMPILER
22138593Ssam#include "pckeys.h"
23138593Ssam#if MSDOS_COMPILER==MSOFTC
24138593Ssam#include <graph.h>
25138593Ssam#else
26138593Ssam#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
27138593Ssam#include <conio.h>
28138593Ssam#if MSDOS_COMPILER==DJGPPC
29138593Ssam#include <pc.h>
30138593Ssamextern int fd0;
31138593Ssam#endif
32138593Ssam#else
33138593Ssam#if MSDOS_COMPILER==WIN32C
34138593Ssam#include <windows.h>
35138593Ssam#endif
36138593Ssam#endif
37138593Ssam#endif
38138593Ssam#include <time.h>
39138593Ssam
40138593Ssam#else
41138593Ssam
42138593Ssam#if HAVE_SYS_IOCTL_H
43138593Ssam#include <sys/ioctl.h>
44166956Ssam#endif
45138593Ssam
46138593Ssam#if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
47138593Ssam#include <termios.h>
48138593Ssam#else
49138593Ssam#if HAVE_TERMIO_H
50138593Ssam#include <termio.h>
51138593Ssam#else
52138593Ssam#if HAVE_SGSTAT_H
53138593Ssam#include <sgstat.h>
54138593Ssam#else
55138593Ssam#include <sgtty.h>
56138593Ssam#endif
57166956Ssam#endif
58138593Ssam#endif
59138593Ssam
60138593Ssam#if HAVE_TERMCAP_H
61166956Ssam#include <termcap.h>
62138593Ssam#endif
63138593Ssam#ifdef _OSK
64138593Ssam#include <signal.h>
65138593Ssam#endif
66138593Ssam#if OS2
67166956Ssam#include <sys/signal.h>
68167388Ssam#include "pckeys.h"
69166956Ssam#endif
70138593Ssam#if HAVE_SYS_STREAM_H
71138593Ssam#include <sys/stream.h>
72138593Ssam#endif
73138593Ssam#if HAVE_SYS_PTEM_H
74138593Ssam#include <sys/ptem.h>
75138593Ssam#endif
76138593Ssam
77138593Ssam#endif /* MSDOS_COMPILER */
78138593Ssam
79138593Ssam/*
80138593Ssam * Check for broken termios package that forces you to manually
81138593Ssam * set the line discipline.
82138593Ssam */
83138593Ssam#ifdef __ultrix__
84138593Ssam#define MUST_SET_LINE_DISCIPLINE 1
85138593Ssam#else
86138593Ssam#define MUST_SET_LINE_DISCIPLINE 0
87138593Ssam#endif
88138593Ssam
89138593Ssam#if OS2
90138593Ssam#define	DEFAULT_TERM		"ansi"
91138593Ssamstatic char *windowid;
92138593Ssam#else
93138593Ssam#define	DEFAULT_TERM		"unknown"
94138593Ssam#endif
95138593Ssam
96138593Ssam#if MSDOS_COMPILER==MSOFTC
97138593Ssamstatic int videopages;
98138593Ssamstatic long msec_loops;
99138593Ssamstatic int flash_created = 0;
100138593Ssam#define	SETCOLORS(fg,bg)	{ _settextcolor(fg); _setbkcolor(bg); }
101138593Ssam#endif
102138593Ssam
103138593Ssam#if MSDOS_COMPILER==BORLANDC
104138593Ssamstatic unsigned short *whitescreen;
105138593Ssamstatic int flash_created = 0;
106138593Ssam#endif
107138593Ssam#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
108138593Ssam#define _settextposition(y,x)   gotoxy(x,y)
109138593Ssam#define _clearscreen(m)         clrscr()
110138593Ssam#define _outtext(s)             cputs(s)
111138593Ssam#define	SETCOLORS(fg,bg)	{ textcolor(fg); textbackground(bg); }
112138593Ssamextern int sc_height;
113138593Ssam#endif
114138593Ssam
115138593Ssam#if MSDOS_COMPILER==WIN32C
116138593Ssamstruct keyRecord
117138593Ssam{
118138593Ssam	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	} else
1555		line_left();
1556#else
1557#if MSDOS_COMPILER==WIN32C
1558	if (!no_init)
1559		win32_init_term();
1560#endif
1561	initcolor();
1562	flush();
1563#endif
1564	init_done = 1;
1565}
1566
1567/*
1568 * Deinitialize terminal
1569 */
1570	public void
1571deinit()
1572{
1573	if (!init_done)
1574		return;
1575#if !MSDOS_COMPILER
1576	if (!no_keypad)
1577		tputs(sc_e_keypad, sc_height, putchr);
1578	if (!no_init)
1579		tputs(sc_deinit, sc_height, putchr);
1580#else
1581	/* Restore system colors. */
1582	SETCOLORS(sy_fg_color, sy_bg_color);
1583#if MSDOS_COMPILER==WIN32C
1584	if (!no_init)
1585		win32_deinit_term();
1586#else
1587	/* Need clreol to make SETCOLORS take effect. */
1588	clreol();
1589#endif
1590#endif
1591	init_done = 0;
1592}
1593
1594/*
1595 * Home cursor (move to upper left corner of screen).
1596 */
1597	public void
1598home()
1599{
1600#if !MSDOS_COMPILER
1601	tputs(sc_home, 1, putchr);
1602#else
1603	flush();
1604	_settextposition(1,1);
1605#endif
1606}
1607
1608/*
1609 * Add a blank line (called with cursor at home).
1610 * Should scroll the display down.
1611 */
1612	public void
1613add_line()
1614{
1615#if !MSDOS_COMPILER
1616	tputs(sc_addline, sc_height, putchr);
1617#else
1618	flush();
1619#if MSDOS_COMPILER==MSOFTC
1620	_scrolltextwindow(_GSCROLLDOWN);
1621	_settextposition(1,1);
1622#else
1623#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
1624	movetext(1,1, sc_width,sc_height-1, 1,2);
1625	gotoxy(1,1);
1626	clreol();
1627#else
1628#if MSDOS_COMPILER==WIN32C
1629    {
1630	CHAR_INFO fillchar;
1631	SMALL_RECT rcSrc, rcClip;
1632	COORD new_org;
1633	CONSOLE_SCREEN_BUFFER_INFO csbi;
1634
1635	GetConsoleScreenBufferInfo(con_out,&csbi);
1636
1637	/* The clip rectangle is the entire visible screen. */
1638	rcClip.Left = csbi.srWindow.Left;
1639	rcClip.Top = csbi.srWindow.Top;
1640	rcClip.Right = csbi.srWindow.Right;
1641	rcClip.Bottom = csbi.srWindow.Bottom;
1642
1643	/* The source rectangle is the visible screen minus the last line. */
1644	rcSrc = rcClip;
1645	rcSrc.Bottom--;
1646
1647	/* Move the top left corner of the source window down one row. */
1648	new_org.X = rcSrc.Left;
1649	new_org.Y = rcSrc.Top + 1;
1650
1651	/* Fill the right character and attributes. */
1652	fillchar.Char.AsciiChar = ' ';
1653	curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
1654	fillchar.Attributes = curr_attr;
1655	ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar);
1656	_settextposition(1,1);
1657    }
1658#endif
1659#endif
1660#endif
1661#endif
1662}
1663
1664#if 0
1665/*
1666 * Remove the n topmost lines and scroll everything below it in the
1667 * window upward.  This is needed to stop leaking the topmost line
1668 * into the scrollback buffer when we go down-one-line (in WIN32).
1669 */
1670	public void
1671remove_top(n)
1672	int n;
1673{
1674#if MSDOS_COMPILER==WIN32C
1675	SMALL_RECT rcSrc, rcClip;
1676	CHAR_INFO fillchar;
1677	COORD new_org;
1678	CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */
1679
1680	if (n >= sc_height - 1)
1681	{
1682		clear();
1683		home();
1684		return;
1685	}
1686
1687	flush();
1688
1689	GetConsoleScreenBufferInfo(con_out, &csbi);
1690
1691	/* Get the extent of all-visible-rows-but-the-last. */
1692	rcSrc.Left    = csbi.srWindow.Left;
1693	rcSrc.Top     = csbi.srWindow.Top + n;
1694	rcSrc.Right   = csbi.srWindow.Right;
1695	rcSrc.Bottom  = csbi.srWindow.Bottom;
1696
1697	/* Get the clip rectangle. */
1698	rcClip.Left   = rcSrc.Left;
1699	rcClip.Top    = csbi.srWindow.Top;
1700	rcClip.Right  = rcSrc.Right;
1701	rcClip.Bottom = rcSrc.Bottom ;
1702
1703	/* Move the source window up n rows. */
1704	new_org.X = rcSrc.Left;
1705	new_org.Y = rcSrc.Top - n;
1706
1707	/* Fill the right character and attributes. */
1708	fillchar.Char.AsciiChar = ' ';
1709	curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
1710	fillchar.Attributes = curr_attr;
1711
1712	ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar);
1713
1714	/* Position cursor on first blank line. */
1715	goto_line(sc_height - n - 1);
1716#endif
1717}
1718#endif
1719
1720#if MSDOS_COMPILER==WIN32C
1721/*
1722 * Clear the screen.
1723 */
1724	static void
1725win32_clear()
1726{
1727	/*
1728	 * This will clear only the currently visible rows of the NT
1729	 * console buffer, which means none of the precious scrollback
1730	 * rows are touched making for faster scrolling.  Note that, if
1731	 * the window has fewer columns than the console buffer (i.e.
1732	 * there is a horizontal scrollbar as well), the entire width
1733	 * of the visible rows will be cleared.
1734	 */
1735	COORD topleft;
1736	DWORD nchars;
1737	DWORD winsz;
1738	CONSOLE_SCREEN_BUFFER_INFO csbi;
1739
1740	/* get the number of cells in the current buffer */
1741	GetConsoleScreenBufferInfo(con_out, &csbi);
1742	winsz = csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1);
1743	topleft.X = 0;
1744	topleft.Y = csbi.srWindow.Top;
1745
1746	curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
1747	FillConsoleOutputCharacter(con_out, ' ', winsz, topleft, &nchars);
1748	FillConsoleOutputAttribute(con_out, curr_attr, winsz, topleft, &nchars);
1749}
1750
1751/*
1752 * Remove the n topmost lines and scroll everything below it in the
1753 * window upward.
1754 */
1755	public void
1756win32_scroll_up(n)
1757	int n;
1758{
1759	SMALL_RECT rcSrc, rcClip;
1760	CHAR_INFO fillchar;
1761	COORD topleft;
1762	COORD new_org;
1763	DWORD nchars;
1764	DWORD size;
1765	CONSOLE_SCREEN_BUFFER_INFO csbi;
1766
1767	if (n <= 0)
1768		return;
1769
1770	if (n >= sc_height - 1)
1771	{
1772		win32_clear();
1773		_settextposition(1,1);
1774		return;
1775	}
1776
1777	/* Get the extent of what will remain visible after scrolling. */
1778	GetConsoleScreenBufferInfo(con_out, &csbi);
1779	rcSrc.Left    = csbi.srWindow.Left;
1780	rcSrc.Top     = csbi.srWindow.Top + n;
1781	rcSrc.Right   = csbi.srWindow.Right;
1782	rcSrc.Bottom  = csbi.srWindow.Bottom;
1783
1784	/* Get the clip rectangle. */
1785	rcClip.Left   = rcSrc.Left;
1786	rcClip.Top    = csbi.srWindow.Top;
1787	rcClip.Right  = rcSrc.Right;
1788	rcClip.Bottom = rcSrc.Bottom ;
1789
1790	/* Move the source text to the top of the screen. */
1791	new_org.X = rcSrc.Left;
1792	new_org.Y = rcClip.Top;
1793
1794	/* Fill the right character and attributes. */
1795	fillchar.Char.AsciiChar = ' ';
1796	fillchar.Attributes = MAKEATTR(nm_fg_color, nm_bg_color);
1797
1798	/* Scroll the window. */
1799	SetConsoleTextAttribute(con_out, fillchar.Attributes);
1800	ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar);
1801
1802	/* Clear remaining lines at bottom. */
1803	topleft.X = csbi.dwCursorPosition.X;
1804	topleft.Y = rcSrc.Bottom - n;
1805	size = (n * csbi.dwSize.X) + (rcSrc.Right - topleft.X);
1806	FillConsoleOutputCharacter(con_out, ' ', size, topleft,
1807		&nchars);
1808	FillConsoleOutputAttribute(con_out, fillchar.Attributes, size, topleft,
1809		&nchars);
1810	SetConsoleTextAttribute(con_out, curr_attr);
1811
1812	/* Move cursor n lines up from where it was. */
1813	csbi.dwCursorPosition.Y -= n;
1814	SetConsoleCursorPosition(con_out, csbi.dwCursorPosition);
1815}
1816#endif
1817
1818/*
1819 * Move cursor to lower left corner of screen.
1820 */
1821	public void
1822lower_left()
1823{
1824#if !MSDOS_COMPILER
1825	tputs(sc_lower_left, 1, putchr);
1826#else
1827	flush();
1828	_settextposition(sc_height, 1);
1829#endif
1830}
1831
1832/*
1833 * Move cursor to left position of current line.
1834 */
1835	public void
1836line_left()
1837{
1838#if !MSDOS_COMPILER
1839	tputs(sc_return, 1, putchr);
1840#else
1841	int row;
1842	flush();
1843#if MSDOS_COMPILER==WIN32C
1844	{
1845		CONSOLE_SCREEN_BUFFER_INFO scr;
1846		GetConsoleScreenBufferInfo(con_out, &scr);
1847		row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1;
1848	}
1849#else
1850#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
1851		row = wherey();
1852#else
1853	{
1854		struct rccoord tpos = _gettextposition();
1855		row = tpos.row;
1856	}
1857#endif
1858#endif
1859	_settextposition(row, 1);
1860#endif
1861}
1862
1863/*
1864 * Check if the console size has changed and reset internals
1865 * (in lieu of SIGWINCH for WIN32).
1866 */
1867	public void
1868check_winch()
1869{
1870#if MSDOS_COMPILER==WIN32C
1871	CONSOLE_SCREEN_BUFFER_INFO scr;
1872	COORD size;
1873
1874	if (con_out == INVALID_HANDLE_VALUE)
1875		return;
1876
1877	flush();
1878	GetConsoleScreenBufferInfo(con_out, &scr);
1879	size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1;
1880	size.X = scr.srWindow.Right - scr.srWindow.Left + 1;
1881	if (size.Y != sc_height || size.X != sc_width)
1882	{
1883		sc_height = size.Y;
1884		sc_width = size.X;
1885		if (!no_init && con_out_ours == con_out)
1886			SetConsoleScreenBufferSize(con_out, size);
1887		pos_init();
1888		wscroll = (sc_height + 1) / 2;
1889		screen_trashed = 1;
1890	}
1891#endif
1892}
1893
1894/*
1895 * Goto a specific line on the screen.
1896 */
1897	public void
1898goto_line(slinenum)
1899	int slinenum;
1900{
1901#if !MSDOS_COMPILER
1902	tputs(tgoto(sc_move, 0, slinenum), 1, putchr);
1903#else
1904	flush();
1905	_settextposition(slinenum+1, 1);
1906#endif
1907}
1908
1909#if MSDOS_COMPILER==MSOFTC || MSDOS_COMPILER==BORLANDC
1910/*
1911 * Create an alternate screen which is all white.
1912 * This screen is used to create a "flash" effect, by displaying it
1913 * briefly and then switching back to the normal screen.
1914 * {{ Yuck!  There must be a better way to get a visual bell. }}
1915 */
1916	static void
1917create_flash()
1918{
1919#if MSDOS_COMPILER==MSOFTC
1920	struct videoconfig w;
1921	char *blanks;
1922	int row, col;
1923
1924	_getvideoconfig(&w);
1925	videopages = w.numvideopages;
1926	if (videopages < 2)
1927	{
1928		at_enter(AT_STANDOUT);
1929		at_exit();
1930	} else
1931	{
1932		_setactivepage(1);
1933		at_enter(AT_STANDOUT);
1934		blanks = (char *) ecalloc(w.numtextcols, sizeof(char));
1935		for (col = 0;  col < w.numtextcols;  col++)
1936			blanks[col] = ' ';
1937		for (row = w.numtextrows;  row > 0;  row--)
1938			_outmem(blanks, w.numtextcols);
1939		_setactivepage(0);
1940		_setvisualpage(0);
1941		free(blanks);
1942		at_exit();
1943	}
1944#else
1945#if MSDOS_COMPILER==BORLANDC
1946	register int n;
1947
1948	whitescreen = (unsigned short *)
1949		malloc(sc_width * sc_height * sizeof(short));
1950	if (whitescreen == NULL)
1951		return;
1952	for (n = 0;  n < sc_width * sc_height;  n++)
1953		whitescreen[n] = 0x7020;
1954#else
1955#if MSDOS_COMPILER==WIN32C
1956	register int n;
1957
1958	whitescreen = (WORD *)
1959		malloc(sc_height * sc_width * sizeof(WORD));
1960	if (whitescreen == NULL)
1961		return;
1962	/* Invert the standard colors. */
1963	for (n = 0;  n < sc_width * sc_height;  n++)
1964		whitescreen[n] = (WORD)((nm_fg_color << 4) | nm_bg_color);
1965#endif
1966#endif
1967#endif
1968	flash_created = 1;
1969}
1970#endif /* MSDOS_COMPILER */
1971
1972/*
1973 * Output the "visual bell", if there is one.
1974 */
1975	public void
1976vbell()
1977{
1978#if !MSDOS_COMPILER
1979	if (*sc_visual_bell == '\0')
1980		return;
1981	tputs(sc_visual_bell, sc_height, putchr);
1982#else
1983#if MSDOS_COMPILER==DJGPPC
1984	ScreenVisualBell();
1985#else
1986#if MSDOS_COMPILER==MSOFTC
1987	/*
1988	 * Create a flash screen on the second video page.
1989	 * Switch to that page, then switch back.
1990	 */
1991	if (!flash_created)
1992		create_flash();
1993	if (videopages < 2)
1994		return;
1995	_setvisualpage(1);
1996	delay(100);
1997	_setvisualpage(0);
1998#else
1999#if MSDOS_COMPILER==BORLANDC
2000	unsigned short *currscreen;
2001
2002	/*
2003	 * Get a copy of the current screen.
2004	 * Display the flash screen.
2005	 * Then restore the old screen.
2006	 */
2007	if (!flash_created)
2008		create_flash();
2009	if (whitescreen == NULL)
2010		return;
2011	currscreen = (unsigned short *)
2012		malloc(sc_width * sc_height * sizeof(short));
2013	if (currscreen == NULL) return;
2014	gettext(1, 1, sc_width, sc_height, currscreen);
2015	puttext(1, 1, sc_width, sc_height, whitescreen);
2016	delay(100);
2017	puttext(1, 1, sc_width, sc_height, currscreen);
2018	free(currscreen);
2019#else
2020#if MSDOS_COMPILER==WIN32C
2021	/* paint screen with an inverse color */
2022	clear();
2023
2024	/* leave it displayed for 100 msec. */
2025	Sleep(100);
2026
2027	/* restore with a redraw */
2028	repaint();
2029#endif
2030#endif
2031#endif
2032#endif
2033#endif
2034}
2035
2036/*
2037 * Make a noise.
2038 */
2039	static void
2040beep()
2041{
2042#if !MSDOS_COMPILER
2043	putchr(CONTROL('G'));
2044#else
2045#if MSDOS_COMPILER==WIN32C
2046	MessageBeep(0);
2047#else
2048	write(1, "\7", 1);
2049#endif
2050#endif
2051}
2052
2053/*
2054 * Ring the terminal bell.
2055 */
2056	public void
2057bell()
2058{
2059	if (quiet == VERY_QUIET)
2060		vbell();
2061	else
2062		beep();
2063}
2064
2065/*
2066 * Clear the screen.
2067 */
2068	public void
2069clear()
2070{
2071#if !MSDOS_COMPILER
2072	tputs(sc_clear, sc_height, putchr);
2073#else
2074	flush();
2075#if MSDOS_COMPILER==WIN32C
2076	win32_clear();
2077#else
2078	_clearscreen(_GCLEARSCREEN);
2079#endif
2080#endif
2081}
2082
2083/*
2084 * Clear from the cursor to the end of the cursor's line.
2085 * {{ This must not move the cursor. }}
2086 */
2087	public void
2088clear_eol()
2089{
2090#if !MSDOS_COMPILER
2091	tputs(sc_eol_clear, 1, putchr);
2092#else
2093#if MSDOS_COMPILER==MSOFTC
2094	short top, left;
2095	short bot, right;
2096	struct rccoord tpos;
2097
2098	flush();
2099	/*
2100	 * Save current state.
2101	 */
2102	tpos = _gettextposition();
2103	_gettextwindow(&top, &left, &bot, &right);
2104	/*
2105	 * Set a temporary window to the current line,
2106	 * from the cursor's position to the right edge of the screen.
2107	 * Then clear that window.
2108	 */
2109	_settextwindow(tpos.row, tpos.col, tpos.row, sc_width);
2110	_clearscreen(_GWINDOW);
2111	/*
2112	 * Restore state.
2113	 */
2114	_settextwindow(top, left, bot, right);
2115	_settextposition(tpos.row, tpos.col);
2116#else
2117#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
2118	flush();
2119	clreol();
2120#else
2121#if MSDOS_COMPILER==WIN32C
2122	DWORD           nchars;
2123	COORD           cpos;
2124	CONSOLE_SCREEN_BUFFER_INFO scr;
2125
2126	flush();
2127	memset(&scr, 0, sizeof(scr));
2128	GetConsoleScreenBufferInfo(con_out, &scr);
2129	cpos.X = scr.dwCursorPosition.X;
2130	cpos.Y = scr.dwCursorPosition.Y;
2131	curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
2132	FillConsoleOutputAttribute(con_out, curr_attr,
2133		scr.dwSize.X - cpos.X, cpos, &nchars);
2134	FillConsoleOutputCharacter(con_out, ' ',
2135		scr.dwSize.X - cpos.X, cpos, &nchars);
2136#endif
2137#endif
2138#endif
2139#endif
2140}
2141
2142/*
2143 * Clear the current line.
2144 * Clear the screen if there's off-screen memory below the display.
2145 */
2146	static void
2147clear_eol_bot()
2148{
2149#if MSDOS_COMPILER
2150	clear_eol();
2151#else
2152	if (below_mem)
2153		tputs(sc_eos_clear, 1, putchr);
2154	else
2155		tputs(sc_eol_clear, 1, putchr);
2156#endif
2157}
2158
2159/*
2160 * Clear the bottom line of the display.
2161 * Leave the cursor at the beginning of the bottom line.
2162 */
2163	public void
2164clear_bot()
2165{
2166	/*
2167	 * If we're in a non-normal attribute mode, temporarily exit
2168	 * the mode while we do the clear.  Some terminals fill the
2169	 * cleared area with the current attribute.
2170	 */
2171	if (oldbot)
2172		lower_left();
2173	else
2174		line_left();
2175
2176	if (attrmode == AT_NORMAL)
2177		clear_eol_bot();
2178	else
2179	{
2180		int saved_attrmode = attrmode;
2181
2182		at_exit();
2183		clear_eol_bot();
2184		at_enter(saved_attrmode);
2185	}
2186}
2187
2188	public void
2189at_enter(attr)
2190	int attr;
2191{
2192	attr = apply_at_specials(attr);
2193
2194#if !MSDOS_COMPILER
2195	/* The one with the most priority is last.  */
2196	if (attr & AT_UNDERLINE)
2197		tputs(sc_u_in, 1, putchr);
2198	if (attr & AT_BOLD)
2199		tputs(sc_b_in, 1, putchr);
2200	if (attr & AT_BLINK)
2201		tputs(sc_bl_in, 1, putchr);
2202	if (attr & AT_STANDOUT)
2203		tputs(sc_s_in, 1, putchr);
2204#else
2205	flush();
2206	/* The one with the most priority is first.  */
2207	if (attr & AT_STANDOUT)
2208	{
2209		SETCOLORS(so_fg_color, so_bg_color);
2210	} else if (attr & AT_BLINK)
2211	{
2212		SETCOLORS(bl_fg_color, bl_bg_color);
2213	}
2214	else if (attr & AT_BOLD)
2215	{
2216		SETCOLORS(bo_fg_color, bo_bg_color);
2217	}
2218	else if (attr & AT_UNDERLINE)
2219	{
2220		SETCOLORS(ul_fg_color, ul_bg_color);
2221	}
2222#endif
2223
2224	attrmode = attr;
2225}
2226
2227	public void
2228at_exit()
2229{
2230#if !MSDOS_COMPILER
2231	/* Undo things in the reverse order we did them.  */
2232	if (attrmode & AT_STANDOUT)
2233		tputs(sc_s_out, 1, putchr);
2234	if (attrmode & AT_BLINK)
2235		tputs(sc_bl_out, 1, putchr);
2236	if (attrmode & AT_BOLD)
2237		tputs(sc_b_out, 1, putchr);
2238	if (attrmode & AT_UNDERLINE)
2239		tputs(sc_u_out, 1, putchr);
2240#else
2241	flush();
2242	SETCOLORS(nm_fg_color, nm_bg_color);
2243#endif
2244
2245	attrmode = AT_NORMAL;
2246}
2247
2248	public void
2249at_switch(attr)
2250	int attr;
2251{
2252	int new_attrmode = apply_at_specials(attr);
2253	int ignore_modes = AT_ANSI;
2254
2255	if ((new_attrmode & ~ignore_modes) != (attrmode & ~ignore_modes))
2256	{
2257		at_exit();
2258		at_enter(attr);
2259	}
2260}
2261
2262	public int
2263is_at_equiv(attr1, attr2)
2264	int attr1;
2265	int attr2;
2266{
2267	attr1 = apply_at_specials(attr1);
2268	attr2 = apply_at_specials(attr2);
2269
2270	return (attr1 == attr2);
2271}
2272
2273	public int
2274apply_at_specials(attr)
2275	int attr;
2276{
2277	if (attr & AT_BINARY)
2278		attr |= binattr;
2279	if (attr & AT_HILITE)
2280		attr |= AT_STANDOUT;
2281	attr &= ~(AT_BINARY|AT_HILITE);
2282
2283	return attr;
2284}
2285
2286#if 0 /* No longer used */
2287/*
2288 * Erase the character to the left of the cursor
2289 * and move the cursor left.
2290 */
2291	public void
2292backspace()
2293{
2294#if !MSDOS_COMPILER
2295	/*
2296	 * Erase the previous character by overstriking with a space.
2297	 */
2298	tputs(sc_backspace, 1, putchr);
2299	putchr(' ');
2300	tputs(sc_backspace, 1, putchr);
2301#else
2302#if MSDOS_COMPILER==MSOFTC
2303	struct rccoord tpos;
2304
2305	flush();
2306	tpos = _gettextposition();
2307	if (tpos.col <= 1)
2308		return;
2309	_settextposition(tpos.row, tpos.col-1);
2310	_outtext(" ");
2311	_settextposition(tpos.row, tpos.col-1);
2312#else
2313#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
2314	cputs("\b");
2315#else
2316#if MSDOS_COMPILER==WIN32C
2317	COORD cpos;
2318	DWORD cChars;
2319	CONSOLE_SCREEN_BUFFER_INFO scr;
2320
2321	flush();
2322	GetConsoleScreenBufferInfo(con_out, &scr);
2323	cpos = scr.dwCursorPosition;
2324	if (cpos.X <= 0)
2325		return;
2326	cpos.X--;
2327	SetConsoleCursorPosition(con_out, cpos);
2328	FillConsoleOutputCharacter(con_out, (TCHAR)' ', 1, cpos, &cChars);
2329	SetConsoleCursorPosition(con_out, cpos);
2330#endif
2331#endif
2332#endif
2333#endif
2334}
2335#endif /* 0 */
2336
2337/*
2338 * Output a plain backspace, without erasing the previous char.
2339 */
2340	public void
2341putbs()
2342{
2343#if !MSDOS_COMPILER
2344	tputs(sc_backspace, 1, putchr);
2345#else
2346	int row, col;
2347
2348	flush();
2349	{
2350#if MSDOS_COMPILER==MSOFTC
2351		struct rccoord tpos;
2352		tpos = _gettextposition();
2353		row = tpos.row;
2354		col = tpos.col;
2355#else
2356#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
2357		row = wherey();
2358		col = wherex();
2359#else
2360#if MSDOS_COMPILER==WIN32C
2361		CONSOLE_SCREEN_BUFFER_INFO scr;
2362		GetConsoleScreenBufferInfo(con_out, &scr);
2363		row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1;
2364		col = scr.dwCursorPosition.X - scr.srWindow.Left + 1;
2365#endif
2366#endif
2367#endif
2368	}
2369	if (col <= 1)
2370		return;
2371	_settextposition(row, col-1);
2372#endif /* MSDOS_COMPILER */
2373}
2374
2375#if MSDOS_COMPILER==WIN32C
2376/*
2377 * Determine whether an input character is waiting to be read.
2378 */
2379	static int
2380win32_kbhit(tty)
2381	HANDLE tty;
2382{
2383	INPUT_RECORD ip;
2384	DWORD read;
2385
2386	if (keyCount > 0)
2387		return (TRUE);
2388
2389	currentKey.ascii = 0;
2390	currentKey.scan = 0;
2391
2392	/*
2393	 * Wait for a real key-down event, but
2394	 * ignore SHIFT and CONTROL key events.
2395	 */
2396	do
2397	{
2398		PeekConsoleInput(tty, &ip, 1, &read);
2399		if (read == 0)
2400			return (FALSE);
2401		ReadConsoleInput(tty, &ip, 1, &read);
2402	} while (ip.EventType != KEY_EVENT ||
2403		ip.Event.KeyEvent.bKeyDown != TRUE ||
2404		ip.Event.KeyEvent.wVirtualScanCode == 0 ||
2405		ip.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT ||
2406		ip.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL ||
2407		ip.Event.KeyEvent.wVirtualKeyCode == VK_MENU);
2408
2409	currentKey.ascii = ip.Event.KeyEvent.uChar.AsciiChar;
2410	currentKey.scan = ip.Event.KeyEvent.wVirtualScanCode;
2411	keyCount = ip.Event.KeyEvent.wRepeatCount;
2412
2413	if (ip.Event.KeyEvent.dwControlKeyState &
2414		(LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
2415	{
2416		switch (currentKey.scan)
2417		{
2418		case PCK_ALT_E:     /* letter 'E' */
2419			currentKey.ascii = 0;
2420			break;
2421		}
2422	} else if (ip.Event.KeyEvent.dwControlKeyState &
2423		(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
2424	{
2425		switch (currentKey.scan)
2426		{
2427		case PCK_RIGHT: /* right arrow */
2428			currentKey.scan = PCK_CTL_RIGHT;
2429			break;
2430		case PCK_LEFT: /* left arrow */
2431			currentKey.scan = PCK_CTL_LEFT;
2432			break;
2433		case PCK_DELETE: /* delete */
2434			currentKey.scan = PCK_CTL_DELETE;
2435			break;
2436		}
2437	}
2438	return (TRUE);
2439}
2440
2441/*
2442 * Read a character from the keyboard.
2443 */
2444	public char
2445WIN32getch(tty)
2446	int tty;
2447{
2448	int ascii;
2449
2450	if (pending_scancode)
2451	{
2452		pending_scancode = 0;
2453		return ((char)(currentKey.scan & 0x00FF));
2454	}
2455
2456	while (win32_kbhit((HANDLE)tty) == FALSE)
2457	{
2458		Sleep(20);
2459		if (ABORT_SIGS())
2460			return ('\003');
2461		continue;
2462	}
2463	keyCount --;
2464	ascii = currentKey.ascii;
2465	/*
2466	 * On PC's, the extended keys return a 2 byte sequence beginning
2467	 * with '00', so if the ascii code is 00, the next byte will be
2468	 * the lsb of the scan code.
2469	 */
2470	pending_scancode = (ascii == 0x00);
2471	return ((char)ascii);
2472}
2473#endif
2474
2475#if MSDOS_COMPILER
2476/*
2477 */
2478	public void
2479WIN32setcolors(fg, bg)
2480	int fg;
2481	int bg;
2482{
2483	SETCOLORS(fg, bg);
2484}
2485
2486/*
2487 */
2488	public void
2489WIN32textout(text, len)
2490	char *text;
2491	int len;
2492{
2493#if MSDOS_COMPILER==WIN32C
2494	DWORD written;
2495	WriteConsole(con_out, text, len, &written, NULL);
2496#else
2497	char c = text[len];
2498	text[len] = '\0';
2499	cputs(text);
2500	text[len] = c;
2501#endif
2502}
2503#endif
2504