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