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