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