/*======================================================================*\
|*		Editor mined						*|
|*		operating system dependent I/O				*|
\*======================================================================*/

#include "mined.h"
#include "io.h"

#ifdef ANSI
#undef conio
#endif


#include <errno.h>
#include <signal.h>

/* include configuration for SIGWINCH information retrieval */
#ifdef TERMIO
#define include_TERMIO
#endif
#ifdef SGTTY
#define include_SGTTY
#endif


/* terminal handling selection */
#ifdef CURSES
#include <curses.h>
# undef FALSE
# undef TRUE

#ifdef SETLOCALE
#include <locale.h>
#endif

/* ensure window size detection capability */
# ifndef SGTTY
# define include_TERMIO
# endif

# undef TERMIO	/* \ must be  */
# undef SGTTY	/* / disabled */
#endif


#ifdef include_TERMIO
# ifdef questionable_but_recommended_by_some_SuSe_Guru_doesnt_work_on_HP
#  if defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1))
#	include <termios.h>		/* define POSIX.1 termios */
#	include <sys/ioctl.h>		/* declare ioctl() for winsize */
#	undef   TCGETS		/* Use POSIX.1 instead */
#  else
/* Compatible <termio.h> for old 'struct termio' ioctl interface. */
#	include <termio.h>
#  endif
# else
#	include <termios.h>		/* define POSIX.1 termios */
#	include <sys/ioctl.h>		/* declare ioctl() for winsize */
# endif
#endif

#ifdef include_SGTTY
#include <sys/ioctl.h>
#include <sgtty.h>
/* #include <sys/ttold.h> */
/*extern int ioctl ();*/
#endif


#ifdef __CYGWIN__
#include <sys/ioctl.h>
#endif

#ifdef SIGPHONE		/* this trick was taken from less' screen.c */
#include <sys/window.h>	/* for window size detection */
#endif

#ifdef msdos
#define _getch_
#include <dos.h>
#endif

#ifdef unix
# undef _POSIX_SOURCE
# undef _XOPEN_SOURCE
#define selectread	/* use select () ? */
#endif
#ifndef __TURBOC__
#include <sys/time.h>	/* for struct timeval (for select in inputreadyafter) */
#endif

#ifdef vms
#include <socket.h>	/* for select () and struct timeval */
# ifdef CURSES
# define _getch_
# endif
#endif

#ifdef __EMX__
#undef selectread
# ifdef CURSES
# define _getch_
# endif
#endif

#ifdef CURSES_INPUT	/* currently not defined */
#define _getch_	/* not necessarily needed for Unix, but might be better 
		   with new curs_readchar loop in inputreadyafter () */
#endif

#ifdef _getch_
#ifndef CURSES
extern int getch ();
#endif
#endif


#ifdef msdos

#ifndef __TURBOC__
#undef unix
/* don't #define ANSI */
#endif

#ifndef conio
#define ANSI
#endif

#endif


/*======================================================================*\
|*			Mode indication					*|
\*======================================================================*/

/* terminal capabilites */
FLAG can_scroll_reverse = True;
FLAG can_add_line = True;
FLAG can_delete_line = True;
FLAG can_clear_eol = True;
FLAG can_alt_cset = True;
FLAG mouse_hilite_tracking = False;
FLAG mouse_button_event_tracking = True;
FLAG colours_256 = False;
FLAG colours_88 = False;
FLAG ansi_esc = True;
FLAG standout_glitch = False;

/* menu border style */
FLAG use_ascii_graphics = False;	/* use ASCII graphics for borders */
FLAG use_vga_block_graphics = False;	/* charset is VGA with block graphics */
FLAG use_pc_block_graphics = False;	/* alternate charset is VGA with block graphics */
FLAG use_vt100_block_graphics = False;
char menu_border_style = 'r';
int menumargin = 0;
FLAG explicit_border_style = False;
FLAG use_stylish_menu_selection = False;
#define use_normalchars_boxdrawing ((utf8_screen || use_ascii_graphics) && ! use_vt100_block_graphics && ! use_vga_block_graphics)

FLAG
use_unicode_menubar ()
{
  return use_stylish_menu_selection
      && use_normalchars_boxdrawing
      && ! use_ascii_graphics && menu_border_style != 'd';
}

/* feature usage options */
FLAG use_mouse = True;
FLAG use_bold = True;
FLAG use_bgcolor = True;

/* state */
FLAG in_menu_mouse_mode = False;
FLAG in_menu_border = False;
int current_cursor_y = 0;


/*======================================================================*\
|*			Unix signalling routines			*|
\*======================================================================*/

void
catch_signals (catchfunc)
  signalfunc catchfunc;
{
#ifdef SIGHUP
  signal (SIGHUP, catchfunc);
#endif
#ifdef SIGILL
  signal (SIGILL, catchfunc);
#endif
#ifdef SIGTRAP
  signal (SIGTRAP, catchfunc);
#endif
#ifdef SIGABRT
  signal (SIGABRT, catchfunc);
#endif
#ifdef SIGEMT
  signal (SIGEMT, catchfunc);
#endif
#ifdef SIGFPE
  signal (SIGFPE, catchfunc);
#endif
#ifdef SIGBUS
  signal (SIGBUS, catchfunc);
#endif
#ifdef SIGSEGV
  signal (SIGSEGV, catchfunc);
#endif
#ifdef SIGSYS
  signal (SIGSYS, catchfunc);
#endif
#ifdef SIGPIPE
  signal (SIGPIPE, catchfunc);
#endif
#ifdef SIGALRM
  signal (SIGALRM, catchfunc);
#endif
#ifdef SIGTERM
  signal (SIGTERM, catchfunc);
#endif
#ifdef SIGXCPU
  signal (SIGXCPU, catchfunc);
#endif
#ifdef SIGXFSZ
  signal (SIGXFSZ, catchfunc);
#endif
#ifdef SIGVTALRM
  signal (SIGVTALRM, catchfunc);
#endif
#ifdef SIGPROF
  signal (SIGPROF, catchfunc);
#endif
#ifdef SIGLOST
  signal (SIGLOST, catchfunc);
#endif
#ifdef SIGUSR1
  signal (SIGUSR1, catchfunc);
#endif
#ifdef SIGUSR2
  signal (SIGUSR2, catchfunc);
#endif
#ifdef SIGUSR3
  signal (SIGUSR3, catchfunc);
#endif
}

#ifdef SIGTSTP
FLAG cansuspendmyself = True;
#else
FLAG cansuspendmyself = False;
#endif

void
suspendmyself ()
{
#ifdef SIGTSTP
  kill (getpid (), SIGTSTP);
#endif
}

#ifdef SIGWINCH
/*
 * Catch the SIGWINCH signal sent to mined.
 */
static
void
catchwinch (dummy)
  int dummy;
{
  winchg = True;
  interrupted = True;

/* This is now performed in __readchar () to prevent display garbage 
   in case this interrupts occurs during display output operations which 
   get screen size related values changed while relying on them.
  if (waitingforinput) {
	RDwin ();
  }
*/

  signal (SIGWINCH, catchwinch); /* Re-installation of the signal */
  kill (getppid (), SIGWINCH);	/* propagate to parent (e.g. shell) */
}
#endif

#ifdef SIGQUIT
/*
 * Catch the SIGQUIT signal (^\) sent to mined. It turns on the quitflag.
 */
static
void
catchquit (dummy)
  int dummy;
{
#ifdef UNUSED /* Should not be needed with new __readchar () */
/* Was previously needed on Sun but showed bad effects on Iris. */
  static char quitchar = '\0';
  if (waitingforinput) {
	/* simulate input to enable immediate break also during input */
	(void) ioctl (input_fd, TIOCSTI, & quitchar);
  }
#endif
  quit = True;
  signal (SIGQUIT, catchquit); /* Re-installation of the signal */
}
#endif

#ifdef SIGBREAK
/*
 * Catch the SIGBREAK signal (control-Break) and turn on the quitflag.
 */
static
void
catchbreak (dummy)
  int dummy;
{
  quit = True;
  signal (SIGBREAK, catchbreak); /* do we need this ? */
}
#else
# ifdef msdos
static
int
controlbreak ()
{
  quit = True;
  return 1; /* continue program execution */
}
# endif
#endif

#ifdef SIGINT
/*
 * Catch the SIGINT signal (^C) sent if it cannot be ignored by tty driver
 */
static
void
catchint (dummy)
  int dummy;
{
  intr_char = True;
  signal (SIGINT, catchint); /* Re-installation of the signal */
}
#endif


/*======================================================================*\
|*			Terminal mode switcher				*|
\*======================================================================*/

static void start_screen_mode _((void));
static void end_screen_mode _((void));

#ifdef TERMIO
static FLAG old_termprops_not_saved = True;
#endif
#ifdef SGTTY
static FLAG old_termprops_not_saved = True;
#endif

/*
 * Set and reset tty into CBREAK or old mode according to argument 'state'.
 * It also sets all signal characters (except for ^\) to UNDEF. ^\ is caught.
 */
void
raw_mode (to_raw_state)
  FLAG to_raw_state;
{
#ifdef TERMIO
  static struct termios old_termio;
	 struct termios new_termio;
#ifdef TCGETS
# define gettermio(fd, iopoi)	(void) ioctl (fd, TCGETS, iopoi);
# ifdef TCSETSW
# define settermio(fd, iopoi)	(void) ioctl (fd, TCSETSW, iopoi);
# else
# define settermio(fd, iopoi)	(void) ioctl (fd, TCSETS, iopoi);
# endif
#else
# define gettermio(fd, iopoi)	tcgetattr (fd, iopoi);
# ifdef TCSADRAIN
# define settermio(fd, iopoi)	tcsetattr (fd, TCSADRAIN, iopoi);
# else
# define settermio(fd, iopoi)	tcsetattr (fd, 0, iopoi);
# endif
#endif
#endif /* TERMIO */

#ifdef SGTTY
  static struct sgttyb old_tty;
	 struct sgttyb new_tty;
  static int oldlmode;
	 int lmode;
  static struct tchars old_tchars;
  static struct ltchars old_ltchars;
#define NDEF '\377'
  static struct tchars new_tchars = {NDEF, quit_char, NDEF, NDEF, NDEF, NDEF};
  static struct tchars new_QStchars = {NDEF, quit_char, '\021', '\023', NDEF, NDEF};
  static struct ltchars new_ltchars = {NDEF, NDEF, NDEF, NDEF, NDEF, NDEF};
/* correspondence between the tchars/ltchars characters of the sgtty 
   interface and the c_cc characters of the termios interface (codes vary):
	sgtty		termio		sgtty		termio
	t_intrc		VINTR		t_suspc		VSUSP
	t_quitc		VQUIT		t_dsuspc	VDSUSP
	t_startc	VSTART		t_rprntc	VREPRINT
	t_stopc		VSTOP		t_flushc	VDISCARD
	t_eofc		VEOF (VMIN)	t_werasc	VWERASE
	t_brkc		VEOL (VTIME)	t_lnextc	VLNEXT
*/
#endif /* SGTTY */


  if (to_raw_state == False) {
	isscreenmode = False;


/* Reset screen */
#ifdef CURSES
	end_screen_mode ();
#else	/* ifndef CURSES: */
	end_screen_mode ();	/* will put controls to flush buffer */
	flush ();
#endif	/* ifndef CURSES */


/* Reset tty */
#ifdef TERMIO
	settermio (input_fd, & old_termio);
/*	one byte of output swallowed here on SunOS	*/
#endif
#ifdef SGTTY
	(void) ioctl (input_fd, TIOCSETP, & old_tty);
	(void) ioctl (input_fd, TIOCSETC, & old_tchars);
/*	one byte of output swallowed here on SunOS	*/
	(void) ioctl (input_fd, TIOCSLTC, & old_ltchars);
	(void) ioctl (input_fd, TIOCLSET, & oldlmode);
#endif
/*	output a byte that SunOS may swallow:	*/
	write (output_fd, " \r", 2);
	return;
  }


  else /* (to_raw_state == True) */ {
	isscreenmode = True;


/* Init tty */
#ifdef TERMIO
	if (old_termprops_not_saved) {
		/* Save old tty settings */
		gettermio (input_fd, & old_termio);

		/* Determine configured erase character */
		erase_char = old_termio.c_cc [VERASE];
		/*kill_char = old_termio.c_cc [VKILL];*/

		old_termprops_not_saved = False;
	}

	gettermio (input_fd, & new_termio);

	if (controlQS == False) {
		new_termio.c_iflag &= ~(ISTRIP|IXON|IXOFF);
	} else {
		new_termio.c_iflag &= ~(ISTRIP);
	}
	new_termio.c_oflag &= ~OPOST;
	new_termio.c_cflag &= ~(PARENB|CSIZE);
	new_termio.c_cflag |= CS8;
	new_termio.c_lflag &= ~(ICANON|ECHO
#ifdef ISIG
		|ISIG
#endif
		);
	new_termio.c_iflag &= ~ICRNL;	/* pass through ^M */
#define NDEF '\000'
	new_termio.c_cc [VMIN] = 1;
	new_termio.c_cc [VTIME] = 0;
	new_termio.c_cc [VQUIT] = quit_char;
	new_termio.c_cc [VINTR] = NDEF;
	new_termio.c_cc [VSUSP] = NDEF;
#ifdef VDISCARD
	new_termio.c_cc [VDISCARD] = NDEF;
#endif
#ifdef VDSUSP
	new_termio.c_cc [VDSUSP] = NDEF;
#endif
#ifdef VLNEXT
	new_termio.c_cc [VLNEXT] = NDEF;
#endif
	settermio (input_fd, & new_termio);
#endif /* TERMIO */
#ifdef SGTTY
	if (old_termprops_not_saved) {
		/* Save old tty settings */
		(void) ioctl (input_fd, TIOCGETP, & old_tty);
		(void) ioctl (input_fd, TIOCGETC, & old_tchars);
		(void) ioctl (input_fd, TIOCGLTC, & old_ltchars);
		(void) ioctl (input_fd, TIOCLGET, & oldlmode);

		/* Determine configured erase character */
		erase_char = old_tty.sg_erase;
		/*kill_char = old_tty.sg_kill;*/

		old_termprops_not_saved = False;
	}

	/* Set line mode */
	/* If this feature should not be available on some system, 
	   RAW must be used instead of CBREAK below to enable 8 bit output */
	lmode = oldlmode;
	lmode |= LPASS8; /* enable 8 bit characters on input in CBREAK mode */
	lmode |= LLITOUT; /* enable 8 bit characters on output in CBREAK mode;
	    this may not be necessary in newer Unixes, e.g. SunOS 4;
	    output handling is slightly complicated by LITOUT */
	(void) ioctl (input_fd, TIOCLSET, & lmode);
	/* Set tty to CBREAK (or RAW) mode */
	new_tty = old_tty;
	new_tty.sg_flags &= ~ECHO;
	new_tty.sg_flags |= CBREAK;
	(void) ioctl (input_fd, TIOCSETP, & new_tty);
	/* Unset signal chars */
	if (controlQS == False) {
		(void) ioctl (input_fd, TIOCSETC, & new_tchars);  /* Only leaves quit_char */
	} else {
		(void) ioctl (input_fd, TIOCSETC, & new_QStchars);  /* Leaves quit_char, ^Q, ^S */
	}
	(void) ioctl (input_fd, TIOCSLTC, & new_ltchars); /* Leaves nothing */
#endif /* SGTTY */


/* Init screen */
#ifdef CURSES
	start_screen_mode ();
	refresh ();
	/* Determine configured erase character */
	erase_char = erasechar ();
	/*kill_char = killchar ();*/
#else
	start_screen_mode ();
	flush ();
#endif


/* Setup signal handlers */
#ifdef SIGQUIT
	signal (SIGQUIT, catchquit);	/* Catch quit_char */
#endif
#ifdef SIGBREAK
	signal (SIGBREAK, catchbreak);	/* control-Break (OS/2) */
#else
# ifdef msdos_suppressbreak
	ctrlbrk (controlbreak);	/* completely useless, stupid Turbo-C! */
# endif
#endif
#ifdef SIGINT
	signal (SIGINT, catchint);	/* Catch INTR char (^C) */
#endif
#ifdef SIGWINCH
	signal (SIGWINCH, catchwinch);	/* Catch window size changes */
#endif
  }
}


/*======================================================================*\
|*			Unix I/O routines				*|
\*======================================================================*/

#ifdef unix
#ifndef CURSES

/**
   termcap API and storage
 */

extern int tgetent ();
extern char * tgetstr ();
extern int tgetnum ();
extern int tgetflag ();
extern char * tgoto ();
extern int tputs ();
#define termputstr(str, aff)	(void) tputs (str, aff, (intfunc) __putchar)

static char * cCL, * cCE, * cSR, * cAL, * cDL, * cCS, * cSC, * cRC,
     * cCM, * cSO, * cSE, * cVS, * cVE, * cVI,
     * cTI, * cTE, * cKS, * cKE,
     * cAS, * cAE, * cEA, * cAC, * cS2, * cS3,
     * cME, * cMR, * cMH, * cMD, * cAF, * cAB,
     * cMouS, * cMouT, * cMouT1, * cMouT2, * cMouE, * cMouE0, * cMouE1, * cMouE2;

#define aff1 0
#define affmax YMAX

#endif
#endif


/*	block graphics mapping
	note: VGA (PC) encoding also used on Linux console!
VT100	VGA	curses
j	D9	ACS_LRCORNER
k	BF	ACS_URCORNER
l	DA	ACS_ULCORNER
m	C0	ACS_LLCORNER
n		ACS_PLUS
o		ACS_S1
p		ACS_S3
q	C4	ACS_HLINE
r		ACS_S7
s		ACS_S9
t	C3	ACS_LTEE
u	B4	ACS_RTEE
v	C1	ACS_BTEE
w	C2	ACS_TTEE
x	B3	ACS_VLINE
*/

/**
   ASCII block graphics mapping
   +-+-+
   +-+-+
   | | |
   +-+-+
 */
static
char *
asciiblockchar (c)
  character c;
{
    if (menu_border_style == 'r') {
	switch (c) {
		case 'j':	return "/";	/* LR */
		case 'k':	return "\\";	/* UR */
		case 'l':	return "/";	/* UL */
		case 'm':	return "\\";	/* LL */
		case 'n':	return "+";	/* crossing (not used) */
		case 'q':	if (width_data_version & cjk_width_data) {
					return "--";	/* hor. line */
				} else {
					return "-";	/* hor. line */
				}
		case 't':	return "+";	/* left tee */
		case 'u':	return "+";	/* right tee */
		case 'v':	return "+";	/* bottom tee */
		case 'w':	return "+";	/* top tee */
		case 'x':	return "|";	/* vert. line */
		case 'f':
		case 'g':	/* scrolling indication */
				if (utf8_screen) {
					return "¦";
				} else if (cjk_term || mapped_term) {
					return "|";
				} else {
					return "�";
				}
		default: return "";
	}
    } else {
	switch (c) {
		case 'j':	return "+";	/* LR */
		case 'k':	return "+";	/* UR */
		case 'l':	return "+";	/* UL */
		case 'm':	return "+";	/* LL */
		case 'n':	return "+";	/* crossing (not used) */
		case 'q':	if (width_data_version & cjk_width_data) {
					return "--";	/* hor. line */
				} else {
					return "-";	/* hor. line */
				}
		case 't':	return "+";	/* left tee */
		case 'u':	return "+";	/* right tee */
		case 'v':	return "+";	/* bottom tee */
		case 'w':	return "+";	/* top tee */
		case 'x':	return "|";	/* vert. line */
		case 'f':
		case 'g':	/* scrolling indication */
				if (utf8_screen) {
					return "¦";
				} else if (cjk_term || mapped_term) {
					return "|";
				} else {
					return "�";
				}
		default: return "";
	}
    }
}

#ifndef CURSES

/**
   VGA (PC/DOS) block graphics mapping
 */
static
character
vgablockchar (c)
  character c;
{
  switch (c) {
	case 'j':	return 0xD9;	/* LR */
	case 'k':	return 0xBF;	/* UR */
	case 'l':	return 0xDA;	/* UL */
	case 'm':	return 0xC0;	/* LL */
	case 'n':	return 0xC5;	/* crossing (not used) */
	case 'q':	return 0xC4;	/* hor. line */
	case 't':	return 0xC3;	/* left tee */
	case 'u':	return 0xB4;	/* right tee */
	case 'v':	return 0xC1;	/* bottom tee */
	case 'w':	return 0xC2;	/* top tee */
	case 'x':	return 0xB3;	/* vert. line */
	case 'f':
	case 'g':	return 0xB2;	/* box as scrolling indication */
	default:	return c;
  }
}

#ifndef msdos

/**
   block graphics mapping using termcap "ac" / terminfo "acsc" capability, 
   or VT100 block graphics by default
 */
static
char *
acblockchar (c)
  character c;
{
  character cout = '\0';
  static char sout [3];
  char * spoi = sout;

  if (c == 'f' || c == 'g') {
#ifdef choose_one_of
	c = '`';
	c = 'a';
	c = '|';
	c = 'f';
#endif
	c = '`';
  }

  if (* cAC) {
	int i;
	for (i = 0; i < strlen (cAC); i += 2) {
		if (cAC [i] == c) {
			cout = cAC [i + 1];
			break;
		}
	}
	if (cout == '\0') {
		return asciiblockchar (c);
	}
  } else {
	cout = c;
  }

  * spoi ++ = cout;
  if (c == 'q' && (width_data_version & cjk_width_data) && cjk_term) {
	* spoi ++ = cout;
  }
  * spoi = '\0';
  return sout;
}

#endif

#endif

/**
   Unicode block graphics mapping
   ┌─┬─┐  ╭─┬─╮
   ├─┼─┤  ├─┼─┤
   │ │ │  │ │ │
   └─┴─┘  ╰─┴─╯
   ┏━┳━┓  ╔═╦═╗
   ┣━╋━┫  ╠═╬═╣
   ┃ ┃ ┃  ║ ║ ║
   ┗━┻━┛  ╚═╩═╝
 */
static
char *
unicodeblockchar (c)
  character c;
{
  if (use_ascii_graphics) {
	return asciiblockchar (c);
  } else {
    if (menu_border_style == 'f') {
	switch (c) {
		case 'j':	return "┛";	/* LR */
		case 'k':	return "┓";	/* UR */
		case 'l':	return "┏";	/* UL */
		case 'm':	return "┗";	/* LL */
		case 'n':	return "╋";	/* crossing (not used) */
		case 'q':	return "━";	/* hor. line */
		case 't':	return "┣";	/* left tee */
		case 'u':	return "┫";	/* right tee */
		case 'v':	return "┻";	/* bottom tee */
		case 'w':	return "┳";	/* top tee */
		case 'x':	return "┃";	/* vert. line */
		case 'f':
		case 'g':	return "┇";	/* scrolling indication */
		default: return "";
	}
    } else if (menu_border_style == 'd') {
	switch (c) {
		case 'j':	return "╝";	/* LR */
		case 'k':	return "╗";	/* UR */
		case 'l':	return "╔";	/* UL */
		case 'm':	return "╚";	/* LL */
		case 'n':	return "╬";	/* crossing (not used) */
		case 'q':	return "═";	/* hor. line */
		case 't':	return "╠";	/* left tee */
		case 'u':	return "╣";	/* right tee */
		case 'v':	return "╩";	/* bottom tee */
		case 'w':	return "╦";	/* top tee */
		case 'x':	return "║";	/* vert. line */
		case 'f':
		case 'g':	return "║";	/* scrolling indication */
		default: return "";
	}
    } else if (menu_border_style == 'r') {
	switch (c) {
		case 'j':	return "╯";	/* LR */
		case 'k':	return "╮";	/* UR */
		case 'l':	return "╭";	/* UL */
		case 'm':	return "╰";	/* LL */
		case 'n':	return "┼";	/* crossing (not used) */
		case 'q':	return "─";	/* hor. line */
		case 't':	return "├";	/* left tee */
		case 'u':	return "┤";	/* right tee */
		case 'v':	return "┴";	/* bottom tee */
		case 'w':	return "┬";	/* top tee */
		case 'x':	return "│";	/* vert. line */
		case 'f':
		case 'g':	return "┆";	/* scrolling indication */
		default: return "";
	}
    } else {
	switch (c) {
		case 'j':	return "┘";	/* LR */
		case 'k':	return "┐";	/* UR */
		case 'l':	return "┌";	/* UL */
		case 'm':	return "└";	/* LL */
		case 'n':	return "┼";	/* crossing (not used) */
		case 'q':	return "─";	/* hor. line */
		case 't':	return "├";	/* left tee */
		case 'u':	return "┤";	/* right tee */
		case 'v':	return "┴";	/* bottom tee */
		case 'w':	return "┬";	/* top tee */
		case 'x':	return "│";	/* vert. line */
		case 'f':
		case 'g':	return "┆";	/* scrolling indication */
		default: return "";
	}
    }
  }
}


#ifdef CURSES
#define TRUE (char) 1
#define FALSE (char) 0
static void init_colours _((void));
extern int curs_readchar _((void));


void
__putchar (c)
  register character c;
{
  addch (c);
}

#ifdef __PDCURSES__
#if defined(DOS) || defined(OS2) || defined(WIN32)

#ifndef ACS_S3
#define ACS_S3 0
#endif
#ifndef ACS_S7
#define ACS_S7 0
#endif
#ifndef ACS_LEQUAL
#define ACS_LEQUAL 0
#endif
#ifndef ACS_GEQUAL
#define ACS_GEQUAL 0
#endif
#ifndef ACS_NEQUAL
#define ACS_NEQUAL 0
#endif
#ifndef ACS_PI
#define ACS_PI 0
#endif
#ifndef ACS_STERLING
#define ACS_STERLING 0
#endif

chtype acs_map [] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
/* 20 */
0, 0, 0, 0, 0, 0, 0, ACS_DIAMOND, 
0, 0, 0, ACS_RARROW, ACS_LARROW, ACS_UARROW, ACS_DARROW, 0, 
/* 30 */
ACS_BLOCK, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
/* 60 */
0, ACS_CKBOARD, 0, 0, 0, 0, ACS_DEGREE, ACS_PLMINUS, 
ACS_BOARD, ACS_LANTERN, ACS_LRCORNER, ACS_URCORNER, 
ACS_ULCORNER, ACS_LLCORNER, ACS_PLUS, ACS_S1, 
/* 70 */
ACS_S3, ACS_HLINE, ACS_S7, ACS_S9, 
ACS_LTEE, ACS_RTEE, ACS_BTEE, ACS_TTEE, 
ACS_VLINE, ACS_LEQUAL, ACS_GEQUAL, ACS_PI, 
ACS_NEQUAL, ACS_STERLING, ACS_BULLET, 0, 
/* 80 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
#endif	/* if defined(DOS) || defined(OS2) || defined(WIN32) */
#endif	/* ifdef __PDCURSES__ */

void
putblockchar (c)
  register character c;
{
#ifdef __CYGWIN__
	/* acs_map broken with -lncursesw */
  if (use_normalchars_boxdrawing) {
	addstr (unicodeblockchar (c));
  } else {
	addstr (asciiblockchar (c));
  }
#else
  if (use_normalchars_boxdrawing) {
	addstr (unicodeblockchar (c));
  } else if (c == 'f' || c == 'g') {
#ifdef choose_one_of
	addch (ACS_DIAMOND);
	addch (ACS_CKBOARD);
	addch (ACS_NEQUAL);
	addch (ACS_DEGREE);
#endif
	addch (ACS_DIAMOND);
  } else {
	addch (acs_map [c]);
  }
#endif
}

void
ring_bell ()
{
  beep ();
}

void
putstring (str)
  register char * str;
{
  addstr (str);
}

void
flush ()
{
  refresh ();
}

void
clear_screen ()
{
  clear ();
}
void
clear_eol ()
{
  clrtoeol ();
}
void
scroll_forward ()
{
  if (MENU) {
	delete_line (0);
  } else {
	scroll (stdscr);
  }
}
void
scroll_reverse ()
{ /* only called if cursor is at top of screen */
  insertln ();
}
void
add_line (y)
  register int y;
{
  move (y + MENU, 0);
  insertln ();
}
void
delete_line (y)
  register int y;
{
  move (y + MENU, 0);
  deleteln ();
}
void
set_cursor (x, y)
  register int x;
  register int y;
{
  current_cursor_y = y;
  move (y + MENU, x);
}
void
reverse_on ()
{
  standout ();
}
void
reverse_off ()
{
  standend ();
}


#define use_newterm	/* otherwise, redirection to stdout doesn't work */

#ifdef __EMX__		/* __PDCURSES__ ? */
#undef use_newterm
#endif
#ifdef msdos
#undef use_newterm
#endif

static FLAG screen_acquired = False;
#ifdef use_newterm
static SCREEN * myscreen;
extern FILE * fdopen ();
#endif

static
void
configure_screen ()
{
#ifdef __PDCURSES__
  keypad (stdscr, TRUE);
#endif

#ifdef KEY_MOUSE

# ifdef __PDCURSES__
  (void) mouse_set (ALL_MOUSE_EVENTS);
# else
#  ifdef NCURSES_VERSION
  (void) mousemask (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED
#   ifdef BUTTON_SHIFT
		    | BUTTON_SHIFT
#   endif
	, /*(mmask_t)*/ (unsigned long) 0);
#  endif
# endif
#endif
  init_colours ();
#ifdef vms
  crmode ();
#else
/*  crmode ();  */
/* cbreak (); not sufficient to suppress ^S processing on Unix in pipe ?!  */
  raw ();
  nonl ();
#endif
  meta (stdscr, TRUE);	/* should force 8-bit-cleanness but doesn't work */
  noecho ();
  scrollok (stdscr, FALSE);

#ifdef unix
# ifndef vax
#  ifndef __EMX__
  idlok (stdscr, TRUE);
#  endif
#  ifndef sun
  typeahead (input_fd);
#  endif
# endif
#endif

  /* curses refuses to accept meta (stdscr, TRUE) as announced.
     A recursive invocation of raw_mode (in configure_screen or 
     start_screen_mode) helps as a work-around although I don't see 
     why and how it works.
     Also it does not work with Sun curses.
   */
  /* raw_mode (True); */
}

void
menu_mouse_mode (menu)
  FLAG menu;
{
  in_menu_mouse_mode = menu;
}

static
void
start_screen_mode ()
{
#ifdef vms
	(void) system ("set terminal /pasthru /nottsync");
#endif

	if (screen_acquired == False) {
#ifdef use_newterm
		FILE * ttyin = fdopen (input_fd, "r");
		FILE * ttyout = fdopen (output_fd, "w");
		myscreen = newterm (NIL_PTR, ttyout, ttyin);
#else
		initscr ();
#endif
		screen_acquired = True;
		configure_screen ();
	/* curses refuses to accept meta (stdscr, TRUE) as announced.
	   A recursive invocation of raw_mode (in configure_screen or 
	   start_screen_mode) helps as a work-around although I don't 
	   see why and how it works.
	   Also it does not work with Sun curses.
	 */
		raw_mode (True);
	} else {
		resetty ();
	}

	getwinsize ();
}

static
void
end_screen_mode ()
{
	savetty ();
	endwin ();

#ifdef vms
	(void) system ("set terminal /ttsync /nopasthru");
#endif
}


static
void
get_term_cap (TERMname)
  char * TERMname;
{
#ifdef SETLOCALE
  setlocale (LC_ALL, "");
#endif

  YMAX = LINES - 1 - MENU;	/* # of lines */
  XMAX = COLS - 1;	/* # of columns */
/*  getwinsize (); */
}

void
altcset_on ()
{
	attron (A_ALTCHARSET);
}
void
altcset_off ()
{
	attroff (A_ALTCHARSET);
}
void
set_screen_mode (m)
  int m;
{}
void
resize_the_screen (sb, keep_columns)
  FLAG sb;
  FLAG keep_columns;
{
#ifdef __PDCURSES__
#define resize_terminal resize_term
#else
#ifdef NCURSES_VERSION
#define resize_terminal resizeterm
#else
#define resize_terminal(l, c)
#endif
#endif
  int coldif = 5;
  if (keep_columns) {
	coldif = 0;
  }
  if (sb == BIGGER) {
	resize_terminal (LINES + 4, COLS + coldif);
  } else  {
	resize_terminal (LINES - 4, COLS - coldif);
  }
}

void
switch_textmode_height (cycle)
  FLAG cycle;
{}

/*------------------------------------------------------------------------*/
#else	/* ifdef CURSES - else: */


#ifndef msdos
void
ring_bell ()
{
  putchar ('\07');
}
#endif

void
__putchar (c)
  register character c;
{
#ifdef conio
  putch (c);
#else
  putoutchar (c);
#endif
}

/**
   Output a menu border character. Decide which style to use, 
   block graphics (VT100 or VGA), Unicode graphics, ASCII graphics.
   This and it's setup is a mess and could do with some revision.
 */
void
putblockchar (c)
  register character c;
{
#ifdef conio
  if (use_ascii_graphics) {
	putstring (asciiblockchar (c));
  } else {
	putchar (vgablockchar (c));
  }
#else
  if (use_vga_block_graphics) {
	putoutchar (vgablockchar (c));
  } else
	if (use_normalchars_boxdrawing) {
		putoutstring (unicodeblockchar (c));
	} else if (use_pc_block_graphics) {
		putoutchar (vgablockchar (c));
	} else {
		putoutstring (acblockchar (c));
	}
#endif
}

void
putstring (str)
  register char * str;
{
#ifdef conio
  cputs (str);
#else
  putoutstring (str);
#endif
}

void
flush ()
{
  (void) flush_buffer (output_fd);
}

#ifdef unix

void
clear_screen ()
{
  termputstr (cCL, affmax);
}
void
clear_eol ()
{
  if (can_clear_eol) {
	termputstr (cCE, aff1);
  }
}
void
scroll_forward ()
{
  if (MENU) {
	delete_line (0);
  } else {
	set_cursor (0, YMAX);
	termputstr ("\n", affmax);
  }
}
void
scroll_reverse ()
{
  if (MENU) {
	add_line (0);
  } else {
	termputstr (cSR, affmax);
  }
}
void
add_line (y)
  register int y;
{
  if (cAL) {
	set_cursor (0, y);
	termputstr (cAL, affmax);
  }
  else {
	set_cursor (0, y);
	termputstr (cSC, aff1);
	termputstr (tgoto (cCS, YMAX + MENU, y + MENU), aff1);
	termputstr (cRC, aff1);
	termputstr (cSR, affmax);
	termputstr (tgoto (cCS, YMAX + MENU, 0), aff1);
	termputstr (cRC, aff1);
  }
}
void
delete_line (y)
  register int y;
{
  if (cDL) {
	set_cursor (0, y);
	termputstr (cDL, affmax);
  }
  else {
	set_cursor (0, y);
	termputstr (cSC, aff1);
	termputstr (tgoto (cCS, YMAX + MENU, y + MENU), aff1);
	set_cursor (0, YMAX);
	termputstr ("\n", affmax);
	termputstr (tgoto (cCS, YMAX + MENU, 0), aff1);
	termputstr (cRC, aff1);
  }
}
void
set_cursor (x, y)
  register int x;
  register int y;
{
  current_cursor_y = y;
  termputstr (tgoto (cCM, x, y + MENU), aff1);
}
void
reverse_on ()
{
  termputstr (cMR, aff1);
/*
  termputstr (cSO, aff1);
*/
}
void
reverse_off ()
{
  termputstr (cME, aff1);
/*
  termputstr (cSE, aff1);
*/
}
void
altcset_on ()
{
  if (use_pc_block_graphics) {
	termputstr (cS2, aff1);
  } else {
	termputstr (cAS, aff1);
  }
}
void
altcset_off ()
{
  if (use_pc_block_graphics) {
	termputstr (cS3, aff1);
  } else {
	termputstr (cAE, aff1);
  }
}

/**
   Adapt mouse mode to menu state (menu opened?)
   While a menu is open, mouse movement should be reported separately.
 */
void
menu_mouse_mode (menu)
  FLAG menu;
{
  if (use_mouse == False) {
	return;
  }
  if (menu) {
	if (in_menu_mouse_mode == False) {
		if (mouse_button_event_tracking && MENU) {
			/* activate button-event tracking */
			termputstr (cMouT2, affmax);
		}
		/* turn cursor invisible for menu display */
		termputstr (cVI, affmax);
	}
	in_menu_mouse_mode = True;
  } else {
	if (in_menu_mouse_mode) {
		if (mouse_hilite_tracking && MENU) {
			/* activate hilite tracking */
			termputstr (cMouT1, affmax);
		}
		/* turn cursor visible again; 
		   first turn it "normal" in case "very visible" does 
		   not work, then try "very visible" */
		termputstr (cVE, affmax);
		termputstr (cVS, affmax);
	}
	in_menu_mouse_mode = False;
  }
}

static
void
start_screen_mode ()
{
  termputstr (cTI, affmax);
  termputstr (cVS, affmax);
  termputstr (cKS, affmax);
  if (MENU && use_mouse) {
	termputstr (cMouS, affmax);	/* activate mouse reporting */
	termputstr (cMouT, affmax);	/* activate mouse tracking */
	if (mouse_hilite_tracking) {
		termputstr (cMouT1, affmax); /* activate hilite tracking */
	}
#ifdef button_event_tracking_init
	termputstr (cMouT2, affmax); /* activate button-event tracking */
#endif
  }

/* Install correct scrolling region in case terminal is bigger than assumed */
/* (this effect was observed after window size changes of Sun windows): */
  if (cCS) {
	termputstr (tgoto (cCS, YMAX + MENU, 0), aff1);
  }
}

static
void
end_screen_mode ()
{
  if (mouse_hilite_tracking) {
	/* abort hilite tracking */
	putstring ("\033[0;0;0;0;0T");
  }
  if (MENU && use_mouse) {
	termputstr (cMouE2, affmax);
	termputstr (cMouE1, affmax);
	termputstr (cMouE0, affmax);
	termputstr (cMouE, affmax);
  }
  /* if mouse was released quickly after, e.g., QUIT command, 
     swallow mouse release report already sent */
  if (char_ready_within (50)) {
	(void) _readchar_nokeymap ();
  }
  termputstr (cKE, affmax);
  termputstr (cVE, affmax);
  termputstr (cTE, affmax);
}


#define dont_debug_termcap

#ifdef debug_termcap
/**
   Debug termcap control strings.
 */
static
void
print_termcapstr (name, s)
  char * name;
  char * s;
{
  printf ("%s: ", name);
  if (s) {
    while (* s) {
	if ((* s & 0xE0) == 0) {
		printf ("^%c", * s + 0x40);
	} else {
		printf ("%c", * s);
	}
	s ++;
    }
  } else {
    printf ("(NULL)");
  }
  printf ("\n");
}
#endif

static
void
get_term_cap (TERMname)
  char * TERMname;
{
#define termbuflen 1024
  char entry [2048];
  static char termbuf [termbuflen];
  char * loc = termbuf;
  char * check;

  if (tgetent (entry, TERMname) <= 0) {
	if (! streq (TERMname, "cygwin")) {
		panic ("Unknown terminal", TERMname);
	} else {
		/* fallback to support cygwin version
		   without cygwin (and esp. termcap/terminfo) installed */
		YMAX = 23;
		XMAX = 79;
		getwinsize ();
		cCL = "[H[J";
		cCE = "[K";
		cSR = "M";
		cAL = "[L";
		cDL = "[M";
		cCS = NIL_PTR;
		cSC = "7";
		cRC = "8";
		cCM = "[%i%p1%d;%p2%dH";
		cSO = "[7m";
		cSE = "[27m";
		cVS = NIL_PTR;
		cVE = NIL_PTR;
		cVI = NIL_PTR;
		cTI = "7[?47h";
		cTE = "[2J[?47l8";
		cKS = NIL_PTR;
		cKE = NIL_PTR;
		cAS = "[11m";
		cAE = "[10m";
		cEA = NIL_PTR;
		cAC = NIL_PTR;
		cS2 = "[11m";
		cS3 = "[10m";
		cME = "[0m";
		cMR = "[7m";
		cMH = NIL_PTR;
		cMD = "[1m";
		cAF = NIL_PTR;
		cAB = NIL_PTR;
	}
  } else {

	YMAX = tgetnum ("li" /*, & loc */) - 1 - MENU;	/* # of lines */
	XMAX = tgetnum ("co" /*, & loc */) - 1;	/* # of columns */
/* getenv ("LINES"), getenv ("COLUMNS") ?! */
	getwinsize ();

	cCL = tgetstr ("cl", & loc);	/* clear screen */
	cCE = tgetstr ("ce", & loc);	/* clear to end of line */
	cSR = tgetstr ("sr", & loc);	/* scroll reverse */
	cAL = tgetstr ("al", & loc);	/* add line */
	if (!cSR) {
		cSR = cAL;
	}
	cDL = tgetstr ("dl", & loc);	/* delete line */
	cCS = tgetstr ("cs", & loc);	/* change scrolling region */
	cSC = tgetstr ("sc", & loc);	/* save cursor    \ needed with VT100   */
	cRC = tgetstr ("rc", & loc);	/* restore cursor / for add/delete line */
	cCM = tgetstr ("cm", & loc);	/* cursor motion */
	cSO = tgetstr ("so", & loc);	/* standout mode */
	cSE = tgetstr ("se", & loc);	/* end " */
	cVS = tgetstr ("vs", & loc);	/* visual cursor */
	cVE = tgetstr ("ve", & loc);	/* normal cursor */
	cVI = tgetstr ("vi", & loc);	/* invisible cursor */
	cTI = tgetstr ("ti", & loc);	/* positioning mode */
	cTE = tgetstr ("te", & loc);	/* end " */
	cKS = tgetstr ("ks", & loc);	/* keypad mode */
	cKE = tgetstr ("ke", & loc);	/* end " */
	cAS = tgetstr ("as", & loc);	/* alternate character set */
	cAE = tgetstr ("ae", & loc);	/* end " */
	cEA = tgetstr ("eA", & loc);	/* enable " */
	cAC = tgetstr ("ac", & loc);	/* block graphics code mapping */
	cS2 = tgetstr ("S2", & loc);	/* PC character set */
	cS3 = tgetstr ("S3", & loc);	/* end " */
	cME = tgetstr ("me", & loc);	/* end all modes */
	cMR = tgetstr ("mr", & loc);	/* reverse */
	cMH = tgetstr ("mh", & loc);	/* dim / half-bright */
	cMD = tgetstr ("md", & loc);	/* bold / extra bright */
	cAF = tgetstr ("AF", & loc);	/* set foreground colour */
	cAB = tgetstr ("AB", & loc);	/* set background colour */
	standout_glitch = tgetflag ("xs", & loc);	/* standout glitch */
#ifdef debug_termcap_full
	printf ("%s %d %d; cCL %s, cCE %s, cSR %s, cAL %s,	cSR %s, cDL %s, cCS %s, cSC %s, cRC %s, cCM %s, cSO %s, cSE %s, cVS %s, cVE %s, cVI %s, cTI %s, cTE %s, cKS %s, cKE %s, cAS %s, cAE %s, cEA %s, cS2 %s, cS3 %s, cME %s, cMR %s, cMH %s, cMD %s, cAF %s, cAB %s\n",
		TERMname, YMAX, XMAX, 
		cCL, cCE, cSR, cAL, cSR, cDL, cCS, cSC, cRC, cCM, cSO, cSE, cVS, cVE, cVI, cTI, cTE, cKS, cKE, cAS, cAE, cEA, cS2, cS3, cME, cMR, cMH, cMD, cAF, cAB);
#endif
  }
#ifdef debug_termcap
  print_termcapstr ("cm", cCM);
  print_termcapstr ("AF", cAF);
  print_termcapstr ("AB", cAB);
  print_termcapstr ("as", cAS);
  print_termcapstr ("eA", cEA);
#endif

  if (loc > termbuf + termbuflen) {
	panic ("Terminal control strings don't fit", NIL_PTR);
  }

  /* derive and fix properties */

  if (cAL || (cSR && cCS)) {
	can_add_line = True;
  }
  if (cSR) {
	can_scroll_reverse = True;
  }
  if (cDL || cCS) {
	can_delete_line = True;
  }
  if (cCE) {
	can_clear_eol = True;
  }

#define denull(c)	if (c == NIL_PTR) c = ""

  denull (cSO);
  denull (cSE);
  denull (cTI);
  denull (cTE);
  denull (cVS);
  denull (cVE);
  denull (cVI);
  denull (cAS);
  denull (cAE);
  denull (cEA);
  denull (cAC);
  denull (cS2);
  denull (cS3);
  denull (cKS);
  denull (cKE);
  denull (cME);
  denull (cMR);
  denull (cMH);
  denull (cMD);
  denull (cAF);
  denull (cAB);

  /* if termcap/terminfo defines combined control sequences that 
     set/reset both application cursor keys and application keypad mode, 
     reduce them to application cursor keys only in order to maintain 
     distinguishable keys on the otherwise redundant small and big 
     keypad blocks of a PC keyboard */
  if (strncmp (cKS, "\033[?1h", 5) == 0) {
	cKS = "\033[?1h";
  }
  if (strncmp (cKE, "\033[?1l", 5) == 0) {
	cKE = "\033[?1l";
  }

  /* if the terminal has no explicit "reverse" control, use standout instead */
  if (* cMR == '\0') {
	cMR = cSO;
	cME = cSE;
  }

  if (!cCL || !cCM /* || !cSO || !cSE */ ) {
	panic ("Sorry, terminal features are insufficient for mined", NIL_PTR);
  }

  /* check if terminal accepts ANSI control sequence patterns */
  check = cME;
  if (* check == '\0') {
	check = cSE;
  }
  if (strstr (check, "\033[") && strchr (check, 'm') != NIL_PTR) {
	ansi_esc = True;
  } else {
	ansi_esc = False;
  }
#ifdef debug_termcap
  print_termcapstr ("me", cME);
  print_termcapstr ("se", cSE);
  print_termcapstr ("check", check);
  printf ("ansi_esc %d\n", ansi_esc);
#endif

  /* set mouse mode controls */
  if (ansi_esc) {
	cMouS = "\033[?9h";	/* to enable xterm mouse escape sequences */
	cMouT = "\033[?1000h";
	cMouT1 = "\033[?1001h";
	cMouT2 = "\033[?1002h";
	cMouE = "\033[?9l";
	cMouE0 = "\033[?1000l";
	cMouE1 = "\033[?1001l";
	cMouE2 = "\033[?1002l";
  } else {
	cMouS = "";
	cMouT = "";
	cMouT1 = "";
	cMouT2 = "";
	cMouE = "";
	cMouE0 = "";
	cMouE1 = "";
	cMouE2 = "";
  }

  if (* cAS == '\0') {
	if (use_vt100_block_graphics) {
		cAS = "";
		cAE = "";
	}
  }
  if (streq (TERMname, "linux") && use_vt100_block_graphics) {
	cAS = "";
	cAE = "";
  }
  if (* cAS == '\0') {
	can_alt_cset = False;
  }
  if (* cEA == '\0' && * cAS != '\0'
      && (strisprefix ("xterm", TERMname)
       || strisprefix ("rxvt", TERMname)
       || strisprefix ("konsole", TERMname)
     )) {
	/* workaround for missing eA capability in /etc/termcap */
	cEA = "\033(B\033)0";
  }
  if (* cS2 != '\0' && * cS3 != '\0') {
	use_pc_block_graphics = True;
  }

  if (* cKS == '\0' && ansi_esc) {
	cKS = "\033[?66;1h";
	cKE = "\033[?66;1l";
  }

  if (* cAF == '\0' && ansi_esc) {
	cAF = "\033[3%dm";
	cAB = "\033[4%dm";
  }
}

/*------------------------------------------------------------------------*/
#endif	/* ifdef unix */


#ifdef msdos

int
get_codepage ()
{
  union REGS Regs;

  Regs.x.ax = 0x6601;
  int86 (0x21, & Regs, & Regs);
  return Regs.x.bx;
}

/*
 * checkwin checks if a screen size change has occurred
	BIOS data:
	40:4A	word	video columns
	can also be determined with INT10h, function 0Fh
	40:84	byte	video lines - 1			(EGA/VGA required)
	40:85	word	character height (pixels)	(EGA/VGA required)
	can also be determined with INT10h, function 11h/30h
 */
#ifdef __TURBOC__
static
int
checkwin ()
{
  return peek (0x40, 0x4A) != XMAX + 1
/* the following line requires EGA or VGA: */
	  || peekb (0x40, 0x84) != YMAX + MENU
	;
}
#else
#include <sys/farptr.h>

static
int
checkwin ()
{
  return _farpeekw (0x40, 0x4A) != XMAX + 1
/* the following line requires EGA or VGA: */
	  || _farpeekb (0x40, 0x84) != YMAX + MENU
	;
}
#endif

#ifdef __TURBOC__
int wincheck = 1;
#else
#ifdef ANSI
int wincheck = 1;
#endif
#endif

#ifdef __TURBOC__	/* djgpp provides its own getch */

/*
 * getch () reads in a character from keyboard. We seem to need our 
 * own low-level input routine since (at least with Turbo-C) the use 
 * of getch () from conio.h makes the runtime system switch to normal 
 * text mode on startup so that extended text modes could not be used.
 * This is really a very stupid chicane of Turbo-C.
 */
int
getch ()
{
  union REGS Regs;
  int result;

#ifdef getchBIOS
  /* Don't use; for some reason, this crap doesn't work. And if it's 
     tweaked to work, it doesn't provide keypad keys. And it doesn't 
     even help to avoid ^S/^Q/^C/^P handling either.
   */
  static int BIOSgetch = -1;
  if (BIOSgetch == -1) {
	Regs.h.ah = 9;
	int86 (0x16, & Regs, & Regs);
	if (Regs.h.al & 0x40) {
		BIOSgetch = 0x20;
	} else if (Regs.h.al & 0x20) {
		BIOSgetch = 0x10;
	} else {
		BIOSgetch = 0x00;
	}
  }

  Regs.h.ah = BIOSgetch;
  int86 (0x16, & Regs, & Regs);
#else
  Regs.h.ah = 0x07;
  intdos (& Regs, & Regs);
#endif

  result = Regs.h.al;

  if (wincheck && checkwin ()) {
	winchg = True;
	interrupted = True;
  /* in MSDOS, RDwin calls getch, so it cannot be called directly here */
  }

  return result;
}

#endif /* __TURBOC__ */

void
ring_bell ()
{
  union REGS Regs;

  Regs.h.ah = 0x0E;
  Regs.h.al = 7;
  Regs.h.bh = 0;
  Regs.h.bl = 0;
  int86 (0x10, & Regs, & Regs);
}

void
set_video_lines (r)
/* 0/1/2: 200/350/400 lines */
  int r;
{
  union REGS Regs;

  Regs.h.ah = 0x12;
  Regs.h.bl = 0x30;
  Regs.h.al = r;

  int86 (0x10, & Regs, & Regs);
}

int font_height = 16;
void
set_font_height (r)
/* set font height in character pixels, <= 32 */
  int r;
{
#ifdef __TURBOC__
#define useintr
#endif

#ifdef useintr
  struct REGPACK Regs;

  Regs.r_ax = 0x1130;
  Regs.r_bx = 0;
  intr (0x10, & Regs);
  Regs.r_ax = 0x1110;
  Regs.r_bx = r << 8;
  Regs.r_cx = 256;
  Regs.r_dx = 0;
/*
  Regs.r_bp = 0;
  Regs.r_es = 0;
*/
  intr (0x10, & Regs);
#else
  union REGS Regs;

  Regs.h.ah = 0x11;
  Regs.h.al = 0x10;
  Regs.h.bh = r;
  font_height = r;
  Regs.h.bl = 0;
  Regs.x.cx = 256;
  Regs.x.dx = 0;
/*  Regs.x.bp = 0;	ignored by Turbo-C's int86 function */
/*  Regs.x.es = 0;	not in structure but accepted by rotten C */
  int86 (0x10, & Regs, & Regs);
#endif
}

void
set_grafmode_height (r, l)
/* 0/1/2: font height 8/14/16 ; 1/2/3/n: 14/25/43/n lines */
  int r;
  int l;
{
  union REGS Regs;

  Regs.h.ah = 0x11;
  if (r == 0) {
	Regs.h.al = 0x23;
  } else if (r == 1) {
	Regs.h.al = 0x22;
  } else {
	Regs.h.al = 0x24;
  }
  if (l <= 0) {
	Regs.h.bl = 1;
  } else if (l <= 3) {
	Regs.h.bl = l;
  } else {
	Regs.h.bl = 0;
	Regs.h.dl = l;
  }

  int86 (0x10, & Regs, & Regs);
}

void
set_fontbank (f)
/* 0..7 */
  int f;
{
  union REGS Regs;

  Regs.h.ah = 0x11;
  Regs.h.al = 0x03;
  Regs.h.bl = (f & 3) * 5 + (f & 4) * 12;

  int86 (0x10, & Regs, & Regs);
}

#ifdef conio
#include <conio.h>

static struct text_info scrinfo;
static unsigned char norm_attr;	/* default character colours */
static unsigned char inverse_attr;

void
clear_screen ()
{
  clrscr ();
}

void
clear_eol ()
{
  clreol ();
}

void
scroll_forward ()
{
  if (MENU) {
	delete_line (0);
  } else {
	set_cursor (0, YMAX);
	putchar ('\n');
  }
}

void
scroll_reverse ()
{ /* only called if cursor is at top of screen */
  insline ();
}

void
add_line (y)
  register int y;
{
  set_cursor (0, y);
  insline ();
}

void
delete_line (y)
  register int y;
{
  set_cursor (0, y);
  delline ();
}

void
set_cursor (x, y)
{
  current_cursor_y = y;
  gotoxy (x + 1, y + MENU + 1);
}

void
reverse_on ()
{
  textattr (inverse_attr);
/*highvideo ();*/
}

void
reverse_off ()
{
  textattr (norm_attr);
/*normvideo ();*/
}

void
altcset_on ()
{}

void
altcset_off ()
{}

static
void
start_screen_mode ()
{}

static
void
end_screen_mode ()
{}

void
menu_mouse_mode (menu)
  FLAG menu;
{
  in_menu_mouse_mode = menu;
}

void
get_term ()
{
  getwinsize ();

  norm_attr = scrinfo.normattr;
  inverse_attr = ((norm_attr & 0x0F) << 4) | (norm_attr >> 4);
}

void
getwinsize ()
{
/* this has to be extended to request the current screen size */
  gettextinfo (& scrinfo);
  /* This seems to be a junk procedure since no other information than
     25 * 80 comes out in 50 lines mode */
  YMAX = scrinfo.screenheight - 1 - MENU;
  XMAX = scrinfo.screenwidth - 1;
}

#endif

#endif /* ifdef msdos */


#ifdef ANSI

/* suppress some additional termcap calls */
#define termputstr(str, aff)

/* adjust the following values to the capabilities of your ANSI driver: */
static char CUP1 [] = "\033[1;1H";
static char CUD1 [] = "\033[B";
static char CUF1 [] = "\033[C";
static char DSR [] = "\033[6n";
static char ED2 [] = "\033[2J";
static char EL [] = "\033[K";
static char IL1 [] = "\033[L";
static char DL1 [] = "\033[M";
static char reverse_on_str [30] = "\033[7m";	/* inverse mode */
static char reverse_off_str [30] = "\033[27m";	/* inverse off */

void
clear_screen ()
{
  putstring (ED2);
}

void
clear_eol ()
{
  if (can_clear_eol) {
	putstring (EL);
  }
}

void
scroll_forward ()
{
  if (MENU) {
	delete_line (0);
  } else {
	set_cursor (0, YMAX);
	putchar ('\n');
  }
}

void
scroll_reverse ()
{ /* only called if cursor is at top of screen */
  putstring (IL1);
}

void
add_line (y)
  register int y;
{
  set_cursor (0, y);
  putstring (IL1);
}

void
delete_line (y)
  register int y;
{
  set_cursor (0, y);
  putstring (DL1);
}

void
set_cursor (x, y)
  register int x;
  register int y;
{
  static char s [11];
  current_cursor_y = y;
  build_string (s, "\033[%d;%dH", y + MENU + 1, x + 1);
  putstring (s);
}

void
reverse_on ()
{
  putstring (reverse_on_str); /* 1m | 7m | 7m    | 7;2m */
}

void
reverse_off ()
{
  putstring (reverse_off_str); /* m  | 0m | 0m 1m | m    */
}

void
altcset_on ()
{}

void
altcset_off ()
{}

static
void
start_screen_mode ()
{}

static
void
end_screen_mode ()
{}

void
menu_mouse_mode (menu)
  FLAG menu;
{
  in_menu_mouse_mode = menu;
}

static FLAG noCPR = False;

static
int
getANSIpos ()
/* returns False if indicated position is upper left corner */
/* also leaves position in XMAX+1, YMAX+1 */
/* Checks characters input against ANSI CPR (cursor position report) 
   sequence. If it does not comply, sets noCPR flag, does not check again
   and reports as if cursor had been moved. By this trick, we implement 
   the following auxiliary behaviour: If an ANSI driver cannot deliver 
   cursor reports, mined may be stuffed the screen size as its first input, 
   at the positions of an ANSI CPR sequence but embedded in different 
   characters, e.g. xx25x80xx */
{
  int result;
  if (noCPR) {
	return 1;
  }
#ifdef msdos
  wincheck = 0;	/* prevent recursive calls of checkwin and getANSIpos */
#endif
  putstring (DSR);
  flush ();
  if (_readchar_nokeymap () != '\033') {
	noCPR = True;
  }
  if (_readchar_nokeymap () != '[') {
	noCPR = True;
  }
  if (get_digits (& YMAX) != ';') {
	noCPR = True;
  }
  if (get_digits (& XMAX) != 'R') {
	noCPR = True;
  }
  (void) _readchar_nokeymap ();	/* MSDOS ANSI drivers send a final return */
  YMAX = YMAX - 1; XMAX = XMAX - 1;
#ifdef msdos
  wincheck = 1;
#endif
  result = YMAX != 0 || XMAX != 0;
  return result;
}

static
int
failANSI (check)
/* returns False if control string does not change screen position */
  char * check;
{
  putstring (CUP1);
  putstring (check);
  return getANSIpos ();
}

void
get_term ()
{
  char * colstr = unnull (getenv ("MINEDCOL"));
  char * colrev = colstr;
  while (* colrev != '\0' && * colrev != ' ') {
	colrev ++;
  }
  if (* colrev == ' ') {
	* colrev = '\0';
	colrev ++;
  }

/* first do some harmless checks to set noCPR if simple ANSI driver */
/*
  if (failANSI (EL)) {
	can_clear_eol = False; probably every driver can do this;
  }
*/
  if (failANSI (DL1)) {
	can_delete_line = False;
  }
  if (failANSI (IL1)) {
	can_add_line = False;
	can_scroll_reverse = False;
  }

/* heuristics: if driver cannot delete line we assume if can neither set 
   extended attributes like our default inverse and inverse off,
   so change the default */
  if (can_delete_line == False) {
	build_string (reverse_off_str, "\033[32;40m");
	build_string (reverse_on_str, "\033[30;42m");
  }

/* if MINEDCOL set, use it for display attributes instead of defaults */
  if (* colstr != '\0') {
	build_string (reverse_off_str, "\033[%sm", colstr);
  }
  if (* colrev != '\0') {
	build_string (reverse_on_str, "\033[%sm", colrev);
  }

/* if cursor reports are available, check display attribute strings */
  if (noCPR == False) {
	if (failANSI (reverse_on_str)) {
		panic ("Invalid control sequence for exposed display", NIL_PTR);
	}
	if (failANSI (reverse_off_str)) {
		panic ("Invalid control sequence for normal display", NIL_PTR);
	}
  }

  getwinsize ();
}

void
getwinsize ()
{
#define stepcur 9
  int oldx = -1; int oldy = -1;
  int i;
  if (noCPR == False) {
	putstring (CUP1);
	XMAX = 0; YMAX = 0;
	do {
		for (i = 0; i < stepcur; i ++) {
			if (oldx < XMAX) {
				putstring (CUF1);
			}
			if (oldy < YMAX) {
				putstring (CUD1);
			}
		}
		oldx = XMAX + (stepcur - 1);
		oldy = YMAX + (stepcur - 1);
		(void) getANSIpos ();
	} while (oldx < XMAX || oldy < YMAX);
  }
  if (getenv ("LINES")) {
	(void) scan_int (getenv ("LINES"), & YMAX);
	YMAX --;
  }
  if (getenv ("COLUMNS")) {
	(void) scan_int (getenv ("COLUMNS"), & XMAX);
	XMAX --;
  }
  if (getenv ("NOCLEAREOL")) {
	can_clear_eol = False;	/* for debugging */
  }
  if (getenv ("NORSCROLL")) {
	can_scroll_reverse = False;	/* for debugging */
  }
/*  set_cursor (999, 999); */
/*  (void) getANSIpos (); */
  if (MENU) {
	YMAX --;
  }
}

#endif	/* ifdef ANSI */


/**************************************************************************
 * screen mode setting functions, alternatively by MSDOS BIOS calls or 
 * ANSI sequences
 */

static int textmode_height = 2;

#ifdef msdos

void
set_screen_mode (m)
  int m;
{
  union REGS Regs;

  if (m >= 0) {
	Regs.h.ah = 0x00;
	Regs.h.al = m;
	int86 (0x10, & Regs, & Regs);
  }
}

void
set_textmode_height (r)
/* 0/1/2: font height 8/14/16 */
  int r;
{
  union REGS Regs;

  Regs.h.ah = 0x11;
  Regs.h.bl = 0;
  textmode_height = r;
  if (r == 0) {
	Regs.h.al = 0x12;
  } else if (r == 1) {
	Regs.h.al = 0x11;
  } else {
	Regs.h.al = 0x14;
  }

  int86 (0x10, & Regs, & Regs);
}

#else /* ifdef msdos - else use ANSI driver: */

int screen_mode = 3	/* just an assumption, cannot be determined */;

void
set_screen_mode (m)
  int m;
{
  char resize_str [8];

  if (m >= 0) {
	if (m != 50 && m != 43) {
		screen_mode = m;
	}
	build_string (resize_str, "\033[=%dh", m);
	putstring (resize_str);
  }
}

void
set_textmode_height (r)
/* 0/1/2: font height 8/14/16 */
  int r;
{
  textmode_height = r;
  if (r == 0) {
	set_screen_mode (50);
  } else if (r == 1) {
	set_screen_mode (43);
  } else {
	set_screen_mode (screen_mode);
  }
}

#endif

void
switch_textmode_height (cycle)
/* True: cycle through font heights 8/14/16 
   False: switch between font heights 8/16 */
  FLAG cycle;
{
  if (textmode_height >= 2) {
	set_textmode_height (0);
  } else if (cycle) {
	set_textmode_height (textmode_height + 1);
  } else {
	set_textmode_height (2);
  }
}


#define djconiox
#ifdef djconio

int ltab [] = {25, 28, 35, 40, 43, 50};

void
resize_the_screen (sb, keep_columns)
  FLAG sb;
  FLAG keep_columns;
{
/*  char resize_str [8]; */
  int lins = YMAX + 1;
  int i;

  if (sb == BIGGER) {
	i = 0;
	while (i < arrlen (ltab) && ltab [i] <= lins) {
		i ++;
	}
	if (i == arrlen (ltab)) {
		i = 0;
	}
  } else {
	i = arrlen (ltab) - 1;
	while (i >= 0 && ltab [i] >= lins) {
		i --;
	}
	if (i < 0) {
		i = arrlen (ltab) - 1;
	}
  }
  _set_screen_lines (ltab [i]);
}

#else

#include "dosvideo.h"

void
resize_the_screen (sb, keep_columns)
  FLAG sb;
  FLAG keep_columns;
{
/*  char resize_str [8]; */
  int totalchars = (XMAX + 1) * (YMAX + 1);
  int newtotal = 0;
  int curtotal;
  int newmode = -1;
  int i;

  if (keep_columns && ((sb == SMALLER && textmode_height > 0)
			|| (sb == BIGGER && textmode_height < 2)))
  {
	if (sb == SMALLER) {
		set_textmode_height (textmode_height - 1);
	} else {
		set_textmode_height (textmode_height + 1);
	}
  }
  else
  {
     i = 0;
     while (modetab [i].mode >= 0) {
	curtotal = modetab [i].cols * modetab [i].lins;
	if (((sb == SMALLER && curtotal < totalchars && curtotal > newtotal) ||
	     (sb == BIGGER && curtotal > totalchars && (newtotal == 0 || curtotal < newtotal)))
	    && (keep_columns == False || modetab [i].cols == XMAX + 1))
	{
		newtotal = curtotal;
		newmode = modetab [i].mode;
	}
	i ++;
     }
     if (newmode >= 0) {
	set_screen_mode (newmode);
	if (keep_columns) {
		if (sb == BIGGER) {
			set_textmode_height (0);
		} else {
			set_textmode_height (2);
		}
	}
     }
  }
}

#endif

#endif	/* ifdef CURSES - else */


/*------------------------------------------------------------------------*/

/*
 * Read a character from the operating system and handle interrupts.
 * Concerning problems due to the interference of read operations and
 * incoming signals (QUIT, WINCH) see the comments at readchar ().
 */

#ifdef selectread

static
int
strange (err)
  char * err;
{
  ring_bell ();
  error2 ("Interrupted while reading from terminal: ", err);
  sleep (1);
  ring_bell ();
  return quit_char;
}

#endif


#ifndef FD_SET
#define intfds
#endif

/*
 * Is a character available within msec milliseconds from file no fid?
 */
int
inputreadyafter (fid, msec)
  int fid;
  int msec;
{
#ifdef selectread

#ifdef intfds
  int readfds;	/* gives compiler warnings on System V without casting */
#else
  fd_set readfds;	/* causes mined not to work in pipe mode */
#endif

  struct timeval timeoutstru;
  register int nfds;

  timeoutstru.tv_sec = msec / 1000;
  timeoutstru.tv_usec = (msec % 1000) * 1000;

#ifdef intfds
  readfds = 1 << fid;
  nfds = select (fid + 1, & readfds, 0, 0, & timeoutstru);
#else
  FD_ZERO (& readfds);
  FD_SET (fid, & readfds);
/*nfds = select (fid + 1, (fd_set *) & readfds, 0, 0, & timeoutstru);*/
  nfds = select (fid + 1, & readfds, 0, 0, & timeoutstru);
#endif

  return nfds;

#else	/* ifdef selectread */

#ifdef CURSES
  int key;
  int timerms = 0;
  #define timestep 10

  nodelay (stdscr, TRUE);
  while (timerms < msec && (key = curs_readchar ()) == ERR) {
	napms (timestep);
	timerms += timestep;
  }
  nodelay (stdscr, FALSE);
  if (key == ERR) {
	return 0;
  } else {
	ungetch (key);
	return 1;
  }
#else
  if (msec < 500) {
	return 1;
  } else {
	return 0;
  }
#endif

#endif
}


/*
 * Read a char from operating system, handle interrupts if possible, 
 * handle window size changes if possible.
 */

#ifdef selectread

#define dont_debug__readchar

int
__readchar ()
{
#ifndef _getch_
  character c;
  register int n;
#endif

#ifdef intfds
  int readfds;
  int exceptfds;	/* comments see above */
#else
  fd_set readfds;
  fd_set exceptfds;
#endif

  do {
#	ifdef debug__readchar
	printf ("__readchar loop\n");
#	endif
	if (winchg) {
#		ifdef debug__readchar
		printf ("vor RDwin\n");
#		endif
		RDwin ();
#		ifdef debug__readchar
		printf ("nach RDwin\n");
#		endif
	}

#ifdef __CYGWIN__	/* obsolete with ISIG (termios) */
#define control_C_unsure
#endif
#ifdef CURSES
#define control_C_unsure
#endif

#ifdef control_C_unsure
	if (intr_char) {
		intr_char = False;
		return '\003';
	}
#endif

#ifdef intfds
	readfds = 1 << input_fd;
	exceptfds = readfds;
	select (input_fd + 1, & readfds, 0, & exceptfds, 0);
	if (exceptfds != 0) {
#else
	FD_ZERO (& readfds);
	FD_SET (input_fd, & readfds);
	FD_ZERO (& exceptfds);
	FD_SET (input_fd, & exceptfds);
/*	select (input_fd + 1, (fd_set *) & readfds, 0, (fd_set *) & exceptfds, 0);	*/
	select (input_fd + 1, & readfds, 0, & exceptfds, 0);
	if (FD_ISSET (input_fd, & exceptfds)) {
#endif

	   if (quit) {
#		ifdef debug__readchar
		printf ("select -> quit\n");
#		endif
		return quit_char;

	   } else if (winchg) {
#		ifdef debug__readchar
		printf ("select -> winchg\n");
#		endif

	   } else if (intr_char) {
#		ifdef debug__readchar
		printf ("select -> intr_char\n");
#		endif
		intr_char = False;
		return '\003';

	   } else {
#		ifdef debug__readchar
		printf ("select -> strange\n");
#		endif
		return strange ("exception");
	   }
	} else {
#ifdef _getch_
	   return getch ();
#else
	   n = read (input_fd, & c, 1);
	   if (n == 1) {
#		ifdef debug__readchar
		printf ("read -> %02X\n", c);
#		endif
		return c;
	   } else if ((n == 0) || (geterrno () != EINTR)) {
#		ifdef debug__readchar
		printf ("read -> EINTR\n");
#		endif
		panicio ("Error during character input", serror ());
	   } else {
#		ifdef debug__readchar
		printf ("read -> strange\n");
#		endif
		return strange (serror ());
	   }
#endif
	}
  } while (1);
/* NOTREACHED */
}

#else	/* ifdef selectread */

int
__readchar ()
{
#ifdef _getch_

  int c;

#ifdef dosmouse
  c = mousegetch ();
#else
  c = getch ();
#endif

  if (intr_char) {
	intr_char = False;
	c = '\003';
  }

#else	/* _getch_ */

  character c;

  if (read (input_fd, & c, 1) != 1 && quit == False) {
	if (geterrno () == EINTR) {
		return __readchar ();
	} else {
		panicio ("Error during character input", serror ());
	}
  }

#endif	/* else _getch_ */

  if (quit) {
	c = quit_char;
  }
  return c;
}

#endif	/* else ifdef selectread */


/*------------------------------------------------------------------------*/

#ifdef vms

void
get_term ()
{
  get_term_cap (NIL_PTR);
}

void
getwinsize ()
{
/* Can this be determined on VMS? Any advise by someone? */
}

#endif /* vms */

#ifdef unix

/*
   Should window size be determined with ioctl TIOCGWINSZ?
   On SunOS, the bug was seen, that after a first return value of 
   "0 0" (size not known to SunOS), further calls return junk values 
   that may crash mined.
   So further calls are prevented now if the size cannot be determined.
 */
static int get_winsz = 1;

/*
 * Get current window size
 */
void
getwinsize ()
{
#ifdef TIOCGWINSZ
  struct winsize winsiz;

  if (get_winsz) {
	(void) ioctl (output_fd, TIOCGWINSZ, & winsiz);
	if (winsiz.ws_row != 0) {
		YMAX = winsiz.ws_row - 1 - MENU;
	}
	if (winsiz.ws_col != 0) {
		XMAX = winsiz.ws_col - 1;
	} else {
		get_winsz = 0;
	}
  }
#else
#ifdef TIOCGSIZE
  struct ttysize ttysiz;

  (void) ioctl (output_fd, TIOCGSIZE, & ttysiz);
  if (ttysiz.ts_lines != 0) {
	YMAX = ttysiz.ts_lines - 1 - MENU;
  }
  if (ttysiz.ts_cols != 0) {
	XMAX = ttysiz.ts_cols - 1;
  }
#else
#ifdef WIOCGETD
  struct uwdata uwdat;

  (void) ioctl (output_fd, WIOCGETD, & uwdat);
  if (uwdat.uw_height > 0) {
	YMAX = uwdat.uw_height / uwdat.uw_vs - 1 - MENU;
  }
  if (uwdat.uw_width > 0) {
	XMAX = uwdat.uw_width / uwdat.uw_hs - 1;
  }
#else
#ifdef CURSES
# ifdef __PDCURSES__
  YMAX = curscr->_lastsy2 - MENU;
  XMAX = curscr->_lastsx2;
# else
  YMAX = curscr->_maxy - MENU;
  XMAX = curscr->_maxx;
# endif
#else
/* Who can tell me how to do this on different systems? */
  error ("Cannot determine window size");
  sleep (2);
/* leave previous size assumption, don't decrease (dep. on MENU) again */
  return;
#endif
#endif
#endif
#endif
}

/*
 * Get terminal information
 */
void
get_term ()
{
  char * TERMname = getenv ("TERM");

  if (TERMname == NIL_PTR) {
#ifdef __EMX__
	TERMname = "ansi";
#else
	panic ("Terminal not specified", NIL_PTR);
#endif
  }

  get_term_cap (TERMname);
  if (strisprefix ("xterm", TERMname)) {
	window_string_code = "\033]2;%s%s\007\033]1;%s%s\007";
#ifndef CURSES
	mouse_hilite_tracking = True;
#endif
	colours_256 = True;
  } else if (strisprefix ("rxvt", TERMname)) {
	window_string_code = "\033]2;%s%s\007\033]1;%s%s\007";
	/* colour mode doesn't work with older buggy rxvt after configure --enable-text-blink --disable-256-color */
	colours_256 = False;
	/* recent rxvt reduced support to 88 colours */
	colours_88 = True;
  }
#ifndef CURSES
  else if (streq (TERMname, "cygwin")) {
	window_string_code = "\033]2;%s%s\007\033]1;%s%s\007";
	if (explicit_border_style == False) {
		use_vt100_block_graphics = False;
		use_pc_block_graphics = True;
	}
  }
#endif
  else if (strncmp (TERMname, "sun", 3) == 0 && ! streq (ttyname (output_fd), "/dev/console")) {
	window_string_code = "\033]l%s%s\033\\\033]L%s%s\033\\";
  } else if (strisprefix ("aixterm", TERMname)) {
	window_string_code = "\033]2;%s%s\007\033]1;%s%s\007";
  } else if (strisprefix ("dtterm", TERMname)) {
	window_string_code = "\033]2;%s%s\007\033]1;%s%s\007";
  } else if (strisprefix ("iris-", TERMname)) {
	window_string_code = "\033P1.y%s%s\033\\\033P3.y%s%s\033\\";
  } else if (strisprefix ("hpterm", TERMname)) {
	window_string_code = "\033&f0k%dD%s%s\033&f-1k%dD%s%s";
  } else {
	window_string_code = "";
  }

  if (no_window_title) {
	window_string_code = "";
  }

  if (! ansi_esc) {
	mouse_hilite_tracking = False;
	mouse_button_event_tracking = False;
  }

/* build_string (text_buffer, "Terminal is %s, %d * %d.\n", TERMname, YMAX+1, XMAX+1);
  putstring (text_buffer); */

#ifndef CURSES
/* enable alternate character set */
  termputstr (cEA, aff1);
#endif
}

#endif	/* ifdef unix */


#ifdef msdos	/* and not __TURBOC__ ... */
#ifdef CURSES

void
get_term ()
{
  get_term_cap ("");
}

void
getwinsize ()
{
  get_term_cap ("");
}

#endif
#endif


/*------------------------------------------------------------------------*/
/*	mined-specific character attributes	*/


#ifdef CURSES

/* colour definitions from curses.h
// #define COLOR_BLACK     0
// #define COLOR_RED       1
// #define COLOR_GREEN     2
// #define COLOR_YELLOW    3
// #define COLOR_BLUE      4
// #define COLOR_MAGENTA   5
// #define COLOR_CYAN      6
// #define COLOR_WHITE     7
*/
/* colour pair index definitions for mined */
#define ctrlcolor (short) 1
#define menucolor (short) 2
#define diagcolor (short) 3
#define dimcolor (short) 4
#define scrollfgcolor (short) 5
#define scrollbgcolor (short) 6
#define unicolor (short) 7
#define unimarkcolor (short) 8
#define combiningcolor (short) 9
#define menuborder (short) 10
#define menuheader (short) 11
#define menuitem (short) 12
#define HTMLcolor (short) 13
#define HTMLcomment (short) 14
#define HTMLjsp (short) 15

#if NCURSES_VERSION_MAJOR >= 5
#define use_default_colour
#endif
#if NCURSES_VERSION_MAJOR == 4
# if NCURSES_VERSION_MINOR >= 2
#define use_default_colour
# endif
#endif

#ifdef use_default_colour
#define COLOR_fg (short) -1
#define COLOR_bg (short) -1
#else
#define COLOR_fg (short) COLOR_WHITE /* should be "none" (default foreground) */
#define COLOR_bg (short) COLOR_BLACK /* should be "none" (default background) */
#endif

static
void
init_colours ()
{
  start_color ();
#ifdef use_default_colour
  use_default_colors ();
#endif

  init_pair (unicolor, COLOR_fg, COLOR_CYAN);	/* analyse uniansi */
  init_pair (combiningcolor, COLOR_fg, COLOR_CYAN /* COLOR_MAGENTA? */);
						/* analyse combiningansi */
  init_pair (ctrlcolor, COLOR_fg, COLOR_bg);	/* analyse ctrlansi */
  init_pair (menucolor, COLOR_fg, COLOR_bg);	/* analyse menuansi */
  init_pair (diagcolor, COLOR_fg, COLOR_bg);	/* analyse diagansi */
  init_pair (dimcolor, COLOR_RED, COLOR_bg);	/* analyse dimansi */
  init_pair (unimarkcolor, COLOR_CYAN, COLOR_bg);	/* analyse unimarkansi */
  init_pair (scrollfgcolor, COLOR_CYAN, COLOR_BLUE);	/* analyse scrollfgansi */
  init_pair (scrollbgcolor, COLOR_BLUE, COLOR_CYAN);	/* analyse scrollbgansi */
  init_pair (menuborder, COLOR_RED, COLOR_bg);
  init_pair (menuheader, COLOR_RED, COLOR_BLACK);
  init_pair (menuitem, COLOR_YELLOW, COLOR_BLACK);
  init_pair (HTMLcolor, COLOR_BLUE, COLOR_bg);	/* analyse HTMLansi */
  init_pair (HTMLcomment, COLOR_BLUE, COLOR_YELLOW);	/* analyse HTMLansi */
/*  init_pair (HTMLjsp, COLOR_CYAN, COLOR_BLACK); */
  init_pair (HTMLjsp, COLOR_CYAN, COLOR_bg);	/* analyse HTMLansi */
/* rotten C crap: value is short, parameter is short, so why this silly 
   warning "passing arg ... with different width due to prototype" ???
   (with gcc option -Wconversion)
*/
}

void
dim_on ()
{
	if (* dimansi != '\0') {
		attrset (COLOR_PAIR (dimcolor));
	} else {
		attrset (A_DIM);
	}
	if (dark_term) {
		attron (A_BOLD);
	}
}

void
dim_off ()
{
	standend ();
}


void
bold_on ()
{
	attrset (A_BOLD);
}

void
bold_off ()
{
	standend ();
}

void
disp_normal ()
{
	standend ();
}

void
disp_colour (c)
  int c;
{
}


void
unidisp_on ()
/* for non-displayable or illegal Unicode characters */
{
	if (* uniansi != '\0') {
		attrset (COLOR_PAIR (unicolor));
	} else {
		standout ();
	}
}

void
unidisp_off ()
{
	standend ();
}


void
unimarkdisp_on ()
/* for Unicode line end indications */
{
	if (* unimarkansi != '\0') {
		attrset (COLOR_PAIR (unimarkcolor));
	} else {
		standout ();
	}
}

void
unimarkdisp_off ()
{
	standend ();
}


void
combiningdisp_on ()
/* for Unicode combining character indication in separated display mode */
{
	if (* combiningansi != '\0') {
		attrset (COLOR_PAIR (combiningcolor));
	} else {
		standout ();
	}
}

void
combiningdisp_off ()
{
	standend ();
}


void
ctrldisp_on ()
{
	if (* ctrlansi != '\0') {
		attrset (COLOR_PAIR (ctrlcolor));
	} else {
		standout ();
	}
}

void
ctrldisp_off ()
{
	standend ();
}


void
dispHTML_code ()
{
	if (* HTMLansi != '\0') {
		attrset (COLOR_PAIR (HTMLcolor));
	} else {
		standout ();
	}
}

void
dispHTML_comment ()
{
	if (* HTMLansi != '\0') {
		attrset (COLOR_PAIR (HTMLcomment));
	} else {
		standout ();
	}
}

void
dispHTML_jsp ()
{
	if (* HTMLansi != '\0') {
		attrset (COLOR_PAIR (HTMLjsp));
	} else {
		standout ();
	}
}

void
dispHTML_off ()
{
	standend ();
}


void
menudisp_on ()
{
	if (* menuansi != '\0') {
		attrset (COLOR_PAIR (menucolor));
	} else {
		standout ();
	}
}

void
menudisp_off ()
{
	standend ();
}

void
diagdisp_on ()
{
	if (* diagansi != '\0') {
		attrset (COLOR_PAIR (diagcolor));
	} else {
		standout ();
	}
}

void
diagdisp_off ()
{
	standend ();
}


void
disp_scrollbar_foreground ()
{
	if (* scrollbgansi != '\0') {
		attrset (COLOR_PAIR (scrollfgcolor));
	} else {
		standout ();
	}
}

void
disp_scrollbar_background ()
{
	if (* scrollbgansi != '\0') {
		attrset (COLOR_PAIR (scrollbgcolor));
	}
}

void
disp_scrollbar_off ()
{
	standend ();
}


void
menuborder_on ()
{
	if (! use_normalchars_boxdrawing) {
		altcset_on ();
	}
#ifdef use_default_colour
	attrset (COLOR_PAIR (menuborder));
#endif
	in_menu_border = True;
}

void
menuborder_off ()
{
	standend ();
	if (! use_normalchars_boxdrawing) {
		altcset_off ();
	}
	in_menu_border = False;
}

void
menuitem_on ()
{
#ifdef use_default_colour
	attrset (COLOR_PAIR (menuitem));
#endif
}

void
menuitem_off ()
{
	standend ();
}

void
menuheader_on ()
{
	altcset_off ();
	attrset (COLOR_PAIR (menuheader) | A_BOLD);
	in_menu_border = False;
}

void
menuheader_off ()
{
	standend ();
	in_menu_border = False;
}


#else	/* ifdef CURSES */


#ifdef conio

void
dim_on ()
{
	/* check dimansi ? */
	textcolor (RED);
	if (dark_term) {
		highvideo ();
	}
}

void
dim_off ()
{
	normvideo ();
}


void
bold_on ()
{
	highvideo ();
}

void
bold_off ()
{
	normvideo ();
}

void
disp_normal ()
{
	normvideo ();
}

void
disp_colour (c)
  int c;
{
}


void
unidisp_on ()
/* for non-displayable or illegal Unicode characters */
{
	/* check uniansi ? */
	textbackground (CYAN);
}

void
unidisp_off ()
{
	normvideo ();
}


void
unimarkdisp_on ()
/* for Unicode line end indications */
{
	textcolor (CYAN);
}

void
unimarkdisp_off ()
{
	normvideo ();
}


void
combiningdisp_on ()
/* for Unicode combining character indication in separated display mode */
{
	textbackground (CYAN);
}

void
combiningdisp_off ()
{
	normvideo ();
}


void
ctrldisp_on ()
{
	reverse_on ();
}

void
ctrldisp_off ()
{
	reverse_off ();
}


void
dispHTML_code ()
{
	textcolor (LIGHTBLUE);
}

void
dispHTML_comment ()
{
	textcolor (LIGHTBLUE);
	textbackground (YELLOW);
}

void
dispHTML_jsp ()
{
	textcolor (CYAN);
	textbackground (BLACK);
}

void
dispHTML_off ()
{
	normvideo ();
}


void
menudisp_on ()
{
	reverse_on ();
}

void
menudisp_off ()
{
	reverse_off ();
}

void
diagdisp_on ()
{
	reverse_on ();
}

void
diagdisp_off ()
{
	reverse_off ();
}


void
disp_scrollbar_foreground ()
{
/*	textattr (CYAN);	*/
	textbackground (BLUE);
}

void
disp_scrollbar_background ()
{
/*	textattr (BLUE);	*/
	textbackground (CYAN);
}

void
disp_scrollbar_off ()
{
	normvideo ();
}


void
menuborder_on ()
{
	if (! use_normalchars_boxdrawing) {
		altcset_on ();
	}
	textattr (RED);
	highvideo ();
	in_menu_border = True;
}

void
menuborder_off ()
{
	normvideo ();
	textattr (norm_attr);
	if (! use_normalchars_boxdrawing) {
		altcset_off ();
	}
	in_menu_border = False;
}

void
menuitem_on ()
{
	textattr (YELLOW);
}

void
menuitem_off ()
{
	textattr (norm_attr);
}

void
menuheader_on ()
{
	altcset_off ();
	textattr ((inverse_attr & 0xF0) | RED);
	in_menu_border = False;
}

void
menuheader_off ()
{
	textattr (norm_attr);
	in_menu_border = False;
}

#else	/* ifdef conio */

static
FLAG
putansistring (s)
  char * s;
{
	if (ansi_esc && * s != '\0') {
		putstring ("\033[");
		putstring (s);
		putstring ("m");
		return True;
	} else {
		return False;
	}
}

void
dim_on ()
{
	termputstr (cMH, aff1);
	putansistring (dimansi);
}

void
dim_off ()
{
	if (! putansistring ("0")) {
		termputstr (cME, aff1);
	}
}


void
bold_on ()
{
	if (use_bold) {
		if (! putansistring ("1")) {
			termputstr (cMD, aff1);
		}
	}
}

void
bold_off ()
{
	if (use_bold) {
		if (! putansistring ("0")) {
			termputstr (cME, aff1);
		}
	}
}

void
disp_normal ()
{
	if (! putansistring ("0")) {
		reverse_off ();
	}
}

static
int
rgb (col)
  int col;
{
	if (col < 16) {
		if (col >= 8) {
			return rgb (col - 8);
		}
		switch (col) {
		case 0:	/* black */
			return 0x000000;
		case 1:	/* red */
			return 0xFF0000;
		case 2:	/* green */
			return 0x00FF00;
		case 3:	/* yellow */
			return 0xFFFF00;
		case 4:	/* blue */
			return 0x0000FF;
		case 5:	/* magenta */
			return 0xFF00FF;
		case 6:	/* cyan */
			return 0x00FFFF;
		case 7:	/* white */
			return 0xFFFFFF;
		default:	return 0;
		}
	} else if (col >= 232) {
		int gray = (col - 231) * 256 / 25;
		return gray << 16 | gray << 8 | gray;
	} else {
		int r, g, b;
		col -= 16;
		r = col / 36;
		g = (col % 36) / 6;
		b = col % 6;
		r *= 0x33;
		g *= 0x33;
		b *= 0x33;
		return r << 16 | g << 8 | b;
	}
}

static
int
R (rgb0)
  int rgb0;
{
	return rgb0 / 36;
}

static
int
G (rgb0)
  int rgb0;
{
	return (rgb0 % 36) / 6;
}

static
int
B (rgb0)
  int rgb0;
{
	return rgb0 % 6;
}

static
unsigned int
dist (rgb1, rgb2)
  int rgb1, rgb2;
{
	int dr = R (rgb1) - R (rgb2);
	int dg = G (rgb1) - G (rgb2);
	int db = B (rgb1) - B (rgb2);
	return dr * dr + dg * dg + db * db;
}

static
int
map8 (col)
  int col;
{
	unsigned int d = (unsigned int) -1;	/* max unsigned int */
	int i;
	int rgb_col = rgb (col);
	int repl_col = -1;

	if (col >= 244) {
		return 7;
	} else if (col >= 232) {
		return 0;
	}
	for (i = 0; i < 8; i ++) {
		unsigned int dist_col = dist (rgb (i), rgb_col);
		if (dist_col < d) {
			d = dist_col;
			repl_col = i;
		}
	}
	return repl_col;
}

void
disp_colour (c)
  int c;
{
	char ctrl [19];

	if (c < 8) {
		build_string (ctrl, "3%d", c);
	} else if (c < 16) {
		build_string (ctrl, "9%d", c - 8);
	} else {
		if (colours_256 || colours_88) {
			build_string (ctrl, "38;5;%d", c);
		} else {
			build_string (ctrl, "3%d", map8 (c));
		}
	}
	putansistring (ctrl);
}


void
unidisp_on ()
/* for non-displayable or illegal Unicode characters */
{
	if (! putansistring (uniansi)) {
		reverse_on ();
	}
}

void
unidisp_off ()
{
	if (* uniansi != '\0') {
		if (! putansistring ("0")) {
			reverse_off ();
		}
	} else {
		reverse_off ();
	}
}


void
unimarkdisp_on ()
/* for Unicode line end indications */
{
	if (! putansistring (unimarkansi)) {
		reverse_on ();
	}
}

void
unimarkdisp_off ()
{
	if (* unimarkansi != '\0') {
		if (! putansistring ("0")) {
			reverse_off ();
		}
	} else {
		reverse_off ();
	}
}


void
combiningdisp_on ()
/* for Unicode combining character indication in separated display mode */
{
	if (! putansistring (combiningansi)) {
		reverse_on ();
	}
}

void
combiningdisp_off ()
{
	if (* combiningansi != '\0') {
		if (! putansistring ("0")) {
			reverse_off ();
		}
	} else {
		reverse_off ();
	}
}


void
ctrldisp_on ()
{
	if (! putansistring (ctrlansi)) {
		reverse_on ();
	}
}

void
ctrldisp_off ()
{
	if (* ctrlansi != '\0') {
		if (! putansistring ("0")) {
			reverse_off ();
		}
	} else {
		reverse_off ();
	}
}


void
dispHTML_code ()
{
	if (! putansistring (HTMLansi)) {
		dim_on ();
	}
}

void
dispHTML_comment ()
{
	char ctrl [19];

	if (* HTMLansi != '\0') {
		build_string (ctrl, "%s;43", HTMLansi);
		if (! putansistring (ctrl)) {
			dim_on ();
		}
	} else {
		dim_on ();
	}
}

void
dispHTML_jsp ()
{
	if (* HTMLansi != '\0') {
		/*putansistring ("36");*/
		if (! putansistring ("36;40")) {
			dim_on ();
		}
	} else {
		dim_on ();
	}
}

void
dispHTML_off ()
{
	if (* HTMLansi != '\0') {
		if (! putansistring ("0")) {
			dim_off ();
		}
	} else {
		dim_off ();
	}
}


void
menudisp_on ()
{
	if (! putansistring (menuansi)) {
		reverse_on ();
	}
}

void
menudisp_off ()
{
	if (* menuansi != '\0') {
		if (! putansistring ("0")) {
			reverse_off ();
		}
	} else {
		reverse_off ();
	}
}

void
diagdisp_on ()
{
	if (! putansistring (diagansi)) {
		reverse_on ();
	}
}

void
diagdisp_off ()
{
	if (* diagansi != '\0') {
		if (! putansistring ("0")) {
			reverse_off ();
		}
	} else {
		reverse_off ();
	}
}


void
disp_scrollbar_foreground ()
{
	if (* scrollfgansi != '\0') {
		if (! putansistring (scrollfgansi)) {
			reverse_on ();
		}
	} else {
		putansistring (scrollbgansi);
		reverse_on ();
	}
}

void
disp_scrollbar_background ()
{
	reverse_off ();
	if (! putansistring (scrollbgansi)) {
		termputstr (cSO, aff1);
	}
}

void
disp_scrollbar_off ()
{
	if (* scrollbgansi != '\0') {
		if (! putansistring ("0")) {
			reverse_off ();
		}
	} else {
		reverse_off ();
	}
}


void
menuborder_on ()
{
  FLAG bold_border = use_bold;

	if (! use_normalchars_boxdrawing) {
		altcset_on ();
	} else if (menu_border_style == 'f' || menu_border_style == 'd') {
		bold_border = False;
	}
	if (bold_border) {
		bold_on ();
	}
	putansistring ("31");
	in_menu_border = True;
}

void
menuborder_off ()
{
	putansistring ("0");
	if (! use_normalchars_boxdrawing) {
		altcset_off ();
	}
	in_menu_border = False;
}

void
menuitem_on ()
{
	if (use_bgcolor) {
		/*putansistring ("30;43");*/
		/* this way to keep it visible on old buggy hanterm: */
/*
		if (use_bold) {
			if (! putansistring ("33;7;1;40")) {
				reverse_on ();
			}
		} else {
			if (! putansistring ("33;7;40")) {
				reverse_on ();
			}
		}
*/
		bold_on ();
		if (! putansistring ("33;7;40")) {
			reverse_on ();
		}
	} else {
		if (! putansistring ("0;1")) {
			reverse_on ();
		}
	}
}

void
menuitem_off ()
{
	if (! putansistring ("0")) {
		reverse_off ();
	}
}

void
menuheader_on ()
{
	altcset_off ();
/*
	bold_on ();
	putansistring ("31;40");
*/
	if (! putansistring ("7")) {
		reverse_on ();
	}
	in_menu_border = False;
}

void
menuheader_off ()
{
	if (! putansistring ("0")) {
		reverse_off ();
	}
	in_menu_border = False;
}

#endif	/* else ifdef conio */

#endif	/* else ifdef CURSES */


/*======================================================================*\
|*			include optional mouse API			*|
\*======================================================================*/

#ifdef dosmouse
#include "dosmouse.c"
#endif


/*======================================================================*\
|*				End					*|
\*======================================================================*/
