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