xref: /freebsd/contrib/less/screen.c (revision f0a75d274af375d15b97b830966b99a02b7db911)
1 /* $FreeBSD$ */
2 /*
3  * Copyright (C) 1984-2005  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_move,		/* General cursor positioning */
168 	*sc_clear,		/* Clear screen */
169 	*sc_eol_clear,		/* Clear to end of line */
170 	*sc_eos_clear,		/* Clear to end of screen */
171 	*sc_s_in,		/* Enter standout (highlighted) mode */
172 	*sc_s_out,		/* Exit standout mode */
173 	*sc_u_in,		/* Enter underline mode */
174 	*sc_u_out,		/* Exit underline mode */
175 	*sc_b_in,		/* Enter bold mode */
176 	*sc_b_out,		/* Exit bold mode */
177 	*sc_bl_in,		/* Enter blink mode */
178 	*sc_bl_out,		/* Exit blink mode */
179 	*sc_visual_bell,	/* Visual bell (flash screen) sequence */
180 	*sc_backspace,		/* Backspace cursor */
181 	*sc_s_keypad,		/* Start keypad mode */
182 	*sc_e_keypad,		/* End keypad mode */
183 	*sc_init,		/* Startup terminal initialization */
184 	*sc_deinit;		/* Exit terminal de-initialization */
185 #endif
186 
187 static int init_done = 0;
188 
189 public int auto_wrap;		/* Terminal does \r\n when write past margin */
190 public int ignaw;		/* Terminal ignores \n immediately after wrap */
191 public int erase_char;		/* The user's erase char */
192 public int erase2_char;		/* The user's other erase char */
193 public int kill_char;		/* The user's line-kill char */
194 public int werase_char;		/* The user's word-erase char */
195 public int sc_width, sc_height;	/* Height & width of screen */
196 public int bo_s_width, bo_e_width;	/* Printing width of boldface seq */
197 public int ul_s_width, ul_e_width;	/* Printing width of underline seq */
198 public int so_s_width, so_e_width;	/* Printing width of standout seq */
199 public int bl_s_width, bl_e_width;	/* Printing width of blink seq */
200 public int above_mem, below_mem;	/* Memory retained above/below screen */
201 public int can_goto_line;		/* Can move cursor to any line */
202 public int clear_bg;		/* Clear fills with background color */
203 public int missing_cap = 0;	/* Some capability is missing */
204 
205 static int attrmode = AT_NORMAL;
206 extern int binattr;
207 
208 #if !MSDOS_COMPILER
209 static char *cheaper();
210 static void tmodes();
211 #endif
212 
213 /*
214  * These two variables are sometimes defined in,
215  * and needed by, the termcap library.
216  */
217 #if MUST_DEFINE_OSPEED
218 extern short ospeed;	/* Terminal output baud rate */
219 extern char PC;		/* Pad character */
220 #endif
221 #ifdef _OSK
222 short ospeed;
223 char PC_, *UP, *BC;
224 #endif
225 
226 extern int quiet;		/* If VERY_QUIET, use visual bell for bell */
227 extern int no_back_scroll;
228 extern int swindow;
229 extern int no_init;
230 extern int quit_at_eof;
231 extern int more_mode;
232 extern int no_keypad;
233 extern int sigs;
234 extern int wscroll;
235 extern int screen_trashed;
236 extern int tty;
237 extern int top_scroll;
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 
628 	strcpy(name, "LESS_TERMCAP_");
629 	strcat(name, capname);
630 	return (lgetenv(name));
631 }
632 
633 	static int
634 ltgetflag(capname)
635 	char *capname;
636 {
637 	char *s;
638 
639 	if ((s = ltget_env(capname)) != NULL)
640 		return (*s != '\0' && *s != '0');
641 	if (hardcopy)
642 		return (0);
643 	return (tgetflag(capname));
644 }
645 
646 	static int
647 ltgetnum(capname)
648 	char *capname;
649 {
650 	char *s;
651 
652 	if ((s = ltget_env(capname)) != NULL)
653 		return (atoi(s));
654 	if (hardcopy)
655 		return (-1);
656 	return (tgetnum(capname));
657 }
658 
659 	static char *
660 ltgetstr(capname, pp)
661 	char *capname;
662 	char **pp;
663 {
664 	char *s;
665 
666 	if ((s = ltget_env(capname)) != NULL)
667 		return (s);
668 	if (hardcopy)
669 		return (NULL);
670 	return (tgetstr(capname, pp));
671 }
672 #endif /* MSDOS_COMPILER */
673 
674 /*
675  * Get size of the output screen.
676  */
677 	public void
678 scrsize()
679 {
680 	register char *s;
681 	int sys_height;
682 	int sys_width;
683 #if !MSDOS_COMPILER
684 	int n;
685 #endif
686 
687 #define	DEF_SC_WIDTH	80
688 #if MSDOS_COMPILER
689 #define	DEF_SC_HEIGHT	25
690 #else
691 #define	DEF_SC_HEIGHT	24
692 #endif
693 
694 
695 	sys_width = sys_height = 0;
696 
697 #if MSDOS_COMPILER==MSOFTC
698 	{
699 		struct videoconfig w;
700 		_getvideoconfig(&w);
701 		sys_height = w.numtextrows;
702 		sys_width = w.numtextcols;
703 	}
704 #else
705 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
706 	{
707 		struct text_info w;
708 		gettextinfo(&w);
709 		sys_height = w.screenheight;
710 		sys_width = w.screenwidth;
711 	}
712 #else
713 #if MSDOS_COMPILER==WIN32C
714 	{
715 		CONSOLE_SCREEN_BUFFER_INFO scr;
716 		GetConsoleScreenBufferInfo(con_out, &scr);
717 		sys_height = scr.srWindow.Bottom - scr.srWindow.Top + 1;
718 		sys_width = scr.srWindow.Right - scr.srWindow.Left + 1;
719 	}
720 #else
721 #if OS2
722 	{
723 		int s[2];
724 		_scrsize(s);
725 		sys_width = s[0];
726 		sys_height = s[1];
727 		/*
728 		 * When using terminal emulators for XFree86/OS2, the
729 		 * _scrsize function does not work well.
730 		 * Call the scrsize.exe program to get the window size.
731 		 */
732 		windowid = getenv("WINDOWID");
733 		if (windowid != NULL)
734 		{
735 			FILE *fd = popen("scrsize", "rt");
736 			if (fd != NULL)
737 			{
738 				int w, h;
739 				fscanf(fd, "%i %i", &w, &h);
740 				if (w > 0 && h > 0)
741 				{
742 					sys_width = w;
743 					sys_height = h;
744 				}
745 				pclose(fd);
746 			}
747 		}
748 	}
749 #else
750 #ifdef TIOCGWINSZ
751 	{
752 		struct winsize w;
753 		if (ioctl(2, TIOCGWINSZ, &w) == 0)
754 		{
755 			if (w.ws_row > 0)
756 				sys_height = w.ws_row;
757 			if (w.ws_col > 0)
758 				sys_width = w.ws_col;
759 		}
760 	}
761 #else
762 #ifdef WIOCGETD
763 	{
764 		struct uwdata w;
765 		if (ioctl(2, WIOCGETD, &w) == 0)
766 		{
767 			if (w.uw_height > 0)
768 				sys_height = w.uw_height / w.uw_vs;
769 			if (w.uw_width > 0)
770 				sys_width = w.uw_width / w.uw_hs;
771 		}
772 	}
773 #endif
774 #endif
775 #endif
776 #endif
777 #endif
778 #endif
779 
780 	if (sys_height > 0)
781 		sc_height = sys_height;
782 	else if ((s = lgetenv("LINES")) != NULL)
783 		sc_height = atoi(s);
784 #if !MSDOS_COMPILER
785 	else if ((n = ltgetnum("li")) > 0)
786  		sc_height = n;
787 #endif
788 	else
789 		sc_height = DEF_SC_HEIGHT;
790 
791 	if (sys_width > 0)
792 		sc_width = sys_width;
793 	else if ((s = lgetenv("COLUMNS")) != NULL)
794 		sc_width = atoi(s);
795 #if !MSDOS_COMPILER
796 	else if ((n = ltgetnum("co")) > 0)
797  		sc_width = n;
798 #endif
799 	else
800 		sc_width = DEF_SC_WIDTH;
801 }
802 
803 #if MSDOS_COMPILER==MSOFTC
804 /*
805  * Figure out how many empty loops it takes to delay a millisecond.
806  */
807 	static void
808 get_clock()
809 {
810 	clock_t start;
811 
812 	/*
813 	 * Get synchronized at the start of a tick.
814 	 */
815 	start = clock();
816 	while (clock() == start)
817 		;
818 	/*
819 	 * Now count loops till the next tick.
820 	 */
821 	start = clock();
822 	msec_loops = 0;
823 	while (clock() == start)
824 		msec_loops++;
825 	/*
826 	 * Convert from (loops per clock) to (loops per millisecond).
827 	 */
828 	msec_loops *= CLOCKS_PER_SEC;
829 	msec_loops /= 1000;
830 }
831 
832 /*
833  * Delay for a specified number of milliseconds.
834  */
835 	static void
836 dummy_func()
837 {
838 	static long delay_dummy = 0;
839 	delay_dummy++;
840 }
841 
842 	static void
843 delay(msec)
844 	int msec;
845 {
846 	long i;
847 
848 	while (msec-- > 0)
849 	{
850 		for (i = 0;  i < msec_loops;  i++)
851 		{
852 			/*
853 			 * Make it look like we're doing something here,
854 			 * so the optimizer doesn't remove the whole loop.
855 			 */
856 			dummy_func();
857 		}
858 	}
859 }
860 #endif
861 
862 /*
863  * Return the characters actually input by a "special" key.
864  */
865 	public char *
866 special_key_str(key)
867 	int key;
868 {
869 	static char tbuf[40];
870 	char *s;
871 #if MSDOS_COMPILER || OS2
872 	static char k_right[]		= { '\340', PCK_RIGHT, 0 };
873 	static char k_left[]		= { '\340', PCK_LEFT, 0  };
874 	static char k_ctl_right[]	= { '\340', PCK_CTL_RIGHT, 0  };
875 	static char k_ctl_left[]	= { '\340', PCK_CTL_LEFT, 0  };
876 	static char k_insert[]		= { '\340', PCK_INSERT, 0  };
877 	static char k_delete[]		= { '\340', PCK_DELETE, 0  };
878 	static char k_ctl_delete[]	= { '\340', PCK_CTL_DELETE, 0  };
879 	static char k_ctl_backspace[]	= { '\177', 0 };
880 	static char k_home[]		= { '\340', PCK_HOME, 0 };
881 	static char k_end[]		= { '\340', PCK_END, 0 };
882 	static char k_up[]		= { '\340', PCK_UP, 0 };
883 	static char k_down[]		= { '\340', PCK_DOWN, 0 };
884 	static char k_backtab[]		= { '\340', PCK_SHIFT_TAB, 0 };
885 	static char k_pagedown[]	= { '\340', PCK_PAGEDOWN, 0 };
886 	static char k_pageup[]		= { '\340', PCK_PAGEUP, 0 };
887 	static char k_f1[]		= { '\340', PCK_F1, 0 };
888 #endif
889 #if !MSDOS_COMPILER
890 	char *sp = tbuf;
891 #endif
892 
893 	switch (key)
894 	{
895 #if OS2
896 	/*
897 	 * If windowid is not NULL, assume less is executed in
898 	 * the XFree86 environment.
899 	 */
900 	case SK_RIGHT_ARROW:
901 		s = windowid ? ltgetstr("kr", &sp) : k_right;
902 		break;
903 	case SK_LEFT_ARROW:
904 		s = windowid ? ltgetstr("kl", &sp) : k_left;
905 		break;
906 	case SK_UP_ARROW:
907 		s = windowid ? ltgetstr("ku", &sp) : k_up;
908 		break;
909 	case SK_DOWN_ARROW:
910 		s = windowid ? ltgetstr("kd", &sp) : k_down;
911 		break;
912 	case SK_PAGE_UP:
913 		s = windowid ? ltgetstr("kP", &sp) : k_pageup;
914 		break;
915 	case SK_PAGE_DOWN:
916 		s = windowid ? ltgetstr("kN", &sp) : k_pagedown;
917 		break;
918 	case SK_HOME:
919 		s = windowid ? ltgetstr("kh", &sp) : k_home;
920 		break;
921 	case SK_END:
922 		s = windowid ? ltgetstr("@7", &sp) : k_end;
923 		break;
924 	case SK_DELETE:
925 		if (windowid)
926 		{
927 			s = ltgetstr("kD", &sp);
928 			if (s == NULL)
929 			{
930 				tbuf[0] = '\177';
931 				tbuf[1] = '\0';
932 				s = tbuf;
933 			}
934 		} else
935 			s = k_delete;
936 		break;
937 #endif
938 #if MSDOS_COMPILER
939 	case SK_RIGHT_ARROW:
940 		s = k_right;
941 		break;
942 	case SK_LEFT_ARROW:
943 		s = k_left;
944 		break;
945 	case SK_UP_ARROW:
946 		s = k_up;
947 		break;
948 	case SK_DOWN_ARROW:
949 		s = k_down;
950 		break;
951 	case SK_PAGE_UP:
952 		s = k_pageup;
953 		break;
954 	case SK_PAGE_DOWN:
955 		s = k_pagedown;
956 		break;
957 	case SK_HOME:
958 		s = k_home;
959 		break;
960 	case SK_END:
961 		s = k_end;
962 		break;
963 	case SK_DELETE:
964 		s = k_delete;
965 		break;
966 #endif
967 #if MSDOS_COMPILER || OS2
968 	case SK_INSERT:
969 		s = k_insert;
970 		break;
971 	case SK_CTL_LEFT_ARROW:
972 		s = k_ctl_left;
973 		break;
974 	case SK_CTL_RIGHT_ARROW:
975 		s = k_ctl_right;
976 		break;
977 	case SK_CTL_BACKSPACE:
978 		s = k_ctl_backspace;
979 		break;
980 	case SK_CTL_DELETE:
981 		s = k_ctl_delete;
982 		break;
983 	case SK_F1:
984 		s = k_f1;
985 		break;
986 	case SK_BACKTAB:
987 		s = k_backtab;
988 		break;
989 #else
990 	case SK_RIGHT_ARROW:
991 		s = ltgetstr("kr", &sp);
992 		break;
993 	case SK_LEFT_ARROW:
994 		s = ltgetstr("kl", &sp);
995 		break;
996 	case SK_UP_ARROW:
997 		s = ltgetstr("ku", &sp);
998 		break;
999 	case SK_DOWN_ARROW:
1000 		s = ltgetstr("kd", &sp);
1001 		break;
1002 	case SK_PAGE_UP:
1003 		s = ltgetstr("kP", &sp);
1004 		break;
1005 	case SK_PAGE_DOWN:
1006 		s = ltgetstr("kN", &sp);
1007 		break;
1008 	case SK_HOME:
1009 		s = ltgetstr("kh", &sp);
1010 		break;
1011 	case SK_END:
1012 		s = ltgetstr("@7", &sp);
1013 		break;
1014 	case SK_DELETE:
1015 		s = ltgetstr("kD", &sp);
1016 		if (s == NULL)
1017 		{
1018 			tbuf[0] = '\177';
1019 			tbuf[1] = '\0';
1020 			s = tbuf;
1021 		}
1022 		break;
1023 #endif
1024 	case SK_CONTROL_K:
1025 		tbuf[0] = CONTROL('K');
1026 		tbuf[1] = '\0';
1027 		s = tbuf;
1028 		break;
1029 	default:
1030 		return (NULL);
1031 	}
1032 	return (s);
1033 }
1034 
1035 /*
1036  * Get terminal capabilities via termcap.
1037  */
1038 	public void
1039 get_term()
1040 {
1041 #if MSDOS_COMPILER
1042 	auto_wrap = 1;
1043 	ignaw = 0;
1044 	can_goto_line = 1;
1045 	clear_bg = 1;
1046 	/*
1047 	 * Set up default colors.
1048 	 * The xx_s_width and xx_e_width vars are already initialized to 0.
1049 	 */
1050 #if MSDOS_COMPILER==MSOFTC
1051 	sy_bg_color = _getbkcolor();
1052 	sy_fg_color = _gettextcolor();
1053 	get_clock();
1054 #else
1055 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
1056     {
1057 	struct text_info w;
1058 	gettextinfo(&w);
1059 	sy_bg_color = (w.attribute >> 4) & 0x0F;
1060 	sy_fg_color = (w.attribute >> 0) & 0x0F;
1061     }
1062 #else
1063 #if MSDOS_COMPILER==WIN32C
1064     {
1065 	DWORD nread;
1066 	CONSOLE_SCREEN_BUFFER_INFO scr;
1067 
1068 	con_out_save = con_out = GetStdHandle(STD_OUTPUT_HANDLE);
1069 	/*
1070 	 * Always open stdin in binary. Note this *must* be done
1071 	 * before any file operations have been done on fd0.
1072 	 */
1073 	SET_BINARY(0);
1074 	GetConsoleScreenBufferInfo(con_out, &scr);
1075 	ReadConsoleOutputAttribute(con_out, &curr_attr,
1076 					1, scr.dwCursorPosition, &nread);
1077 	sy_bg_color = (curr_attr & BG_COLORS) >> 4; /* normalize */
1078 	sy_fg_color = curr_attr & FG_COLORS;
1079     }
1080 #endif
1081 #endif
1082 #endif
1083 	nm_fg_color = sy_fg_color;
1084 	nm_bg_color = sy_bg_color;
1085 	bo_fg_color = 11;
1086 	bo_bg_color = 0;
1087 	ul_fg_color = 9;
1088 	ul_bg_color = 0;
1089 	so_fg_color = 15;
1090 	so_bg_color = 9;
1091 	bl_fg_color = 15;
1092 	bl_bg_color = 0;
1093 
1094 	/*
1095 	 * Get size of the screen.
1096 	 */
1097 	scrsize();
1098 	pos_init();
1099 
1100 
1101 #else /* !MSDOS_COMPILER */
1102 
1103 	char *sp;
1104 	register char *t1, *t2;
1105 	char *term;
1106 	char termbuf[TERMBUF_SIZE];
1107 
1108 	static char sbuf[TERMSBUF_SIZE];
1109 
1110 #if OS2
1111 	/*
1112 	 * Make sure the termcap database is available.
1113 	 */
1114 	sp = lgetenv("TERMCAP");
1115 	if (sp == NULL || *sp == '\0')
1116 	{
1117 		char *termcap;
1118 		if ((sp = homefile("termcap.dat")) != NULL)
1119 		{
1120 			termcap = (char *) ecalloc(strlen(sp)+9, sizeof(char));
1121 			sprintf(termcap, "TERMCAP=%s", sp);
1122 			free(sp);
1123 			putenv(termcap);
1124 		}
1125 	}
1126 #endif
1127 	/*
1128 	 * Find out what kind of terminal this is.
1129 	 */
1130  	if ((term = lgetenv("TERM")) == NULL)
1131  		term = DEFAULT_TERM;
1132 	hardcopy = 0;
1133  	if (tgetent(termbuf, term) <= 0)
1134  		hardcopy = 1;
1135  	if (ltgetflag("hc"))
1136 		hardcopy = 1;
1137 
1138 	/*
1139 	 * Get size of the screen.
1140 	 */
1141 	scrsize();
1142 	pos_init();
1143 
1144 	auto_wrap = ltgetflag("am");
1145 	ignaw = ltgetflag("xn");
1146 	above_mem = ltgetflag("da");
1147 	below_mem = ltgetflag("db");
1148 	clear_bg = ltgetflag("ut");
1149 
1150 	/*
1151 	 * Assumes termcap variable "sg" is the printing width of:
1152 	 * the standout sequence, the end standout sequence,
1153 	 * the underline sequence, the end underline sequence,
1154 	 * the boldface sequence, and the end boldface sequence.
1155 	 */
1156 	if ((so_s_width = ltgetnum("sg")) < 0)
1157 		so_s_width = 0;
1158 	so_e_width = so_s_width;
1159 
1160 	bo_s_width = bo_e_width = so_s_width;
1161 	ul_s_width = ul_e_width = so_s_width;
1162 	bl_s_width = bl_e_width = so_s_width;
1163 
1164 #if HILITE_SEARCH
1165 	if (so_s_width > 0 || so_e_width > 0)
1166 		/*
1167 		 * Disable highlighting by default on magic cookie terminals.
1168 		 * Turning on highlighting might change the displayed width
1169 		 * of a line, causing the display to get messed up.
1170 		 * The user can turn it back on with -g,
1171 		 * but she won't like the results.
1172 		 */
1173 		hilite_search = 0;
1174 #endif
1175 
1176 	/*
1177 	 * Get various string-valued capabilities.
1178 	 */
1179 	sp = sbuf;
1180 
1181 #if HAVE_OSPEED
1182 	sc_pad = ltgetstr("pc", &sp);
1183 	if (sc_pad != NULL)
1184 		PC = *sc_pad;
1185 #endif
1186 
1187 	sc_s_keypad = ltgetstr("ks", &sp);
1188 	if (sc_s_keypad == NULL)
1189 		sc_s_keypad = "";
1190 	sc_e_keypad = ltgetstr("ke", &sp);
1191 	if (sc_e_keypad == NULL)
1192 		sc_e_keypad = "";
1193 
1194 	/*
1195 	 * This loses for terminals with termcap entries with ti/te strings
1196 	 * that switch to/from an alternate screen, and we're in quit_at_eof
1197 	 * (eg, more(1)).
1198  	 */
1199 	if (!quit_at_eof && !more_mode) {
1200 		sc_init = ltgetstr("ti", &sp);
1201 		sc_deinit = ltgetstr("te", &sp);
1202 	}
1203 
1204 	if (sc_init == NULL)
1205 		sc_init = "";
1206 
1207 	if (sc_deinit == NULL)
1208 		sc_deinit = "";
1209 
1210 	sc_eol_clear = ltgetstr("ce", &sp);
1211 	if (sc_eol_clear == NULL || *sc_eol_clear == '\0')
1212 	{
1213 		missing_cap = 1;
1214 		sc_eol_clear = "";
1215 	}
1216 
1217 	sc_eos_clear = ltgetstr("cd", &sp);
1218 	if (below_mem && (sc_eos_clear == NULL || *sc_eos_clear == '\0'))
1219 	{
1220 		missing_cap = 1;
1221 		sc_eos_clear = "";
1222 	}
1223 
1224 	sc_clear = ltgetstr("cl", &sp);
1225 	if (sc_clear == NULL || *sc_clear == '\0')
1226 	{
1227 		missing_cap = 1;
1228 		sc_clear = "\n\n";
1229 	}
1230 
1231 	sc_move = ltgetstr("cm", &sp);
1232 	if (sc_move == NULL || *sc_move == '\0')
1233 	{
1234 		/*
1235 		 * This is not an error here, because we don't
1236 		 * always need sc_move.
1237 		 * We need it only if we don't have home or lower-left.
1238 		 */
1239 		sc_move = "";
1240 		can_goto_line = 0;
1241 	} else
1242 		can_goto_line = 1;
1243 
1244 	tmodes("so", "se", &sc_s_in, &sc_s_out, "", "", &sp);
1245 	tmodes("us", "ue", &sc_u_in, &sc_u_out, sc_s_in, sc_s_out, &sp);
1246 	tmodes("md", "me", &sc_b_in, &sc_b_out, sc_s_in, sc_s_out, &sp);
1247 	tmodes("mb", "me", &sc_bl_in, &sc_bl_out, sc_s_in, sc_s_out, &sp);
1248 
1249 	sc_visual_bell = ltgetstr("vb", &sp);
1250 	if (sc_visual_bell == NULL)
1251 		sc_visual_bell = "";
1252 
1253 	if (ltgetflag("bs"))
1254 		sc_backspace = "\b";
1255 	else
1256 	{
1257 		sc_backspace = ltgetstr("bc", &sp);
1258 		if (sc_backspace == NULL || *sc_backspace == '\0')
1259 			sc_backspace = "\b";
1260 	}
1261 
1262 	/*
1263 	 * Choose between using "ho" and "cm" ("home" and "cursor move")
1264 	 * to move the cursor to the upper left corner of the screen.
1265 	 */
1266 	t1 = ltgetstr("ho", &sp);
1267 	if (t1 == NULL)
1268 		t1 = "";
1269 	if (*sc_move == '\0')
1270 		t2 = "";
1271 	else
1272 	{
1273 		strcpy(sp, tgoto(sc_move, 0, 0));
1274 		t2 = sp;
1275 		sp += strlen(sp) + 1;
1276 	}
1277 	sc_home = cheaper(t1, t2, "|\b^");
1278 
1279 	/*
1280 	 * Choose between using "ll" and "cm"  ("lower left" and "cursor move")
1281 	 * to move the cursor to the lower left corner of the screen.
1282 	 */
1283 	t1 = ltgetstr("ll", &sp);
1284 	if (t1 == NULL)
1285 		t1 = "";
1286 	if (*sc_move == '\0')
1287 		t2 = "";
1288 	else
1289 	{
1290 		strcpy(sp, tgoto(sc_move, 0, sc_height-1));
1291 		t2 = sp;
1292 		sp += strlen(sp) + 1;
1293 	}
1294 	sc_lower_left = cheaper(t1, t2, "\r");
1295 
1296 	/*
1297 	 * Choose between using "al" or "sr" ("add line" or "scroll reverse")
1298 	 * to add a line at the top of the screen.
1299 	 */
1300 	t1 = ltgetstr("al", &sp);
1301 	if (t1 == NULL)
1302 		t1 = "";
1303 	t2 = ltgetstr("sr", &sp);
1304 	if (t2 == NULL)
1305 		t2 = "";
1306 #if OS2
1307 	if (*t1 == '\0' && *t2 == '\0')
1308 		sc_addline = "";
1309 	else
1310 #endif
1311 	if (above_mem)
1312 		sc_addline = t1;
1313 	else
1314 		sc_addline = cheaper(t1, t2, "");
1315 	if (*sc_addline == '\0')
1316 	{
1317 		/*
1318 		 * Force repaint on any backward movement.
1319 		 */
1320 		no_back_scroll = 1;
1321 	}
1322 #endif /* MSDOS_COMPILER */
1323 }
1324 
1325 #if !MSDOS_COMPILER
1326 /*
1327  * Return the cost of displaying a termcap string.
1328  * We use the trick of calling tputs, but as a char printing function
1329  * we give it inc_costcount, which just increments "costcount".
1330  * This tells us how many chars would be printed by using this string.
1331  * {{ Couldn't we just use strlen? }}
1332  */
1333 static int costcount;
1334 
1335 /*ARGSUSED*/
1336 	static int
1337 inc_costcount(c)
1338 	int c;
1339 {
1340 	costcount++;
1341 	return (c);
1342 }
1343 
1344 	static int
1345 cost(t)
1346 	char *t;
1347 {
1348 	costcount = 0;
1349 	tputs(t, sc_height, inc_costcount);
1350 	return (costcount);
1351 }
1352 
1353 /*
1354  * Return the "best" of the two given termcap strings.
1355  * The best, if both exist, is the one with the lower
1356  * cost (see cost() function).
1357  */
1358 	static char *
1359 cheaper(t1, t2, def)
1360 	char *t1, *t2;
1361 	char *def;
1362 {
1363 	if (*t1 == '\0' && *t2 == '\0')
1364 	{
1365 		missing_cap = 1;
1366 		return (def);
1367 	}
1368 	if (*t1 == '\0')
1369 		return (t2);
1370 	if (*t2 == '\0')
1371 		return (t1);
1372 	if (cost(t1) < cost(t2))
1373 		return (t1);
1374 	return (t2);
1375 }
1376 
1377 	static void
1378 tmodes(incap, outcap, instr, outstr, def_instr, def_outstr, spp)
1379 	char *incap;
1380 	char *outcap;
1381 	char **instr;
1382 	char **outstr;
1383 	char *def_instr;
1384 	char *def_outstr;
1385 	char **spp;
1386 {
1387 	*instr = ltgetstr(incap, spp);
1388 	if (*instr == NULL)
1389 	{
1390 		/* Use defaults. */
1391 		*instr = def_instr;
1392 		*outstr = def_outstr;
1393 		return;
1394 	}
1395 
1396 	*outstr = ltgetstr(outcap, spp);
1397 	if (*outstr == NULL)
1398 		/* No specific out capability; use "me". */
1399 		*outstr = ltgetstr("me", spp);
1400 	if (*outstr == NULL)
1401 		/* Don't even have "me"; use a null string. */
1402 		*outstr = "";
1403 }
1404 
1405 #endif /* MSDOS_COMPILER */
1406 
1407 
1408 /*
1409  * Below are the functions which perform all the
1410  * terminal-specific screen manipulation.
1411  */
1412 
1413 
1414 #if MSDOS_COMPILER
1415 
1416 #if MSDOS_COMPILER==WIN32C
1417 	static void
1418 _settextposition(int row, int col)
1419 {
1420 	COORD cpos;
1421 	CONSOLE_SCREEN_BUFFER_INFO csbi;
1422 
1423 	GetConsoleScreenBufferInfo(con_out, &csbi);
1424 	cpos.X = csbi.srWindow.Left + (col - 1);
1425 	cpos.Y = csbi.srWindow.Top + (row - 1);
1426 	SetConsoleCursorPosition(con_out, cpos);
1427 }
1428 #endif
1429 
1430 /*
1431  * Initialize the screen to the correct color at startup.
1432  */
1433 	static void
1434 initcolor()
1435 {
1436 	SETCOLORS(nm_fg_color, nm_bg_color);
1437 #if 0
1438 	/*
1439 	 * This clears the screen at startup.  This is different from
1440 	 * the behavior of other versions of less.  Disable it for now.
1441 	 */
1442 	char *blanks;
1443 	int row;
1444 	int col;
1445 
1446 	/*
1447 	 * Create a complete, blank screen using "normal" colors.
1448 	 */
1449 	SETCOLORS(nm_fg_color, nm_bg_color);
1450 	blanks = (char *) ecalloc(width+1, sizeof(char));
1451 	for (col = 0;  col < sc_width;  col++)
1452 		blanks[col] = ' ';
1453 	blanks[sc_width] = '\0';
1454 	for (row = 0;  row < sc_height;  row++)
1455 		_outtext(blanks);
1456 	free(blanks);
1457 #endif
1458 }
1459 #endif
1460 
1461 #if MSDOS_COMPILER==WIN32C
1462 
1463 /*
1464  * Termcap-like init with a private win32 console.
1465  */
1466 	static void
1467 win32_init_term()
1468 {
1469 	CONSOLE_SCREEN_BUFFER_INFO scr;
1470 	COORD size;
1471 
1472 	if (con_out_save == INVALID_HANDLE_VALUE)
1473 		return;
1474 
1475 	GetConsoleScreenBufferInfo(con_out_save, &scr);
1476 
1477 	if (con_out_ours == INVALID_HANDLE_VALUE)
1478 	{
1479 		/*
1480 		 * Create our own screen buffer, so that we
1481 		 * may restore the original when done.
1482 		 */
1483 		con_out_ours = CreateConsoleScreenBuffer(
1484 			GENERIC_WRITE | GENERIC_READ,
1485 			FILE_SHARE_WRITE | FILE_SHARE_READ,
1486 			(LPSECURITY_ATTRIBUTES) NULL,
1487 			CONSOLE_TEXTMODE_BUFFER,
1488 			(LPVOID) NULL);
1489 	}
1490 
1491 	size.X = scr.srWindow.Right - scr.srWindow.Left + 1;
1492 	size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1;
1493 	SetConsoleScreenBufferSize(con_out_ours, size);
1494 	SetConsoleActiveScreenBuffer(con_out_ours);
1495 	con_out = con_out_ours;
1496 }
1497 
1498 /*
1499  * Restore the startup console.
1500  */
1501 static void
1502 win32_deinit_term()
1503 {
1504 	if (con_out_save == INVALID_HANDLE_VALUE)
1505 		return;
1506 	if (quitting)
1507 		(void) CloseHandle(con_out_ours);
1508 	SetConsoleActiveScreenBuffer(con_out_save);
1509 	con_out = con_out_save;
1510 }
1511 
1512 #endif
1513 
1514 /*
1515  * Initialize terminal
1516  */
1517 	public void
1518 init()
1519 {
1520 #if !MSDOS_COMPILER
1521 	if (!no_init)
1522 		tputs(sc_init, sc_height, putchr);
1523 	if (!no_keypad)
1524 		tputs(sc_s_keypad, sc_height, putchr);
1525 	if (top_scroll)
1526 	{
1527 		int i;
1528 
1529 		/*
1530 		 * This is nice to terminals with no alternate screen,
1531 		 * but with saved scrolled-off-the-top lines.  This way,
1532 		 * no previous line is lost, but we start with a whole
1533 		 * screen to ourself.
1534 		 */
1535 		for (i = 1; i < sc_height; i++)
1536 			putchr('\n');
1537 	}
1538 #else
1539 #if MSDOS_COMPILER==WIN32C
1540 	if (!no_init)
1541 		win32_init_term();
1542 #endif
1543 	initcolor();
1544 	flush();
1545 #endif
1546 	init_done = 1;
1547 }
1548 
1549 /*
1550  * Deinitialize terminal
1551  */
1552 	public void
1553 deinit()
1554 {
1555 	if (!init_done)
1556 		return;
1557 #if !MSDOS_COMPILER
1558 	if (!no_keypad)
1559 		tputs(sc_e_keypad, sc_height, putchr);
1560 	if (!no_init)
1561 		tputs(sc_deinit, sc_height, putchr);
1562 #else
1563 	/* Restore system colors. */
1564 	SETCOLORS(sy_fg_color, sy_bg_color);
1565 #if MSDOS_COMPILER==WIN32C
1566 	if (!no_init)
1567 		win32_deinit_term();
1568 #else
1569 	/* Need clreol to make SETCOLORS take effect. */
1570 	clreol();
1571 #endif
1572 #endif
1573 	init_done = 0;
1574 }
1575 
1576 /*
1577  * Home cursor (move to upper left corner of screen).
1578  */
1579 	public void
1580 home()
1581 {
1582 #if !MSDOS_COMPILER
1583 	tputs(sc_home, 1, putchr);
1584 #else
1585 	flush();
1586 	_settextposition(1,1);
1587 #endif
1588 }
1589 
1590 /*
1591  * Add a blank line (called with cursor at home).
1592  * Should scroll the display down.
1593  */
1594 	public void
1595 add_line()
1596 {
1597 #if !MSDOS_COMPILER
1598 	tputs(sc_addline, sc_height, putchr);
1599 #else
1600 	flush();
1601 #if MSDOS_COMPILER==MSOFTC
1602 	_scrolltextwindow(_GSCROLLDOWN);
1603 	_settextposition(1,1);
1604 #else
1605 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
1606 	movetext(1,1, sc_width,sc_height-1, 1,2);
1607 	gotoxy(1,1);
1608 	clreol();
1609 #else
1610 #if MSDOS_COMPILER==WIN32C
1611     {
1612 	CHAR_INFO fillchar;
1613 	SMALL_RECT rcSrc, rcClip;
1614 	COORD new_org;
1615 	CONSOLE_SCREEN_BUFFER_INFO csbi;
1616 
1617 	GetConsoleScreenBufferInfo(con_out,&csbi);
1618 
1619 	/* The clip rectangle is the entire visible screen. */
1620 	rcClip.Left = csbi.srWindow.Left;
1621 	rcClip.Top = csbi.srWindow.Top;
1622 	rcClip.Right = csbi.srWindow.Right;
1623 	rcClip.Bottom = csbi.srWindow.Bottom;
1624 
1625 	/* The source rectangle is the visible screen minus the last line. */
1626 	rcSrc = rcClip;
1627 	rcSrc.Bottom--;
1628 
1629 	/* Move the top left corner of the source window down one row. */
1630 	new_org.X = rcSrc.Left;
1631 	new_org.Y = rcSrc.Top + 1;
1632 
1633 	/* Fill the right character and attributes. */
1634 	fillchar.Char.AsciiChar = ' ';
1635 	curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
1636 	fillchar.Attributes = curr_attr;
1637 	ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar);
1638 	_settextposition(1,1);
1639     }
1640 #endif
1641 #endif
1642 #endif
1643 #endif
1644 }
1645 
1646 #if 0
1647 /*
1648  * Remove the n topmost lines and scroll everything below it in the
1649  * window upward.  This is needed to stop leaking the topmost line
1650  * into the scrollback buffer when we go down-one-line (in WIN32).
1651  */
1652 	public void
1653 remove_top(n)
1654 	int n;
1655 {
1656 #if MSDOS_COMPILER==WIN32C
1657 	SMALL_RECT rcSrc, rcClip;
1658 	CHAR_INFO fillchar;
1659 	COORD new_org;
1660 	CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */
1661 
1662 	if (n >= sc_height - 1)
1663 	{
1664 		clear();
1665 		home();
1666 		return;
1667 	}
1668 
1669 	flush();
1670 
1671 	GetConsoleScreenBufferInfo(con_out, &csbi);
1672 
1673 	/* Get the extent of all-visible-rows-but-the-last. */
1674 	rcSrc.Left    = csbi.srWindow.Left;
1675 	rcSrc.Top     = csbi.srWindow.Top + n;
1676 	rcSrc.Right   = csbi.srWindow.Right;
1677 	rcSrc.Bottom  = csbi.srWindow.Bottom;
1678 
1679 	/* Get the clip rectangle. */
1680 	rcClip.Left   = rcSrc.Left;
1681 	rcClip.Top    = csbi.srWindow.Top;
1682 	rcClip.Right  = rcSrc.Right;
1683 	rcClip.Bottom = rcSrc.Bottom ;
1684 
1685 	/* Move the source window up n rows. */
1686 	new_org.X = rcSrc.Left;
1687 	new_org.Y = rcSrc.Top - n;
1688 
1689 	/* Fill the right character and attributes. */
1690 	fillchar.Char.AsciiChar = ' ';
1691 	curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
1692 	fillchar.Attributes = curr_attr;
1693 
1694 	ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar);
1695 
1696 	/* Position cursor on first blank line. */
1697 	goto_line(sc_height - n - 1);
1698 #endif
1699 }
1700 #endif
1701 
1702 #if MSDOS_COMPILER==WIN32C
1703 /*
1704  * Clear the screen.
1705  */
1706 	static void
1707 win32_clear()
1708 {
1709 	/*
1710 	 * This will clear only the currently visible rows of the NT
1711 	 * console buffer, which means none of the precious scrollback
1712 	 * rows are touched making for faster scrolling.  Note that, if
1713 	 * the window has fewer columns than the console buffer (i.e.
1714 	 * there is a horizontal scrollbar as well), the entire width
1715 	 * of the visible rows will be cleared.
1716 	 */
1717 	COORD topleft;
1718 	DWORD nchars;
1719 	DWORD winsz;
1720 	CONSOLE_SCREEN_BUFFER_INFO csbi;
1721 
1722 	/* get the number of cells in the current buffer */
1723 	GetConsoleScreenBufferInfo(con_out, &csbi);
1724 	winsz = csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1);
1725 	topleft.X = 0;
1726 	topleft.Y = csbi.srWindow.Top;
1727 
1728 	curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
1729 	FillConsoleOutputCharacter(con_out, ' ', winsz, topleft, &nchars);
1730 	FillConsoleOutputAttribute(con_out, curr_attr, winsz, topleft, &nchars);
1731 }
1732 
1733 /*
1734  * Remove the n topmost lines and scroll everything below it in the
1735  * window upward.
1736  */
1737 	public void
1738 win32_scroll_up(n)
1739 	int n;
1740 {
1741 	SMALL_RECT rcSrc, rcClip;
1742 	CHAR_INFO fillchar;
1743 	COORD topleft;
1744 	COORD new_org;
1745 	DWORD nchars;
1746 	DWORD size;
1747 	CONSOLE_SCREEN_BUFFER_INFO csbi;
1748 
1749 	if (n <= 0)
1750 		return;
1751 
1752 	if (n >= sc_height - 1)
1753 	{
1754 		win32_clear();
1755 		_settextposition(1,1);
1756 		return;
1757 	}
1758 
1759 	/* Get the extent of what will remain visible after scrolling. */
1760 	GetConsoleScreenBufferInfo(con_out, &csbi);
1761 	rcSrc.Left    = csbi.srWindow.Left;
1762 	rcSrc.Top     = csbi.srWindow.Top + n;
1763 	rcSrc.Right   = csbi.srWindow.Right;
1764 	rcSrc.Bottom  = csbi.srWindow.Bottom;
1765 
1766 	/* Get the clip rectangle. */
1767 	rcClip.Left   = rcSrc.Left;
1768 	rcClip.Top    = csbi.srWindow.Top;
1769 	rcClip.Right  = rcSrc.Right;
1770 	rcClip.Bottom = rcSrc.Bottom ;
1771 
1772 	/* Move the source text to the top of the screen. */
1773 	new_org.X = rcSrc.Left;
1774 	new_org.Y = 0;
1775 
1776 	/* Fill the right character and attributes. */
1777 	fillchar.Char.AsciiChar = ' ';
1778 	fillchar.Attributes = MAKEATTR(nm_fg_color, nm_bg_color);
1779 
1780 	/* Scroll the window. */
1781 	SetConsoleTextAttribute(con_out, fillchar.Attributes);
1782 	ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar);
1783 
1784 	/* Clear remaining lines at bottom. */
1785 	topleft.X = csbi.dwCursorPosition.X;
1786 	topleft.Y = rcSrc.Bottom - n;
1787 	size = (n * csbi.dwSize.X) + (rcSrc.Right - topleft.X);
1788 	FillConsoleOutputCharacter(con_out, ' ', size, topleft,
1789 		&nchars);
1790 	FillConsoleOutputAttribute(con_out, fillchar.Attributes, size, topleft,
1791 		&nchars);
1792 	SetConsoleTextAttribute(con_out, curr_attr);
1793 
1794 	/* Move cursor n lines up from where it was. */
1795 	csbi.dwCursorPosition.Y -= n;
1796 	SetConsoleCursorPosition(con_out, csbi.dwCursorPosition);
1797 }
1798 #endif
1799 
1800 /*
1801  * Move cursor to lower left corner of screen.
1802  */
1803 	public void
1804 lower_left()
1805 {
1806 #if !MSDOS_COMPILER
1807 	tputs(sc_lower_left, 1, putchr);
1808 #else
1809 	flush();
1810 	_settextposition(sc_height, 1);
1811 #endif
1812 }
1813 
1814 /*
1815  * Check if the console size has changed and reset internals
1816  * (in lieu of SIGWINCH for WIN32).
1817  */
1818 	public void
1819 check_winch()
1820 {
1821 #if MSDOS_COMPILER==WIN32C
1822 	CONSOLE_SCREEN_BUFFER_INFO scr;
1823 	COORD size;
1824 
1825 	if (con_out == INVALID_HANDLE_VALUE)
1826 		return;
1827 
1828 	flush();
1829 	GetConsoleScreenBufferInfo(con_out, &scr);
1830 	size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1;
1831 	size.X = scr.srWindow.Right - scr.srWindow.Left + 1;
1832 	if (size.Y != sc_height || size.X != sc_width)
1833 	{
1834 		sc_height = size.Y;
1835 		sc_width = size.X;
1836 		if (!no_init && con_out_ours == con_out)
1837 			SetConsoleScreenBufferSize(con_out, size);
1838 		pos_init();
1839 		wscroll = (sc_height + 1) / 2;
1840 		screen_trashed = 1;
1841 	}
1842 #endif
1843 }
1844 
1845 /*
1846  * Goto a specific line on the screen.
1847  */
1848 	public void
1849 goto_line(slinenum)
1850 	int slinenum;
1851 {
1852 #if !MSDOS_COMPILER
1853 	tputs(tgoto(sc_move, 0, slinenum), 1, putchr);
1854 #else
1855 	flush();
1856 	_settextposition(slinenum+1, 1);
1857 #endif
1858 }
1859 
1860 #if MSDOS_COMPILER==MSOFTC || MSDOS_COMPILER==BORLANDC
1861 /*
1862  * Create an alternate screen which is all white.
1863  * This screen is used to create a "flash" effect, by displaying it
1864  * briefly and then switching back to the normal screen.
1865  * {{ Yuck!  There must be a better way to get a visual bell. }}
1866  */
1867 	static void
1868 create_flash()
1869 {
1870 #if MSDOS_COMPILER==MSOFTC
1871 	struct videoconfig w;
1872 	char *blanks;
1873 	int row, col;
1874 
1875 	_getvideoconfig(&w);
1876 	videopages = w.numvideopages;
1877 	if (videopages < 2)
1878 	{
1879 		at_enter(AT_STANDOUT);
1880 		at_exit();
1881 	} else
1882 	{
1883 		_setactivepage(1);
1884 		at_enter(AT_STANDOUT);
1885 		blanks = (char *) ecalloc(w.numtextcols, sizeof(char));
1886 		for (col = 0;  col < w.numtextcols;  col++)
1887 			blanks[col] = ' ';
1888 		for (row = w.numtextrows;  row > 0;  row--)
1889 			_outmem(blanks, w.numtextcols);
1890 		_setactivepage(0);
1891 		_setvisualpage(0);
1892 		free(blanks);
1893 		at_exit();
1894 	}
1895 #else
1896 #if MSDOS_COMPILER==BORLANDC
1897 	register int n;
1898 
1899 	whitescreen = (unsigned short *)
1900 		malloc(sc_width * sc_height * sizeof(short));
1901 	if (whitescreen == NULL)
1902 		return;
1903 	for (n = 0;  n < sc_width * sc_height;  n++)
1904 		whitescreen[n] = 0x7020;
1905 #else
1906 #if MSDOS_COMPILER==WIN32C
1907 	register int n;
1908 
1909 	whitescreen = (WORD *)
1910 		malloc(sc_height * sc_width * sizeof(WORD));
1911 	if (whitescreen == NULL)
1912 		return;
1913 	/* Invert the standard colors. */
1914 	for (n = 0;  n < sc_width * sc_height;  n++)
1915 		whitescreen[n] = (WORD)((nm_fg_color << 4) | nm_bg_color);
1916 #endif
1917 #endif
1918 #endif
1919 	flash_created = 1;
1920 }
1921 #endif /* MSDOS_COMPILER */
1922 
1923 /*
1924  * Output the "visual bell", if there is one.
1925  */
1926 	public void
1927 vbell()
1928 {
1929 #if !MSDOS_COMPILER
1930 	if (*sc_visual_bell == '\0')
1931 		return;
1932 	tputs(sc_visual_bell, sc_height, putchr);
1933 #else
1934 #if MSDOS_COMPILER==DJGPPC
1935 	ScreenVisualBell();
1936 #else
1937 #if MSDOS_COMPILER==MSOFTC
1938 	/*
1939 	 * Create a flash screen on the second video page.
1940 	 * Switch to that page, then switch back.
1941 	 */
1942 	if (!flash_created)
1943 		create_flash();
1944 	if (videopages < 2)
1945 		return;
1946 	_setvisualpage(1);
1947 	delay(100);
1948 	_setvisualpage(0);
1949 #else
1950 #if MSDOS_COMPILER==BORLANDC
1951 	unsigned short *currscreen;
1952 
1953 	/*
1954 	 * Get a copy of the current screen.
1955 	 * Display the flash screen.
1956 	 * Then restore the old screen.
1957 	 */
1958 	if (!flash_created)
1959 		create_flash();
1960 	if (whitescreen == NULL)
1961 		return;
1962 	currscreen = (unsigned short *)
1963 		malloc(sc_width * sc_height * sizeof(short));
1964 	if (currscreen == NULL) return;
1965 	gettext(1, 1, sc_width, sc_height, currscreen);
1966 	puttext(1, 1, sc_width, sc_height, whitescreen);
1967 	delay(100);
1968 	puttext(1, 1, sc_width, sc_height, currscreen);
1969 	free(currscreen);
1970 #else
1971 #if MSDOS_COMPILER==WIN32C
1972 	/* paint screen with an inverse color */
1973 	clear();
1974 
1975 	/* leave it displayed for 100 msec. */
1976 	Sleep(100);
1977 
1978 	/* restore with a redraw */
1979 	repaint();
1980 #endif
1981 #endif
1982 #endif
1983 #endif
1984 #endif
1985 }
1986 
1987 /*
1988  * Make a noise.
1989  */
1990 	static void
1991 beep()
1992 {
1993 #if !MSDOS_COMPILER
1994 	putchr(CONTROL('G'));
1995 #else
1996 #if MSDOS_COMPILER==WIN32C
1997 	MessageBeep(0);
1998 #else
1999 	write(1, "\7", 1);
2000 #endif
2001 #endif
2002 }
2003 
2004 /*
2005  * Ring the terminal bell.
2006  */
2007 	public void
2008 bell()
2009 {
2010 	if (quiet == VERY_QUIET)
2011 		vbell();
2012 	else
2013 		beep();
2014 }
2015 
2016 /*
2017  * Clear the screen.
2018  */
2019 	public void
2020 clear()
2021 {
2022 #if !MSDOS_COMPILER
2023 	tputs(sc_clear, sc_height, putchr);
2024 #else
2025 	flush();
2026 #if MSDOS_COMPILER==WIN32C
2027 	win32_clear();
2028 #else
2029 	_clearscreen(_GCLEARSCREEN);
2030 #endif
2031 #endif
2032 }
2033 
2034 /*
2035  * Clear from the cursor to the end of the cursor's line.
2036  * {{ This must not move the cursor. }}
2037  */
2038 	public void
2039 clear_eol()
2040 {
2041 #if !MSDOS_COMPILER
2042 	tputs(sc_eol_clear, 1, putchr);
2043 #else
2044 #if MSDOS_COMPILER==MSOFTC
2045 	short top, left;
2046 	short bot, right;
2047 	struct rccoord tpos;
2048 
2049 	flush();
2050 	/*
2051 	 * Save current state.
2052 	 */
2053 	tpos = _gettextposition();
2054 	_gettextwindow(&top, &left, &bot, &right);
2055 	/*
2056 	 * Set a temporary window to the current line,
2057 	 * from the cursor's position to the right edge of the screen.
2058 	 * Then clear that window.
2059 	 */
2060 	_settextwindow(tpos.row, tpos.col, tpos.row, sc_width);
2061 	_clearscreen(_GWINDOW);
2062 	/*
2063 	 * Restore state.
2064 	 */
2065 	_settextwindow(top, left, bot, right);
2066 	_settextposition(tpos.row, tpos.col);
2067 #else
2068 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
2069 	flush();
2070 	clreol();
2071 #else
2072 #if MSDOS_COMPILER==WIN32C
2073 	DWORD           nchars;
2074 	COORD           cpos;
2075 	CONSOLE_SCREEN_BUFFER_INFO scr;
2076 
2077 	flush();
2078 	memset(&scr, 0, sizeof(scr));
2079 	GetConsoleScreenBufferInfo(con_out, &scr);
2080 	cpos.X = scr.dwCursorPosition.X;
2081 	cpos.Y = scr.dwCursorPosition.Y;
2082 	curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
2083 	FillConsoleOutputAttribute(con_out, curr_attr,
2084 		scr.dwSize.X - cpos.X, cpos, &nchars);
2085 	FillConsoleOutputCharacter(con_out, ' ',
2086 		scr.dwSize.X - cpos.X, cpos, &nchars);
2087 #endif
2088 #endif
2089 #endif
2090 #endif
2091 }
2092 
2093 /*
2094  * Clear the current line.
2095  * Clear the screen if there's off-screen memory below the display.
2096  */
2097 	static void
2098 clear_eol_bot()
2099 {
2100 #if MSDOS_COMPILER
2101 	clear_eol();
2102 #else
2103 	if (below_mem)
2104 		tputs(sc_eos_clear, 1, putchr);
2105 	else
2106 		tputs(sc_eol_clear, 1, putchr);
2107 #endif
2108 }
2109 
2110 /*
2111  * Clear the bottom line of the display.
2112  * Leave the cursor at the beginning of the bottom line.
2113  */
2114 	public void
2115 clear_bot()
2116 {
2117 	/*
2118 	 * If we're in a non-normal attribute mode, temporarily exit
2119 	 * the mode while we do the clear.  Some terminals fill the
2120 	 * cleared area with the current attribute.
2121 	 */
2122 	lower_left();
2123 	if (attrmode == AT_NORMAL)
2124 		clear_eol_bot();
2125 	else
2126 	{
2127 		int saved_attrmode = attrmode;
2128 
2129 		at_exit();
2130 		clear_eol_bot();
2131 		at_enter(saved_attrmode);
2132 	}
2133 }
2134 
2135 	public void
2136 at_enter(attr)
2137 	int attr;
2138 {
2139 	attr = apply_at_specials(attr);
2140 
2141 #if !MSDOS_COMPILER
2142 	/* The one with the most priority is last.  */
2143 	if (attr & AT_UNDERLINE)
2144 		tputs(sc_u_in, 1, putchr);
2145 	if (attr & AT_BOLD)
2146 		tputs(sc_b_in, 1, putchr);
2147 	if (attr & AT_BLINK)
2148 		tputs(sc_bl_in, 1, putchr);
2149 	if (attr & AT_STANDOUT)
2150 		tputs(sc_s_in, 1, putchr);
2151 #else
2152 	flush();
2153 	/* The one with the most priority is first.  */
2154 	if (attr & AT_STANDOUT)
2155 	{
2156 		SETCOLORS(so_fg_color, so_bg_color);
2157 	} else if (attr & AT_BLINK)
2158 	{
2159 		SETCOLORS(bl_fg_color, bl_bg_color);
2160 	}
2161 	else if (attr & AT_BOLD)
2162 	{
2163 		SETCOLORS(bo_fg_color, bo_bg_color);
2164 	}
2165 	else if (attr & AT_UNDERLINE)
2166 	{
2167 		SETCOLORS(ul_fg_color, ul_bg_color);
2168 	}
2169 #endif
2170 
2171 	attrmode = attr;
2172 }
2173 
2174 	public void
2175 at_exit()
2176 {
2177 #if !MSDOS_COMPILER
2178 	/* Undo things in the reverse order we did them.  */
2179 	if (attrmode & AT_STANDOUT)
2180 		tputs(sc_s_out, 1, putchr);
2181 	if (attrmode & AT_BLINK)
2182 		tputs(sc_bl_out, 1, putchr);
2183 	if (attrmode & AT_BOLD)
2184 		tputs(sc_b_out, 1, putchr);
2185 	if (attrmode & AT_UNDERLINE)
2186 		tputs(sc_u_out, 1, putchr);
2187 #else
2188 	flush();
2189 	SETCOLORS(nm_fg_color, nm_bg_color);
2190 #endif
2191 
2192 	attrmode = AT_NORMAL;
2193 }
2194 
2195 	public void
2196 at_switch(attr)
2197 	int attr;
2198 {
2199 	if (apply_at_specials(attr) != attrmode)
2200 	{
2201 		at_exit();
2202 		at_enter(attr);
2203 	}
2204 }
2205 
2206 	public int
2207 is_at_equiv(attr1, attr2)
2208 	int attr1;
2209 	int attr2;
2210 {
2211 	attr1 = apply_at_specials(attr1);
2212 	attr2 = apply_at_specials(attr2);
2213 
2214 	return (attr1 == attr2);
2215 }
2216 
2217 	public int
2218 apply_at_specials(attr)
2219 	int attr;
2220 {
2221 	if (attr & AT_BINARY)
2222 		attr |= binattr;
2223 	if (attr & AT_HILITE)
2224 		attr |= AT_STANDOUT;
2225 	attr &= ~(AT_BINARY|AT_HILITE);
2226 
2227 	return attr;
2228 }
2229 
2230 #if 0 /* No longer used */
2231 /*
2232  * Erase the character to the left of the cursor
2233  * and move the cursor left.
2234  */
2235 	public void
2236 backspace()
2237 {
2238 #if !MSDOS_COMPILER
2239 	/*
2240 	 * Erase the previous character by overstriking with a space.
2241 	 */
2242 	tputs(sc_backspace, 1, putchr);
2243 	putchr(' ');
2244 	tputs(sc_backspace, 1, putchr);
2245 #else
2246 #if MSDOS_COMPILER==MSOFTC
2247 	struct rccoord tpos;
2248 
2249 	flush();
2250 	tpos = _gettextposition();
2251 	if (tpos.col <= 1)
2252 		return;
2253 	_settextposition(tpos.row, tpos.col-1);
2254 	_outtext(" ");
2255 	_settextposition(tpos.row, tpos.col-1);
2256 #else
2257 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
2258 	cputs("\b");
2259 #else
2260 #if MSDOS_COMPILER==WIN32C
2261 	COORD cpos;
2262 	DWORD cChars;
2263 	CONSOLE_SCREEN_BUFFER_INFO scr;
2264 
2265 	flush();
2266 	GetConsoleScreenBufferInfo(con_out, &scr);
2267 	cpos = scr.dwCursorPosition;
2268 	if (cpos.X <= 0)
2269 		return;
2270 	cpos.X--;
2271 	SetConsoleCursorPosition(con_out, cpos);
2272 	FillConsoleOutputCharacter(con_out, (TCHAR)' ', 1, cpos, &cChars);
2273 	SetConsoleCursorPosition(con_out, cpos);
2274 #endif
2275 #endif
2276 #endif
2277 #endif
2278 }
2279 #endif /* 0 */
2280 
2281 /*
2282  * Output a plain backspace, without erasing the previous char.
2283  */
2284 	public void
2285 putbs()
2286 {
2287 #if !MSDOS_COMPILER
2288 	tputs(sc_backspace, 1, putchr);
2289 #else
2290 	int row, col;
2291 
2292 	flush();
2293 	{
2294 #if MSDOS_COMPILER==MSOFTC
2295 		struct rccoord tpos;
2296 		tpos = _gettextposition();
2297 		row = tpos.row;
2298 		col = tpos.col;
2299 #else
2300 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
2301 		row = wherey();
2302 		col = wherex();
2303 #else
2304 #if MSDOS_COMPILER==WIN32C
2305 		CONSOLE_SCREEN_BUFFER_INFO scr;
2306 		GetConsoleScreenBufferInfo(con_out, &scr);
2307 		row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1;
2308 		col = scr.dwCursorPosition.X - scr.srWindow.Left + 1;
2309 #endif
2310 #endif
2311 #endif
2312 	}
2313 	if (col <= 1)
2314 		return;
2315 	_settextposition(row, col-1);
2316 #endif /* MSDOS_COMPILER */
2317 }
2318 
2319 #if MSDOS_COMPILER==WIN32C
2320 /*
2321  * Determine whether an input character is waiting to be read.
2322  */
2323 	static int
2324 win32_kbhit(tty)
2325 	HANDLE tty;
2326 {
2327 	INPUT_RECORD ip;
2328 	DWORD read;
2329 
2330 	if (keyCount > 0)
2331 		return (TRUE);
2332 
2333 	currentKey.ascii = 0;
2334 	currentKey.scan = 0;
2335 
2336 	/*
2337 	 * Wait for a real key-down event, but
2338 	 * ignore SHIFT and CONTROL key events.
2339 	 */
2340 	do
2341 	{
2342 		PeekConsoleInput(tty, &ip, 1, &read);
2343 		if (read == 0)
2344 			return (FALSE);
2345 		ReadConsoleInput(tty, &ip, 1, &read);
2346 	} while (ip.EventType != KEY_EVENT ||
2347 		ip.Event.KeyEvent.bKeyDown != TRUE ||
2348 		ip.Event.KeyEvent.wVirtualScanCode == 0 ||
2349 		ip.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT ||
2350 		ip.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL ||
2351 		ip.Event.KeyEvent.wVirtualKeyCode == VK_MENU);
2352 
2353 	currentKey.ascii = ip.Event.KeyEvent.uChar.AsciiChar;
2354 	currentKey.scan = ip.Event.KeyEvent.wVirtualScanCode;
2355 	keyCount = ip.Event.KeyEvent.wRepeatCount;
2356 
2357 	if (ip.Event.KeyEvent.dwControlKeyState &
2358 		(LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
2359 	{
2360 		switch (currentKey.scan)
2361 		{
2362 		case PCK_ALT_E:     /* letter 'E' */
2363 			currentKey.ascii = 0;
2364 			break;
2365 		}
2366 	} else if (ip.Event.KeyEvent.dwControlKeyState &
2367 		(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
2368 	{
2369 		switch (currentKey.scan)
2370 		{
2371 		case PCK_RIGHT: /* right arrow */
2372 			currentKey.scan = PCK_CTL_RIGHT;
2373 			break;
2374 		case PCK_LEFT: /* left arrow */
2375 			currentKey.scan = PCK_CTL_LEFT;
2376 			break;
2377 		case PCK_DELETE: /* delete */
2378 			currentKey.scan = PCK_CTL_DELETE;
2379 			break;
2380 		}
2381 	}
2382 	return (TRUE);
2383 }
2384 
2385 /*
2386  * Read a character from the keyboard.
2387  */
2388 	public char
2389 WIN32getch(tty)
2390 	int tty;
2391 {
2392 	int ascii;
2393 
2394 	if (pending_scancode)
2395 	{
2396 		pending_scancode = 0;
2397 		return ((char)(currentKey.scan & 0x00FF));
2398 	}
2399 
2400 	while (win32_kbhit((HANDLE)tty) == FALSE)
2401 	{
2402 		Sleep(20);
2403 		if (ABORT_SIGS())
2404 			return ('\003');
2405 		continue;
2406 	}
2407 	keyCount --;
2408 	ascii = currentKey.ascii;
2409 	/*
2410 	 * On PC's, the extended keys return a 2 byte sequence beginning
2411 	 * with '00', so if the ascii code is 00, the next byte will be
2412 	 * the lsb of the scan code.
2413 	 */
2414 	pending_scancode = (ascii == 0x00);
2415 	return ((char)ascii);
2416 }
2417 #endif
2418