/*
    TTY interface file
    Copyright (c) 1993, 1994 Tudor Hulubei & Andrei Pitis

This file is part of UIT (UNIX Interactive Tools)

UIT is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 2, or (at your option) any later version.

UIT is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
details .

You should have received a copy of the GNU General Public License along with
UIT; see the file COPYING.  If not, write to the Free Software Foundation,
675 Mass Ave, Cambridge, MA 02139, USA.  */


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <sys/ioctl.h>

#ifdef HAVE_LINUX
#include <sys/stat.h>
#endif /* HAVE_LINUX */

#include "xmalloc.h"
#include "tty.h"
#include "misc.h"


#ifndef HAVE_CONST
#define const
#endif	/* HAVE_CONST */


int tgetent(void *__buffer, const char *__termtype);
int tputs(const char *__string, int __nlines, int (*outfun)());
char *tgetstr(const char *__name, char **__area);
char *tgoto(const char *__cstring, int __hpos, int __vpos);


#define TTY_INPUT	0
#define TTY_OUTPUT	1


/*
 * If tty_kbdmode == 1 single characters are inserted in the linked list.
 * This feature is used by uitps (no command line ... )
 */
static int tty_kbdmode = 0; 

static struct termios old_term;
static struct termios new_term;

static int tty_perm;
static int tty_cursor_x;
static int tty_cursor_y;
static int rows, columns;
static int tty_cursormove_notified;
static int fg_color, bg_color, br_status, rv_status, cursor_status;

static unsigned char tty_current_attribute;

static unsigned char *tty_screen = NULL;
static unsigned char *tty_attributes = NULL;


static unsigned char ansi_foreground[] = { 0x1b, '[', '3', '0', 'm' };
static unsigned char ansi_background[] = { 0x1b, '[', '4', '0', 'm' };

#ifdef HAVE_LINUX
static unsigned char lc_attr_off[] = { 0x1b, '[', 'm' };
static unsigned char lc_defaults[] = { 0x1b, '[', '0', 'm' };
#endif /* HAVE_LINUX */


extern int AnsiColorSequences;


#ifdef HAVE_LINUX
int LinuxConsole;
#endif /* HAVE_LINUX */


struct key_struct *key_list_head = NULL;
struct key_struct *current_key;
struct key_struct default_key;

static char tty_buf[MAX_CAPABILITY_LEN];
static int  tty_index = 0;

static char term_buf[2048];
static char vt100[] = "vt100";

int tty_mode = TTY_CANONIC;

char *tty_type;

char PC;	/* for tputs */
char *BC;	/* for tgoto */
char *UP;

#ifdef HAVE_LINUX
speed_t ospeed;
#else	/* not HAVE_LINUX */
short ospeed;
#endif	/*not HAVE_LINUX */


typedef struct
{
    char *capability;
    char *name;
    int  indispensable;
} TTY_CAPABILITY;


#define TTY_USED_CAPABILITIES	10

static TTY_CAPABILITY tty_capability[TTY_USED_CAPABILITIES] =
{
    { NULL, "me", 0 },		/* turn off all attributes */
    { NULL, "mr", 0 },		/* turn on reverse video mode */
    { NULL, "md", 0 },		/* turn on bold	*/
    { NULL, "vi", 0 },		/* make the cursor invisible */
    { NULL, "ve", 0 },		/* make the cursor appear normal */
    { NULL, "cl", 1 },		/* clear screen and home the cursor */
    { NULL, "cm", 1 },		/* move the cursor */
    { NULL, "pc", 0 },		/* padding character */
    { NULL, "up", 0 },		/* up one line */
    { NULL, "le", 0 },		/* move left one space */
};


#define TTY_ATTRIBUTES_OFF	(tty_capability[0].capability)
#define TTY_REVERSE_ON		(tty_capability[1].capability)
#define TTY_BRIGHT_ON		(tty_capability[2].capability)
#define TTY_CURSOR_OFF		(tty_capability[3].capability)
#define TTY_CURSOR_ON		(tty_capability[4].capability)
#define TTY_CLEAR_SCREEN	(tty_capability[5].capability)
#define TTY_CURSOR_MOVE		(tty_capability[6].capability)
#define TTY_PAD_CHAR		(tty_capability[7].capability)
#define TTY_UP_ONE_LINE		(tty_capability[8].capability)
#define TTY_LEFT_ONE_SPACE	(tty_capability[9].capability)


#define TTY_ATTRIBUTES_OFF_NAME	(tty_capability[0].name)
#define TTY_REVERSE_ON_NAME	(tty_capability[1].name)
#define TTY_BRIGHT_ON_NAME	(tty_capability[2].name)
#define TTY_CURSOR_OFF_NAME	(tty_capability[3].name)
#define TTY_CURSOR_ON_NAME	(tty_capability[4].name)
#define TTY_CLEAR_SCREEN_NAME	(tty_capability[5].name)
#define TTY_CURSOR_MOVE_NAME	(tty_capability[6].name)
#define TTY_PAD_CHAR_NAME	(tty_capability[7].name)
#define TTY_UP_ONE_LINE_NAME	(tty_capability[8].name)
#define TTY_LEFT_ONE_SPACE_NAME	(tty_capability[9].name)


#ifdef USE_TERMCAP

static char term_database[] = "termcap";
static char term_env[]	    = "TERMCAP";

#else	/* not USE_TERMCAP (USE_TERMINFO) */

static char term_database[] = "terminfo";
static char term_env[]	    = "TERMINFO";

#endif	/* not USE_TERMCAP (USE_TERMINFO) */


void fatal(char *);


static char *colors[10] = 
{
    "BLACK",
    "RED",
    "GREEN",
    "YELLOW",
    "BLUE",
    "MAGENTA",
    "CYAN",
    "WHITE",
    "OFF",
    "ON"
};


char key_ctrl_tbl[0x5f] = 
{
    0x20,   			/* 0x20 ( ) */
    0x21,   			/* 0x21 (!) */
    0x22,   			/* 0x22 (") */
    0x23,   			/* 0x23 (#) */
    0x24,   			/* 0x24 ($) */
    0x25,   			/* 0x25 (%) */
    0x26,   			/* 0x26 (&) */
    0x07,   			/* 0x27 (') */
    0x28,   			/* 0x28 (() */
    0x29,   			/* 0x29 ()) */
    0x2a,   			/* 0x2a (*) */
    0x2b,   			/* 0x2b (+) */
    0x2c,   			/* 0x2c (,) */
    0x2d,   			/* 0x2d (-) */
    0x2e,   			/* 0x2e (.) */
    0x2f,   			/* 0x2f (/) */
    0x20,   			/* 0x30 (0) */
    0x20,   			/* 0x31 (1) */
    0x20,   			/* 0x32 (2) */
    0x1b,   			/* 0x33 (3) */
    0x1c,  			/* 0x34 (4) */
    0x1d,   			/* 0x35 (5) */
    0x1e,   			/* 0x36 (6) */
    0x1f,   			/* 0x37 (7) */
    0x7f,   			/* 0x38 (8) */
    0x39,   			/* 0x39 (9) */
    0x3a,   			/* 0x3a (:) */
    0x3b,   			/* 0x3b (;) */
    0x3c,   			/* 0x3c (<) */
    0x20,   			/* 0x3d (=) */
    0x3e,   			/* 0x3e (>) */
    0x20,   			/* 0x3f (?) */
    0x20,   			/* 0x40 (@) */
    0x01,   			/* 0x41 (A) */
    0x02,   			/* 0x42 (B) */
    0x03,   			/* 0x43 (C) */
    0x04,   			/* 0x44 (D) */
    0x05,   			/* 0x45 (E) */
    0x06,   			/* 0x46 (F) */
    0x07,   			/* 0x47 (G) */
    0x08,   			/* 0x48 (H) */
    0x09,   			/* 0x49 (I) */
    0x0a,   			/* 0x4a (J) */
    0x0b,   			/* 0x4b (K) */
    0x0c,   			/* 0x4c (L) */
    0x0d,   			/* 0x4d (M) */
    0x0e,   			/* 0x4e (N) */
    0x0f,   			/* 0x4f (O) */
    0x10,   			/* 0x50 (P) */
    0x11,   			/* 0x51 (Q) */
    0x12,   			/* 0x52 (R) */
    0x13,   			/* 0x53 (S) */
    0x14,   			/* 0x54 (T) */
    0x15,   			/* 0x55 (U) */
    0x16,   			/* 0x56 (V) */
    0x17,   			/* 0x57 (W) */
    0x18,   			/* 0x58 (X) */
    0x19,   			/* 0x59 (Y) */
    0x1a,   			/* 0x5a (Z) */
    0x1b,   			/* 0x5b ([) */
    0x1c,   			/* 0x5c (\) */
    0x1d,   			/* 0x5d (]) */
    0x5e,   			/* 0x5e (^) */
    0x1f,   			/* 0x5f (_) */
    0x20,   			/* 0x60 (`) */
    0x01,   			/* 0x61 (a) */
    0x02,   			/* 0x62 (b) */
    0x03,   			/* 0x63 (c) */
    0x04,   			/* 0x64 (d) */
    0x05,   			/* 0x65 (e) */
    0x06,   			/* 0x66 (f) */
    0x07,   			/* 0x67 (g) */
    0x08,   			/* 0x68 (h) */
    0x09,   			/* 0x69 (i) */
    0x0a,   			/* 0x6a (j) */
    0x0b,   			/* 0x6b (k) */
    0x0c,   			/* 0x6c (l) */
    0x0d,   			/* 0x6d (m) */
    0x0e,   			/* 0x6e (n) */
    0x0f,   			/* 0x6f (o) */
    0x10,   			/* 0x70 (p) */
    0x11,   			/* 0x71 (q) */
    0x12,   			/* 0x72 (r) */
    0x13,   			/* 0x73 (s) */
    0x14,   			/* 0x74 (t) */
    0x15,   			/* 0x75 (u) */
    0x16,   			/* 0x76 (v) */
    0x17,   			/* 0x77 (w) */
    0x18,   			/* 0x78 (x) */
    0x19,   			/* 0x79 (y) */
    0x1a,   			/* 0x7a (z) */
    0x20,   			/* 0x7b ({) */
    0x20,   			/* 0x7c (|) */
    0x20,   			/* 0x7d (}) */
    0x20   			/* 0x7e (~) */
};


#define NO	0
#define YES	1


#define LINUX_VC_MAJOR	4

static int  keyno    = 0;
static int  keyindex = 0;
static char keybuf[1024];


void tty_startup(void)
{
    if (!isatty(TTY_INPUT) || !isatty(TTY_OUTPUT))
	fatal("only standard error can be redirected");

    tty_screen     = (unsigned char *)xcalloc(columns * rows,
    					      sizeof(unsigned char));
    tty_attributes = (unsigned char *)xcalloc(columns * rows,
    					      sizeof(unsigned char));
    tty_current_attribute = 0;

    tcgetattr(TTY_OUTPUT, &old_term);
}


void tty_kbdinit(int kbd_mode)
{
    default_key.key_seq  = xmalloc(16);
    default_key.aux_data = NULL;
    default_key.next     = NULL;
    tty_kbdmode 	 = kbd_mode;
}


void tty_set_mode(int mode)
{
    if (mode == TTY_NONCANONIC)
    {
	new_term = old_term;
	new_term.c_iflag &= ~(ICRNL | IGNCR | INLCR | IGNBRK | BRKINT);
	new_term.c_oflag &= ~OPOST;
	new_term.c_lflag |= ISIG | NOFLSH;
	new_term.c_lflag &= ~(ICANON | ECHO);
	new_term.c_cc[VMIN]  = 1;
	new_term.c_cc[VTIME] = 0;
	tcsetattr(TTY_OUTPUT, TCSADRAIN, &new_term);
	ospeed = cfgetospeed(&new_term);
    }
    else
	tcsetattr(TTY_OUTPUT, TCSADRAIN, &old_term);

    tty_mode = mode;
    tty_defaults();
}


void tty_exit(void)
{
    if (tty_mode == TTY_NONCANONIC)
    {
	tty_set_mode(TTY_CANONIC);
	tty_clrscr();
    }
    else
	tty_defaults();
}


char *tty_key_convert(char *key_seq)
{
    char *first, *second;

    first = second = key_seq;

    if (tty_kbdmode == 0 && *key_seq != '^') return NULL;

    while (*second)
    {
	if (*second == '^')
	    if (*++second)
		*first++ = key_ctrl_tbl[(*second++ & 0x7F) - ' '];
	    else
		return NULL;
	else
	    *first++ = *second++;
    }
    *first = 0;
    return key_seq;
}


int tty_out_char(int c)
{
    return tty_buf[tty_index++] = (char)c;
}


void tty_flush(void)
{
    write(TTY_OUTPUT, tty_buf, tty_index);
    tty_index = 0;
}


void tty_clrscr(void)
{
    memset(tty_screen, ' ', rows * columns * sizeof(unsigned char));
    memset(tty_attributes, tty_current_attribute,
    	   rows * columns * sizeof(unsigned char));
    tputs(TTY_CLEAR_SCREEN, rows, tty_out_char);
    tty_flush();
}


void tty_touch(void)
{
    memset(tty_screen, 0, rows * columns * sizeof(unsigned char));
}


void tty_cursormove(int y, int x)
{
    int old_rv_status;
    char *str;

    str = tgoto(TTY_CURSOR_MOVE, x, y);
    tputs(str, 1, tty_out_char);
    tty_flush();

    tty_cursor_x = x;
    tty_cursor_y = y;
}


void tty_cursormove_notify(int y, int x)
{
    tty_cursormove_notified = 1;
    tty_cursor_x = x;
    tty_cursor_y = y;
}


void tty_foreground(int color)
{
    char str[16];

    memcpy(str, ansi_foreground, sizeof(ansi_foreground));

    if (AnsiColorSequences == ON)
    {
	str[3] += (fg_color = color);
	write(TTY_OUTPUT, str, sizeof(ansi_foreground));
    }
    else
	tty_reverse((fg_color = color) != WHITE);

    tty_current_attribute = (tty_current_attribute & 0xF8) | fg_color;
}


void tty_background(int color)
{
    char str[16];

    memcpy(str, ansi_background, sizeof(ansi_background));

    if (AnsiColorSequences == ON)
    {
	str[3] += (bg_color = color);
	write(TTY_OUTPUT, str, sizeof(ansi_background));
    }
    else
	tty_reverse((bg_color = color) != BLACK);

    tty_current_attribute = (tty_current_attribute & 0xC7) | (bg_color << 3);
}


void tty_bright(int status)
{
    if (status == ON)
    {
	if (TTY_BRIGHT_ON)
	{
	    tputs(TTY_BRIGHT_ON, 1, tty_out_char);
	    tty_flush();
	}
    }
    else
    {
	if (TTY_ATTRIBUTES_OFF)
	{
	    tputs(TTY_ATTRIBUTES_OFF, 1, tty_out_char);
	    tty_flush();
	}
	br_status = OFF;
	/*
	 * There is no terminal capability for bright off  (or bold off).
	 * I am using the 'me' capability (where available)  which  turns
	 * off all attributes so we must restore the reverse status after
	 * that. There is no need to restore the  foreground & background
	 * colors because I've  always  put  tty_bright(status)  *BEFORE*
	 * tty_foreground(color) or tty_background(color).
	 */
	if (rv_status == ON) tty_reverse(ON);
    }

    br_status = status;

    tty_current_attribute = (tty_current_attribute & 0xBF) | (br_status << 6);
}


/* this should be static */

void tty_reverse(int status)
{
    if (status == ON)
    {
	if (TTY_REVERSE_ON)
	{
	    tputs(TTY_REVERSE_ON, 1, tty_out_char);
	    tty_flush();
	}
    }
    else
    {
	if (TTY_ATTRIBUTES_OFF)
	{
	    tputs(TTY_ATTRIBUTES_OFF, 1, tty_out_char);
	    tty_flush();
	}
	rv_status = OFF;
	/* same comment as in tty_bright() */
	if (br_status == ON) tty_bright(ON);
    }

    rv_status = status;
}


void tty_beep(void)
{
    char c = 7;
    write(TTY_OUTPUT, &c, 1);
}


void tty_cursor(int status)
{
    if (cursor_status = status)
    {
	if (TTY_CURSOR_ON)
	{
	    tputs(TTY_CURSOR_ON, 1, tty_out_char);
	    tty_flush();
	}
    }
    else
    {
	if (TTY_CURSOR_OFF)
	{
	    tputs(TTY_CURSOR_OFF, 1, tty_out_char);
	    tty_flush();
	}
    }
}


void tty_save(tty_status *status)
{
    status->fg_color	  = fg_color;
    status->bg_color	  = bg_color;
    status->br_status	  = br_status;
    status->rv_status	  = rv_status;
    status->cursor_status = cursor_status;
    status->attribute	  = tty_current_attribute;
}


void tty_restore(tty_status *status)
{
    tty_bright(status->br_status);
    tty_foreground(status->fg_color);
    tty_background(status->bg_color);
    tty_reverse(status->rv_status);
    tty_cursor(status->cursor_status);
    tty_current_attribute = status->attribute;
}


void tty_defaults(void)
{
#ifdef HAVE_LINUX
    if (LinuxConsole == ON)
	write(TTY_OUTPUT, lc_defaults, sizeof(lc_defaults));
    else
#endif	/* HAVE_LINUX */
	if (TTY_ATTRIBUTES_OFF)
	{
	    tputs(TTY_ATTRIBUTES_OFF, 1, tty_out_char);
	    tty_flush();
	}

    tty_foreground(WHITE);
    tty_background(BLACK);
    br_status = rv_status = OFF;

    tty_cursor(ON);
    
    tty_current_attribute = (br_status << 6) | (bg_color << 3) | fg_color;
}


int tty_write(char *buf, int length)
{
    unsigned char *tty_ptr;
    int i, tty_offset = (tty_cursor_y * columns) + tty_cursor_x;

    if (memcmp(tty_screen + tty_offset, buf, length) == 0)
    {
        for (tty_ptr = tty_attributes + tty_offset, i = 0; i < length; i++)
	    if (tty_ptr[i] != tty_current_attribute)
		goto write_needed;

	tty_cursor_x += length;
	return length;
    }

  write_needed:

    memcpy(tty_screen + tty_offset, buf, length);
    memset(tty_attributes + tty_offset, tty_current_attribute, length);
    if (tty_cursormove_notified)
	tty_cursormove(tty_cursor_y, tty_cursor_x);
    tty_cursormove_notified = 0;
    tty_cursor_x += length;
    return write(TTY_OUTPUT, buf, length);
}


int tty_read(char *buf, int length)
{
    return read(TTY_INPUT, buf, length);
}


char tty_getch(void)
{
    if (keyno) return keybuf[keyno--, keyindex++];
    for (keyindex = 0; (keyno = read(TTY_INPUT, keybuf, 1024)) < 0;);
    return keyno ? keybuf[keyno--, keyindex++] : 0;
}


int tty_putch(char c)
{
    return tty_write(&c, 1);
}


void tty_key_list_insert_sequence(struct key_struct **key,
				  char *key_seq, void *aux_data)
{
    struct key_struct *new_key;

    new_key = (struct key_struct *)xmalloc(sizeof(struct key_struct));
    new_key->key_seq = xmalloc(strlen(key_seq) + 1);
    strcpy(new_key->key_seq, key_seq);
    new_key->aux_data = aux_data;
    new_key->next = *key;
    *key = new_key;
}


void tty_key_list_insert(char *key_seq, void *aux_data)
{
    struct key_struct **key;

    if (*key_seq == 0) return;               /* bad key sequence ! */

    for (key = &key_list_head; *key; key = &(*key)->next)
	if (strcmp(key_seq, (*key)->key_seq) <= 0)
	{
	    tty_key_list_insert_sequence(key, key_seq, aux_data);
	    return;
	}

    tty_key_list_insert_sequence(key, key_seq, aux_data);
}


struct key_struct *tty_key_search(char *key_seq)
{
    int cmp;

    if (current_key == NULL) return NULL;

    for (; current_key; current_key = current_key->next)
    {
	cmp = strcmp(key_seq, current_key->key_seq);
	if (cmp == 0) return current_key;
	if (cmp  < 0) break;
    }

    if (current_key == NULL ||
    	strncmp(key_seq, current_key->key_seq, strlen(key_seq)))
        return (struct key_struct*) - 1;
    else
        return NULL;
}


void tty_key_search_restart(void)
{
    current_key = key_list_head;
}


/*
void tty_key_list_delete(void)
{
    struct key_struct *key, *next_key;

    for (key = key_list_head; key; key = next_key)
    {
	next_key = key->next;
	free(key->key_seq);
	free(key);
    }
}
*/


struct key_struct *tty_getkey(int *repeat_count)
{
    int i, j;
    struct key_struct *key;
    char c, key_seq[16];


  restart:

    while ((c = tty_getch()) == 0);

    if (repeat_count) *repeat_count = 1;

    if (!tty_kbdmode)
    {
	if (c == '\n' || c == '\r')
	{
	    default_key.key_seq[0] = key_ENTER;
	    default_key.key_seq[1] = 0;
	    return &default_key;
	}

	if (c == key_CTRL_H)
	{
	    default_key.key_seq[0] = key_BACKSPACE;
	    default_key.key_seq[1] = 0;
	    return &default_key;
	}

	if (is_print(c) || c == key_TAB || c == key_BACKSPACE)
	{
	    default_key.key_seq[0] = c;
	    default_key.key_seq[1] = 0;
	    return &default_key;
	}
    }

    tty_key_search_restart();

    for (j = 0; j < 15; j++)
    {
        key_seq[j    ] = c;
        key_seq[j + 1] = 0;

        key = tty_key_search(key_seq);
	if (key == (struct key_struct *)-1) goto restart;
	if (key) break;
	while ((c = tty_getch()) == 0);
    }

    if (repeat_count)
	while (keyno > j && (memcmp(key_seq, &keybuf[keyindex], j + 1) == 0))
	{
	    keyindex += j + 1;
	    keyno    -= j + 1;
	    (*repeat_count)++;
	}
    return key;
}


void tty_getsize(int *_columns, int *_rows)
{
    char *env;

#ifndef HAVE_SYSTEMV
    struct winsize winsz;
#endif /* HAVE_SYSTEMV */

    int temp_rows, temp_columns;

    columns = rows = temp_columns = temp_rows = 0;

#ifndef HAVE_SYSTEMV
    ioctl(TTY_OUTPUT, TIOCGWINSZ, &winsz);

    if (winsz.ws_col && winsz.ws_row)
    {
	temp_columns = winsz.ws_col;
	temp_rows    = winsz.ws_row;
    }
#endif /* HAVE_SYSTEMV */

    if (env = getenv("COLUMNS")) sscanf(env, "%d", &columns);
    if (env = getenv("LINES"))   sscanf(env, "%d", &rows);

#ifndef HAVE_SYSTEMV
    if (columns < 80) columns = temp_columns;
    if (rows    < 10) rows    = temp_rows;
#endif /* HAVE_SYSTEMV */

    if (columns < 80 || columns > MAX_TTY_COLUMNS) columns = 80;
    if (rows    < 10 || rows    > MAX_TTY_ROWS)    rows    = 24;

    *_columns = columns;
    *_rows    = rows;
}


void tty_getscreen(char *buf)
{
#ifdef HAVE_LINUX
    int x, y, index;

    if (LinuxConsole == ON)
    {
	buf[0] = 0;
	buf[1] = tty_name[tty_name_len - 1] - '0';

	if (ioctl(TTY_OUTPUT, TIOCLINUX, buf) == -1)
	{
	    buf[0] = buf[1] = 0;			
	    
	    /*
	     * Old Linux bug: bad ioctl on console 8. I think this has been
	     * fixed in the current Linux kernel, but the test won't hurt.
	     */
	    
	    if (ioctl(TTY_OUTPUT, TIOCLINUX, buf) == -1)
	    {
		tty_perm = 0;
		return;
	    }
	}

	tty_perm = 1;
	index = 2 + rows * columns;

	for (y = rows - 1; y >= 0; y--)
	    for (x = columns - 1; x >= 0; x--)
	        if (buf[--index] != ' ')
	            goto found;
      found:
	
	buf[0] = y + 1;
	buf[1] = 0; 
	tty_cursormove(y + 1, 0);
    }
#endif	/* HAVE_LINUX */
}


void tty_putscreen(char *buf)
{    
    tty_defaults();
    tty_cursormove(0, 0);

#ifdef HAVE_LINUX
    if (LinuxConsole == ON)
    {
	if (tty_perm)
	{
	    tty_touch();
	    write(TTY_OUTPUT, buf + 2, rows * columns);
	    tty_cursormove(buf[0], buf[1]);
	}
	else
	{
	    tty_defaults();
	    tty_clrscr();
	}
    }
    else
    {
	tty_defaults();
	tty_clrscr();
    }
#else	/* not HAVE_LINUX */
    tty_defaults();
    tty_clrscr();
#endif	/* not HAVE_LINUX */
}


int tty_getcolorindex(char *colorname)
{
    int i;

    for (i = 0; i < 10; i++)
        if (strcmp(colors[i], colorname) == 0)
            return (i < 8) ? i : (i - 8);
    return -1;
}



void tty_get_capabilities(void)
{
#ifdef HAVE_LINUX
    struct stat statbuf;
#endif /* HAVE_LINUX */
    char *capability_buf, *tmp;
    int err, i, term_errors = 0;
    char *termtype = getenv("TERM");

#ifdef HAVE_LINUX
    fstat(TTY_OUTPUT, &statbuf);
    if ((statbuf.st_rdev >> 8) == LINUX_VC_MAJOR &&
        ((unsigned)(statbuf.st_rdev & 0xFF)) <= 8)
	LinuxConsole = ON;
    else
	LinuxConsole = OFF;
#endif /* HAVE_LINUX */

    if (termtype == NULL)
    {
	fprintf(stderr, "%s: can't find the TERM environment variable, ",
		program);
	goto switch_to_vt100;
    }

    if (strlen(termtype) > 63)
    {
	fprintf(stderr, "%s: the TERM environment variable is too long, ",
		program);
      switch_to_vt100:
	fprintf(stderr, "trying vt100 ...\n");
	termtype = vt100;
    }

    err = tgetent(term_buf, termtype);
    
    if (err == -1)
    {
	fprintf(stderr, "%s: can't find the %s database.\n",
		program, term_database);
	fprintf(stderr, "%s: check your %s environment variable ...\n",
		program, term_env);
	exit(1);
    }

    if (err == 0)
    {
	fprintf(stderr,
		"%s: can't find the terminal type %s in the %s database.\n",
		program, termtype, term_database);
	exit(1);
    }

    tty_type = xmalloc(strlen(termtype) + 1);
    strcpy(tty_type, termtype);

#ifdef USE_TERMCAP
    capability_buf = xmalloc(strlen(term_buf));
#else	/* not USE_TERMCAP (USE_TERMINFO) */
    /* we are using only 10 capabilities, so we don't need much space */
    capability_buf = xmalloc(512);
#endif	/* not USE_TERMCAP (USE_TERMINFO) */

    tmp = tgetstr(TTY_PAD_CHAR_NAME, &capability_buf);
    PC = tmp ? *tmp : 0;

    BC = tgetstr(TTY_LEFT_ONE_SPACE_NAME, &capability_buf);
    UP = tgetstr(TTY_UP_ONE_LINE_NAME,    &capability_buf);

    if (BC == NULL || UP == NULL)
	BC = UP = NULL;

    TTY_ATTRIBUTES_OFF = tgetstr(TTY_ATTRIBUTES_OFF_NAME, &capability_buf);
    TTY_REVERSE_ON     = tgetstr(TTY_REVERSE_ON_NAME,	  &capability_buf);
    TTY_BRIGHT_ON      = tgetstr(TTY_BRIGHT_ON_NAME,	  &capability_buf);

    if (TTY_ATTRIBUTES_OFF == NULL)
    {
#ifdef HAVE_LINUX
	if (LinuxConsole)
	    TTY_ATTRIBUTES_OFF = lc_attr_off;	/* I *really* hate this ! */
	else
#endif /* HAVE_LINUX */
	    TTY_REVERSE_ON = TTY_BRIGHT_ON = NULL;
    }

    TTY_CURSOR_OFF = tgetstr(TTY_CURSOR_OFF_NAME, &capability_buf);
    TTY_CURSOR_ON  = tgetstr(TTY_CURSOR_ON_NAME,  &capability_buf);

    if (TTY_CURSOR_OFF == NULL || TTY_CURSOR_ON == NULL)
	TTY_CURSOR_ON = TTY_CURSOR_OFF = NULL;

    TTY_CLEAR_SCREEN = tgetstr(TTY_CLEAR_SCREEN_NAME, &capability_buf);
    TTY_CURSOR_MOVE  = tgetstr(TTY_CURSOR_MOVE_NAME,  &capability_buf);

    for (i = 0; i < TTY_USED_CAPABILITIES; i++)
	if (tty_capability[i].capability == NULL)
	    if (tty_capability[i].indispensable)
	    {
		term_errors++;
		fprintf(stderr,
			"%s: can't find the '%s' terminal capability.\n",
			program, tty_capability[i].name);
	    }

    if (term_errors)
    {
	fprintf(stderr, "%s: %d errors. Your terminal is too dumb :-< .\n",
		program, term_errors);
	exit(1);
    }
}
