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