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