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