/* Copyright 1992 John Bovey, University of Kent at Canterbury.
 *
 *  You can do what you like with this source code as long as
 *  you don't try to make money out of it and you include an
 *  unaltered copy of this message (including the copyright).
 * 
 *
 * This module has been very heavily modified by R. Nation
 * (nation@rocket.sanders.lockheed.com).
 * No additional restrictions are applied
 *
 * Additional modification by Garrett D'Amore (garrett@netcom.com) to
 * allow vt100 printing.  No additional restrictions are applied.
 *
 * As usual, the author accepts no responsibility for anything, nor does
 * he guarantee anything whatsoever.
 * 
 *
 * Big5 Chinese extension by Edward Der-Hua Liu, Taiwan
 * It is distributed under GPL
 */

#include <stdarg.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <grp.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <ctype.h>

#ifdef ALPHA
#define FREEBSD
#endif
#ifdef AIXV3
#include <sys/select.h>
#endif
#ifdef HPUX
#include <sys/bsdtty.h>
#endif
#ifdef SVR4
#include <sys/stropts.h>
#define _NEW_TTY_CTRL
#endif
#ifdef AIXV3
#include <termio.h>
#else
#include <termios.h>
#endif				/* AIXV3 */

#if defined FREEBSD || defined LINUX
#include <sys/ioctl.h>
#endif

#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>

#include "rxvt.h"
#include "command.h"
#include "screen.h"
#include "xsetup.h"
#include "sbar.h"

#if defined(VDISCRD) && !defined(VDISCARD)
#define	VDISCARD	VDISCRD
#endif

#if defined(VWERSE) && !defined(VWERASE)
#define	VWERASE		VWERSE
#endif

extern char *window_name;
extern char *icon_name;

/* for greek keyboard mapping */
#ifdef GREEK_KBD
#include "grkelot.h"

static  greek_mode_ON = False;

#endif

int     size_set = 0;		/* flag set once the window size has been set */

#define KBUFSIZE	256	/* size of keyboard mapping buffer */
#define COM_BUF_SIZE   2048	/* size of buffer used to read from command */
#define STRING_MAX      512
#define ARGS_MAX         20	/* maximum # of args for esc sequences */

/*  Special character returned by get_com_char().
 */
#define GCC_NULL	0x100	/* Input buffer is empty */
#define ESC		033

/*  Flags used to control get_com_char();
 */
#define BUF_ONLY	1

/*  Global variables that are set up at the beginning and then not changed
 */
extern Display *display;
extern Window vt_win;
extern Window main_win;
extern struct sbar_info sbar;

extern int login_shell;
extern int MetaHandling;

#ifdef MAPALERT
extern int map_alert;
KeySym  AlertKeysym = XK_i;
#endif

int     comm_fd = -1;		/* file descriptor connected to the command */
int     comm_pid;		/* process id if child */
static int x_fd;		/* file descriptor of the X server connection */
static int fd_width;		/* width of file descriptors being used */
static int app_cur_keys = 0;	/* flag to set cursor keys in application mode */
static int app_kp_keys = 0;	/* flag to set application keypad keys */

#ifndef SVR4
static char ptynam[] = "/dev/ptyxx";
char    ttynam[] = "/dev/ttyxx";

#else
char   *ttynam, *ptsname();

#endif
static Atom wm_del_win;
extern WindowInfo MyWinInfo;

extern int fat_sbar;
extern int console;

KeySym  SecureKeysym = XK_s;
KeySym  BigFontKeysym = XK_greater;
KeySym  SmallFontKeysym = XK_less;
KeySym  PageUpKeysym = XK_Prior;
KeySym  PageDownKeysym = XK_Next;

#ifdef GREEK_KBD
KeySym  GreekSwitchKeysym = XK_Mode_switch;
int     GreekMode = GREEK_ELOT928;

#endif
#ifdef PRINT_PIPE
KeySym  PrintScreenKeysym = XK_Print;

#endif

int     refresh_nl_count = 0;
int     refresh_nl_limit = 1;
extern int refresh_type;

/*  Terminal mode structures.
 */
static struct termios ttmode;

#ifndef FREEBSD
#ifndef CINTR
#ifndef _POSIX_VDISABLE
#define _POSIX_VDISABLE 0
#endif

#undef CTRL
#define CTRL(c) ((c) - '@')

#ifdef SVR4
#define CINTR 0177
#define CQUIT CTRL('U')
#define CERASE CTRL('H')
#else
#define CINTR CTRL('C')
#define CQUIT CTRL('\\')
#define CERASE 0177
#endif

#define CEOF CTRL('D')
#define CKILL CTRL('U')
#define CEOL _POSIX_VDISABLE
#define CEOL2 _POSIX_VDISABLE
#define CNSWTCH _POSIX_VDISABLE
#define CSTART CTRL('Q')
#define CSTOP CTRL('S')
#define CSUSP CTRL('Z');
#define CDSUSP _POSIX_VDISABLE
#define CRPRNT CTRL('R')
#define CFLUSH CTRL('O')
#define CWERASE CTRL('W')
#define CLNEXT CTRL('V')
#endif
#endif

#ifndef CEOL
#define CEOL _POSIX_VDISABLE
#endif

/*  Variables used for buffered command input.
 */
static unsigned char com_buf[COM_BUF_SIZE];
static unsigned char *com_buf_next, *com_buf_top;
unsigned char mask = 0xff;

static void catch_child(int);
static void catch_sig(int);
static int run_command(unsigned char *, unsigned char **);
static unsigned char *lookup_key(XEvent *, int *);
static int get_com_char(int);
void    get_X_event(void);
void    process_string(int);

#ifdef PRINT_PIPE
void    process_print_pipe(void);
void    process_print_screen(int);
#endif

void    process_escape_sequence(void);
void    process_csi_sequence(void);
void    process_xterm_sequence(void);
void    process_terminal_mode(int, int, int, int *);
void    process_sgr_mode(int, int, int, int *);

void Gr_expose(Window win);
void Gr_ReportButtonPress(int x, int y);
void Gr_ReportButtonRelease(int x, int y);
void Gr_do_graphics(char c, int nargs, int *args, unsigned char *text);
int send_key(Display *display, Window win, XKeyEvent *eve, char *buf);
void send_FocusIn(Display *display, Window window);
void send_FocusOut(Display *display, Window window);

struct stat ttyfd_stat;
int     ttyfd;

extern int busy_refresh;

#ifdef SVR4
#define _NEW_TTY_CTRL
#include <sys/resource.h>
int static getdtablesize()
{
    struct rlimit rlp;

    getrlimit(RLIMIT_NOFILE, &rlp);
    return (rlp.rlim_cur);
}
#endif


/*  Catch a SIGCHLD signal and exit if the direct child has died.
 */
static void catch_child(int nonsense)
{
    if (wait((int *) NULL) == comm_pid) {
	clean_exit(0);
    }
}

/*  Catch a fatal signal and tidy up before quitting
 */
static void catch_sig(int sig)
{
    signal(sig, SIG_DFL);
    cleanutent();
    setuid(getuid());
    kill(getpid(), sig);
}

/*  Run the command in a subprocess and return a file descriptor for the
 *  master end of the pseudo-teletype pair with the command talking to
 *  the slave.
 */
static int run_command(unsigned char *command, unsigned char **argv)
{
    int     ptyfd;
    int     uid;
    int     i;
    int     width, height;
#ifndef SVR4
    int     gid;
    unsigned char *s3, *s4;
    static char ptyc3[] = "pqrstuvwxyz";
    static char ptyc4[] = "0123456789abcdef";
#endif
    unsigned char argv0[256];

    /*  First find a master pty that we can open.  
     */

#ifdef SVR4
    ptyfd = open("/dev/ptmx", O_RDWR);
    if (ptyfd < 0) {
	error("Can't open a pseudo teletype");
	return (-1);
    }
    grantpt(ptyfd);
    unlockpt(ptyfd);
    fcntl(ptyfd, F_SETFL, O_NDELAY);
    ttynam = ptsname(ptyfd);
#else
    ptyfd = -1;
    for (s3 = ptyc3; *s3 != 0; s3++) {
	for (s4 = ptyc4; *s4 != 0; s4++) {
	    ptynam[8] = ttynam[8] = *s3;
	    ptynam[9] = ttynam[9] = *s4;
	    if ((ptyfd = open(ptynam, O_RDWR)) >= 0) {
		if (geteuid() == 0 || access(ttynam, R_OK | W_OK) == 0)
		    break;
		else {
		    close(ptyfd);
		    ptyfd = -1;
		}
	    }
	}
	if (ptyfd >= 0)
	    break;
    }
    if (ptyfd < 0) {
	error("Can't open a pseudo teletype");
	return (-1);
    }
    fcntl(ptyfd, F_SETFL, O_NDELAY);
#endif
    for (i = 1; i <= 15; i++)
	signal(i, catch_sig);
    signal(SIGCHLD, catch_child);
    signal(SIGALRM, blink_refresh);
    lstat(ttynam, &ttyfd_stat);

    makeutent(&ttynam[5]);	/* stamp /etc/utmp */

    comm_pid = fork();
    if (comm_pid < 0) {
	error("Can't fork");
	return (-1);
    }
    if (comm_pid == 0) {
#ifndef SVR4
	struct group *gr;
#endif

/* FIXME: I (ah) don't know whether unsetenv is defined by these */
/* I don't think its very portable: I'll use #ifdef UNSETENV
 * RN */
#ifdef UNSETENV
	/*
	 * bash may set these env vars, but they should be clear for the child
	 * otherwise the old settings are passed on and will confuse term size 
	 */
	unsetenv("LINES");
	unsetenv("COLUMNS");
#endif

	if ((ttyfd = open(ttynam, O_RDWR)) < 0) {
	    error("could not open slave tty %s", ttynam);
	    clean_exit(1);
	}
#ifdef SVR4
	ioctl(ttyfd, I_PUSH, "ptem");
	ioctl(ttyfd, I_PUSH, "ldterm");
#endif

	uid = getuid();
#ifndef SVR4
	if ((gr = getgrnam("tty")) != NULL)
	    gid = gr->gr_gid;
	else
	    gid = -1;
	fchown(ttyfd, uid, gid);
	fchmod(ttyfd, 0600);
#endif
#ifdef TIOCCONS
	if (console) {
	    int     on = 1;

	    if (ioctl(ttyfd, TIOCCONS, (unsigned char *) &on) == -1)
		fprintf(stderr, "xvt: cannot open console\n");
	}
#endif				/* TIOCCONS */
#ifdef HPUX
	for (i = 0; i < sysconf(_SC_OPEN_MAX); i++)
	    if (i != ttyfd)
		close(i);
#else
	for (i = 0; i < getdtablesize(); i++)
	    if (i != ttyfd)
		close(i);
#endif

	dup(ttyfd);
	dup(ttyfd);
	dup(ttyfd);

	if (ttyfd > 2)
	    close(ttyfd);

	if (setsid() < 0)
	    perror("failed to set process group");
#if defined(TIOCSCTTY)
	ioctl(0, TIOCSCTTY, 0);
#endif
	{
	    int     pgrp = getpid();

	    ioctl(0, TIOCSPGRP, (char *) &pgrp);
/*      setpgrp(); */
	    close(open(ttynam, O_WRONLY, 0));
/*      setpgrp (); */
	}

	/* init of termios structure              */
#ifdef FREEBSD
	ioctl(0, TIOCGETA, (char *) &ttmode);
#else
#ifdef HPUX
	tcgetattr(0, &ttmode);
#else
	ioctl(0, TCGETS, (char *) &ttmode);
#endif
#endif

#ifdef HPUX
	ttmode.c_iflag = BRKINT | IGNPAR | ICRNL | IXON;
	ttmode.c_lflag = ISIG | IEXTEN | ICANON | ECHO | ECHOE | ECHOK;
#else
	ttmode.c_iflag = BRKINT | IGNPAR | ICRNL | IXON | IMAXBEL;
	ttmode.c_lflag = ISIG | IEXTEN | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE;
#endif
	ttmode.c_oflag = OPOST | ONLCR;
	ttmode.c_cflag = B9600 | CS8 | CREAD;

	ttmode.c_cc[VEOF] = CEOF;
#ifdef ALPHA
	(unsigned) ttmode.c_cc[VEOL] = CEOL;
#else
	ttmode.c_cc[VEOL] = CEOL;
#endif
	ttmode.c_cc[VINTR] = CINTR;
	ttmode.c_cc[VQUIT] = CQUIT;
	ttmode.c_cc[VERASE] = CERASE;
	ttmode.c_cc[VKILL] = CKILL;
#ifdef HPUX
	ttmode.c_cc[VSUSP] = CSWTCH;
#else
	ttmode.c_cc[VSUSP] = CSUSP;
#endif
#ifdef VDSUSP
	ttmode.c_cc[VDSUSP] = CDSUSP;
#endif
	ttmode.c_cc[VSTART] = CSTART;
	ttmode.c_cc[VSTOP] = CSTOP;
#ifdef VREPRINT
	ttmode.c_cc[VREPRINT] = CRPRNT;
#endif
#ifdef VDISCARD
	ttmode.c_cc[VDISCARD] = CFLUSH;
#endif
#ifdef VWERASE
	ttmode.c_cc[VWERASE] = CWERASE;
#endif
#ifdef VLNEXT
	ttmode.c_cc[VLNEXT] = CLNEXT;
#endif
#ifdef VSWTC
	ttmode.c_cc[VSWTC] = 0;
#endif

#ifdef VSWTCH
	ttmode.c_cc[VSWTCH] = 0;
#endif
#if VMIN != VEOF
	ttmode.c_cc[VMIN] = 1;
#endif
#if VTIME != VEOL
	ttmode.c_cc[VTIME] = 0;
#endif

	if (mask == 0x7f)
	    ttmode.c_cflag = B9600 | PARENB | CS7 | CREAD;
#ifdef FREEBSD
	ioctl(0, TIOCSETA, (char *) &ttmode);
#else
#ifdef HPUX
	tcsetattr(0, TCSANOW, &ttmode);
#else
	ioctl(0, TCSETS, (char *) &ttmode);
#endif
#endif
	scr_get_size(&width, &height);
	tty_set_size(0, width, height);
	if (login_shell) {
	    char   *tmp;

	    strcpy(argv0, "-");
	    if ((tmp = strrchr(argv[0], '/')) == NULL)
		tmp = argv[0];
	    else
		tmp++;
	    strcat(argv0, tmp);
	    argv[0] = argv0;
	}
	setgid(getgid());
	setuid(uid);

	execvp(command, (char **) argv);
	error("Couldn't execute %s", command);
	clean_exit(1);
    }
    return (ptyfd);
}

/*  Tell the teletype handler what size the window is.  Called after a window
 *  size change.
 */
void    tty_set_size(int fd, int width, int height)
{
    struct winsize wsize;

    if (fd < 0)
	return;

    wsize.ws_row = (unsigned short) height;
    wsize.ws_col = (unsigned short) width;
    ioctl(fd, TIOCSWINSZ, (char *) &wsize);
}

/*  Initialise the command connection.  This should be called after the X
 *  server connection is established.
 */
void    init_command(unsigned char *command, unsigned char **argv)
{
#ifdef GREEK_KBD
    greek_init(GreekMode);
    /*atexit(greek_end);  not necessary */
#endif
    /*  Enable the delete window protocol.
     */

    wm_del_win = XInternAtom(display, "WM_DELETE_WINDOW", False);
    XSetWMProtocols(display, main_win, &wm_del_win, 1);

    if ((comm_fd = run_command(command, argv)) < 0) {
	error("Quitting");
	clean_exit(1);
    }

    x_fd = XConnectionNumber(display);
#ifdef HPUX
    fd_width = sysconf(_SC_OPEN_MAX);
#else
    fd_width = getdtablesize();
#endif

    com_buf_next = com_buf_top = com_buf;
}

/*  Convert the keypress event into a string.
 */
static unsigned char *lookup_key(XEvent * ev, int *pcount)
{
    KeySym  keysym;
    static XComposeStatus compose =
    {NULL, 0};
    int     count;
    static unsigned char kbuf[KBUFSIZE];
    unsigned char *s, *c;
    int     meta;
    unsigned int state;

    count = XLookupString(&ev->xkey, kbuf, KBUFSIZE - 1, &keysym, &compose);
    kbuf[count] = (unsigned char) 0;
    meta = (state = ev->xkey.state) & Mod1Mask;
    s = NULL;
    if ((keysym == SecureKeysym) && ((state & Mod1Mask) == Mod1Mask)) {
	scr_secure();
	count = 0;
    }
#if	0
    else if ((keysym == BigFontKeysym) && ((state & Mod1Mask) == Mod1Mask)) {
	NewFont(BIGGER);
	count = 0;
    }
    else if ((keysym == SmallFontKeysym) && ((state & Mod1Mask) == Mod1Mask)) {
	NewFont(SMALLER);
	count = 0;
    }
#endif	/* 0 */
#ifdef XTERM_SCROLL
    else if ((keysym == PageUpKeysym) && ((state & ShiftMask) == ShiftMask))
#else
    else if ((keysym == PageUpKeysym) && ((state & Mod1Mask) == Mod1Mask))
#endif
    {
	scr_move_up_down(UP);
	count = 0;
    }
#ifdef XTERM_SCROLL
    else if ((keysym == PageDownKeysym) && ((state & ShiftMask) == ShiftMask))
#else
    else if ((keysym == PageDownKeysym) && ((state & Mod1Mask) == Mod1Mask))
#endif
    {
	scr_move_up_down(DOWN);
	count = 0;
    }
#ifdef MAPALERT
    else if ((keysym == AlertKeysym) && ((state & Mod1Mask) == Mod1Mask)) {
	map_alert = !map_alert;
	count = 0;
    }
#endif
#ifdef GREEK_KBD
    else if (keysym == GreekSwitchKeysym) {
	greek_mode_ON = !greek_mode_ON;
	if (greek_mode_ON) {
	    char   *s = malloc(strlen(window_name) + 12);

	    sprintf(s, "%s [GRK %s]", window_name, (GreekMode == GREEK_ELOT928) ? "elot" : "437");
	    change_window_name(s);
	    free(s);
	    greek_reset();
	}
	else
	    change_window_name(window_name);
	count = 0;
    }
#endif
#ifdef PRINT_PIPE
    else if (keysym == PrintScreenKeysym) {
	process_print_screen((ev->xkey.state & ControlMask) != 0);
	count = 0;
    }
#endif
    else {
	count = -1;
	switch (keysym) {
	case XK_Up:
	    strcpy(kbuf, (app_cur_keys ? "\033OA" : "\033[A"));
	    break;
	case XK_Down:
	    strcpy(kbuf, app_cur_keys ? "\033OB" : "\033[B");
	    break;
	case XK_Right:
	    strcpy(kbuf, app_cur_keys ? "\033OC" : "\033[C");
	    if (cursor_rr())
		strcat(kbuf, app_cur_keys ? "\033OC" : "\033[C");
	    break;
	case XK_Left:
	    strcpy(kbuf, app_cur_keys ? "\033OD" : "\033[D");
	    if (cursor_ll())
		strcat(kbuf, app_cur_keys ? "\033OD" : "\033[D");
	    break;
	case XK_KP_F1:
	    strcpy(kbuf, "\033OP");
	    break;
	case XK_KP_F2:
	    strcpy(kbuf, "\033OQ");
	    break;
	case XK_KP_F3:
	    strcpy(kbuf, "\033OR");
	    break;
	case XK_KP_F4:
	    strcpy(kbuf, "\033OS");
	    break;
	case XK_KP_0:
	    strcpy(kbuf, app_kp_keys ? "\033Op" : "0");
	    break;
	case XK_KP_1:
	    strcpy(kbuf, app_kp_keys ? "\033Oq" : "1");
	    break;
	case XK_KP_2:
	    strcpy(kbuf, app_kp_keys ? "\033Or" : "2");
	    break;
	case XK_KP_3:
	    strcpy(kbuf, app_kp_keys ? "\033Os" : "3");
	    break;
	case XK_KP_4:
	    strcpy(kbuf, app_kp_keys ? "\033Ot" : "4");
	    break;
	case XK_KP_5:
	    strcpy(kbuf, app_kp_keys ? "\033Ou" : "5");
	    break;
	case XK_KP_6:
	    strcpy(kbuf, app_kp_keys ? "\033Ov" : "6");
	    break;
	case XK_KP_7:
	    strcpy(kbuf, app_kp_keys ? "\033Ow" : "7");
	    break;
	case XK_KP_8:
	    strcpy(kbuf, app_kp_keys ? "\033Ox" : "8");
	    break;
	case XK_KP_9:
	    strcpy(kbuf, app_kp_keys ? "\033Oy" : "9");
	    break;
	case XK_KP_Subtract:
	    strcpy(kbuf, app_kp_keys ? "\033Om" : "-");
	    break;
	case XK_KP_Separator:
	    strcpy(kbuf, app_kp_keys ? "\033Ol" : ",");
	    break;
	case XK_KP_Decimal:
	    strcpy(kbuf, app_kp_keys ? "\033On" : ".");
	    break;
	case XK_KP_Enter:
	    strcpy(kbuf, app_kp_keys ? "\033OM" : "\r");
	    break;
	case XK_Home:
	    if (state & ControlMask)
		strcpy(kbuf, "\x0bU");
	    else
		strcpy(kbuf, "\033[H");
	    break;
	case XK_End:
	    if (state & ControlMask)
		strcpy(kbuf, "\x0bV");
	    else
		strcpy(kbuf, "\033Ow");
	    break;
	case XK_F1:
	    strcpy(kbuf, "\033[11~");
	    break;
	case XK_F2:
	    strcpy(kbuf, "\033[12~");
	    break;
	case XK_F3:
	    strcpy(kbuf, "\033[13~");
	    break;
	case XK_F4:
	    strcpy(kbuf, "\033[14~");
	    break;
	case XK_F5:
	    strcpy(kbuf, "\033[15~");
	    break;
	case XK_F6:
	    strcpy(kbuf, "\033[17~");
	    break;
	case XK_F7:
	    strcpy(kbuf, "\033[18~");
	    break;
	case XK_F8:
	    strcpy(kbuf, "\033[19~");
	    break;
	case XK_F9:
	    strcpy(kbuf, "\033[20~");
	    break;
	case XK_F10:
	    strcpy(kbuf, "\033[21~");
	    break;
	case XK_F11:
	    strcpy(kbuf, "\033[23~");
	    break;
	case XK_F12:
	    strcpy(kbuf, "\033[24~");
	    break;
	case XK_F13:
	    strcpy(kbuf, "\033[25~");
	    break;
	case XK_F14:
	    strcpy(kbuf, "\033[26~");
	    break;
	case XK_Help:
	case XK_F15:
	    strcpy(kbuf, "\033[28~");
	    break;
	case XK_Menu:
	case XK_F16:
	    strcpy(kbuf, "\033[29~");
	    break;
	case XK_F17:
	    strcpy(kbuf, "\033[31~");
	    break;
	case XK_F18:
	    strcpy(kbuf, "\033[32~");
	    break;
	case XK_F19:
	    strcpy(kbuf, "\033[33~");
	    break;
	case XK_F20:
	    strcpy(kbuf, "\033[34~");
	    break;
	case XK_Find:
	    strcpy(kbuf, "\033[1~");
	    break;
	case XK_Insert:
	    strcpy(kbuf, "\033[2~");
	    break;
	case XK_Execute:
	    strcpy(kbuf, "\033[3~");
	    break;
	case XK_Select:
	    strcpy(kbuf, "\033[4~");
	    break;
	case XK_Prior:
	    strcpy(kbuf, "\033[5~");
	    break;
	case XK_Next:
	    strcpy(kbuf, "\033[6~");
	    break;
	case XK_BackSpace:
	    if (state & ControlMask) {
		strcpy(kbuf, "\x19");
	    }
	    else if (cursor_ll()) {
		strcat(kbuf, "\x08");
	    }
	    break;
	case XK_Delete:
	    {
		static char *del = "\x7f";

		strcpy(kbuf, del);
		if (cursor_rr())
		    strcat(kbuf, del);
		break;
	    }
	case 'T':
	case 't':
	    if ((state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask)) {
		extern int auto_cursor;

		auto_cursor ^= 1;
		count = 0;
	    }
	case 'O':
	case 'o':
	    if ((state & (ControlMask | Mod1Mask)) == (ControlMask | Mod1Mask)) {
		scr_change_rendition(1, ~RS_NONE);
		count = 0;
	    }
#ifdef GREEK_ELOT_KBD
	default:
	    if (greek_mode_ON)
		count = greek_xlat(kbuf, count);
#endif
	}
	if (count < 0)
	    count = strlen(kbuf);
    }
    *pcount = count;

    /* if meta is pressed, precede message with ESC */
    /* used to set the 8th bit high, but this confuses things
     * for some non-english character sets */
    if ((MetaHandling == ESCAPE) && meta && (count > 0)) {
	*pcount = count + 1;
	for (c = (kbuf + (count)); c > kbuf; c--) {
	    *c = *(c - 1);
	}
	*kbuf = 27;
	*(kbuf + count + 1) = 0;
    }
    else if ((MetaHandling == BIT) && meta && (count > 0)) {
	for (c = kbuf; c < kbuf + count; c++) {
	    *c = *c | 0x80;
	}
	*(kbuf + count) = 0;
    }
    else
	*(kbuf + count) = 0;
    return (kbuf);
}

/*  Return the next input character after first passing any keyboard input
 *  to the command.  If flags & BUF_ONLY is true then only buffered 
 *  characters are returned and once the buffer is empty the special value 
 *  GCC_NULL is returned.  
 */
static int get_com_char(int flags)
{
    fd_set  in_fdset;
    int     count, retval;
    unsigned char val;
    struct itimerval value;
    static int refreshed = 0;
    int     total;

    /* If there have been a lot of new lines, then update the screen */
    /* What the heck I'll cheat and only refresh less than every page-full.
     * the number of pages between refreshes is refresh_nl_limit, which
     * is incremented here because we must be doing flat-out scrolling. */
    /* refreshing should be correct for small scrolls, because of the 
     * time-out */
    if (refresh_nl_count > refresh_nl_limit * MyWinInfo.cheight) {
	if (refresh_nl_limit < MAX_REFRESH_PERIOD)
	    refresh_nl_limit++;
	refresh_nl_count = 0;
	refreshed = 1;
	refresh();
    }

    /* If we have characters already read in. return one */
    if (com_buf_next < com_buf_top) {
	refreshed = 0;
	return (*com_buf_next++ & mask);
    }
    /* Nothing to read, either return now or continue and wait */
    if (flags & BUF_ONLY)
	return (GCC_NULL);
    for (;;) {
	/* process any X events that are pending */
	while (XPending(display)) {
	    refreshed = 0;
	    get_X_event();
	}

	/* Nothing to do! */
	FD_ZERO(&in_fdset);
	FD_SET(comm_fd, &in_fdset);
	FD_SET(x_fd, &in_fdset);
	value.it_value.tv_usec = 1000;
	value.it_value.tv_sec = 0;
	if (!refreshed)
	    retval = select(fd_width, &in_fdset, NULL, NULL, &value.it_value);
	else
	    retval = select(fd_width, &in_fdset, NULL, NULL, NULL);

	/* See if we can read from the application */
	if (FD_ISSET(comm_fd, &in_fdset)) {
	    count = 1;
	    com_buf_next = com_buf;
	    com_buf_top = com_buf;
	    total = COM_BUF_SIZE;
	    while ((count > 0) && (total > COM_BUF_SIZE / 2)) {
		count = read(comm_fd, com_buf_top, total);
		if (count > 0) {
		    com_buf_top += count;
		    total -= count;
		}
	    }
	    if (com_buf_top > com_buf_next) {
		val = *com_buf_next++;
		refreshed = 0;
		return (val & mask);
	    }
	}
	/* If the select statement timed out, we better update the screen */
	if (retval == 0) {
	    refresh_nl_count = 0;
	    refresh_nl_limit = 1;
	    if (!refreshed)
		refresh();
	    refreshed = 1;
	    sbar_show(MyWinInfo.cheight + MyWinInfo.sline_top - 1, MyWinInfo.offset,
		      MyWinInfo.offset + MyWinInfo.cheight - 1);
	    XFlush(display);
	}
    }
}

/****************************************************************************
 * Receive and process an X event
 **************************************************************************/

int     no_xcin = 0;

void    get_X_event(void)
{
    XEvent  event;
    unsigned char *s;
    int     count;
    Window  root, child;
    int     root_x, root_y, x, y, tl, lk;
    unsigned int mods;
    XEvent  dummy;
    u_char  chstr[256];
    int     k_status;

#ifdef MULTIPLE_CLICKS
#define DEFAULT_MULTICLICK_TIME 500
    static Time button_press_time;
    static int clicks = 0;

#endif

    XNextEvent(display, &event);
    busy_refresh = 1;
    switch (event.type) {
    case KeyPress:
	s = lookup_key(&event, &count);
	if (no_xcin)
	    send_string(s, count);
	else {
	    k_status = send_key(display, main_win, &(event.xkey), chstr);
	    if (!k_status)
		send_string(s, count);
	    else if (chstr[0]) {
		send_string(chstr, strlen(chstr));
	    }
	}
	busy_refresh = 0;
	return;
    case ClientMessage:
	if (event.xclient.format == 32 && event.xclient.data.l[0] == wm_del_win)
	    clean_exit(0);
	busy_refresh = 0;
	return;
    case MappingNotify:
	XRefreshKeyboardMapping(&event.xmapping);
	busy_refresh = 0;
	return;
    case GraphicsExpose:
    case Expose:
	if (!size_set) {
	    /*  Force a window resize if an exposure event
	     *  arrives before the first resize event.
	     */
	    resize_window(0, 0);
	    size_set = 1;
	}
	if (event.xany.window == vt_win) {
	    scr_refresh(event.xexpose.x, event.xexpose.y,
			event.xexpose.width, event.xexpose.height);
	    busy_refresh = 0;
	    return;
	}
	else {
	    while (XCheckTypedWindowEvent(display, event.xany.window, Expose,
					  &dummy));
	    while (XCheckTypedWindowEvent(display, event.xany.window,
					  GraphicsExpose, &dummy));

	    if (event.xany.window == sbar.sb_win) {
		sbar_show(-1, -1, -1);
		busy_refresh = 0;
		return;
	    }
	    else if (event.xany.window == sbar.sb_up_win) {
		sbar_up_reset();
		busy_refresh = 0;
		return;
	    }
	    else if (event.xany.window == sbar.sb_down_win) {
		sbar_down_reset();
		busy_refresh = 0;
		return;
	    }
	    else {
		Gr_expose(event.xany.window);
	    }
	}
	busy_refresh = 0;
	return;
    case VisibilityNotify:
	/* Here's my conclusiion:
	 * If the window is completely onobscured, use bitblt's
	 * to scroll. Even then, they're only used when doing partial
	 * screen scrolling. When partially obscured, we have to fill
	 * in the GraphicsExpose parts, which means that after each refresh,
	 * we need to wait for the graphics expose or Noexpose events, 
	 * which ought to make things real slow! */
	if (event.xvisibility.state == VisibilityUnobscured) {
	    scr_refresh(0, 0, MyWinInfo.cwidth, MyWinInfo.cheight);
	    refresh_type = FAST;
	}
	else if (event.xvisibility.state == VisibilityPartiallyObscured) {
	    refresh_type = SLOW;
	    scr_refresh(0, 0, MyWinInfo.cwidth, MyWinInfo.cheight);
	}
	else {
	    refresh_type = DONT_BOTHER;
	}
	break;

    case FocusIn:
	scr_focus(1);
#if	1
	send_FocusIn(display, main_win);
#endif
	busy_refresh = 0;
	return;
    case FocusOut:
	scr_focus(0);
	send_FocusOut(display, main_win);
	busy_refresh = 0;
	return;
    case ConfigureNotify:
	resize_window(0, 0);
	size_set = 1;
	busy_refresh = 0;
	return;
    case SelectionClear:
	scr_delete_selection();
	busy_refresh = 0;
	return;
    case SelectionNotify:
	scr_paste_primary(event.xselection.requestor,
			  event.xselection.property, True);
	busy_refresh = 0;
	return;
    case SelectionRequest:
	scr_send_selection((XSelectionRequestEvent *) & event);
	busy_refresh = 0;
	return;
    case ButtonPress:
	if (event.xany.window == vt_win) {
	    if (event.xbutton.subwindow != None) {
		Gr_ReportButtonPress(event.xbutton.x, event.xbutton.y);
	    }
	    else {
		switch (event.xbutton.button) {
		case Button1:
		    scr_start_selection(event.xbutton.x, event.xbutton.y);
#ifdef MULTIPLE_CLICKS
		    if (event.xbutton.time - button_press_time < DEFAULT_MULTICLICK_TIME)
			clicks++;
		    else
			clicks = 1;
		    button_press_time = event.xbutton.time;
		    multiple_click_event(clicks, event.xbutton.x, event.xbutton.y);
#endif
		    busy_refresh = 0;
		    return;
		case Button3:
		    scr_extend_selection(event.xbutton.x, event.xbutton.y);
		default:
		    busy_refresh = 0;
		    return;
		}
	    }
	}
	if (event.xany.window == sbar.sb_win) {
	    if (!fat_sbar)
		scr_move_to(event.xbutton.y);
	    else {
		switch (event.xbutton.button) {
		case Button1:
		    tl = MyWinInfo.cheight + MyWinInfo.sline_top;
		    lk = event.xbutton.y * (MyWinInfo.cheight) / (sbar.height - 1);
		    event.xbutton.y = (tl - MyWinInfo.offset - MyWinInfo.cheight + lk) * (sbar.height - 1) / tl;
		    scr_move_to(event.xbutton.y);
		    break;
		case Button2:
		    scr_move_to(event.xbutton.y);
		    break;
		case Button3:
		    tl = MyWinInfo.cheight + MyWinInfo.sline_top;
		    lk = event.xbutton.y * (MyWinInfo.cheight) / (sbar.height - 1);
		    event.xbutton.y = (tl - MyWinInfo.offset - MyWinInfo.cheight - lk) * (sbar.height - 1) / tl;
		    scr_move_to(event.xbutton.y);
		    break;
		default:
		    break;
		}
	    }
	    busy_refresh = 0;
	    return;
	}
	if (event.xany.window == sbar.sb_up_win) {
	    scr_move_up_down(UP);
	    busy_refresh = 0;
	    return;
	}
	if (event.xany.window == sbar.sb_down_win) {
	    scr_move_up_down(DOWN);
	    busy_refresh = 0;
	    return;
	}
	busy_refresh = 0;
	return;
    case ButtonRelease:
	if (event.xany.window == vt_win) {
	    if (event.xbutton.subwindow != None) {
		Gr_ReportButtonRelease(event.xbutton.x, event.xbutton.y);
	    }
	    else {
		switch (event.xbutton.button) {
		case Button1:
		case Button3:
		    scr_make_selection(event.xbutton.time);
		    busy_refresh = 0;
		    return;
		case Button2:
		    scr_request_selection(event.xbutton.time, event.xbutton.x,
					  event.xbutton.y);
		    busy_refresh = 0;
		    return;
		}
	    }
	}
	busy_refresh = 0;
	return;
    case MotionNotify:
	if ((event.xany.window == sbar.sb_win)
	    && ((event.xbutton.state & Button1Mask) ||
		(event.xbutton.state & Button2Mask) ||
		(event.xbutton.state & Button3Mask))) {
	    while (XCheckTypedWindowEvent(display, sbar.sb_win,
					  MotionNotify, &event));
	    XQueryPointer(display, sbar.sb_win, &root, &child,
			  &root_x, &root_y, &x, &y, &mods);
	    if ((!fat_sbar) || (event.xbutton.state & Button2Mask)) {
		scr_move_to(y);
		refresh();
		refresh_nl_limit = 0;
		refresh_nl_count = 0;
		sbar_show(MyWinInfo.cheight + MyWinInfo.sline_top - 1,
			  MyWinInfo.offset,
			  MyWinInfo.offset + MyWinInfo.cheight - 1);
	    }
	    busy_refresh = 0;
	    return;
	}

	if (event.xany.window == vt_win &&
#ifdef MULTIPLE_CLICKS
	    clicks == 1 &&
#endif
	    ((event.xbutton.state == Button1Mask)
	     || (event.xbutton.state == Button3Mask))) {
	    while (XCheckTypedWindowEvent(display, vt_win, MotionNotify, &event));
	    XQueryPointer(display, vt_win, &root, &child,
			  &root_x, &root_y, &x, &y, &mods);
	    if (event.xmotion.time - button_press_time > 200)
		scr_extend_selection(x, y);
	    busy_refresh = 0;
	    return;
	}
	busy_refresh = 0;
	return;
    default:
	busy_refresh = 0;
	return;
    }
}




/*  Send count characters directly to the command.
 */
void    send_string(unsigned char *buf, int count)
{
    int     i, write_bytes, wrote;

    if (count <= 0)
	return;

    i = 0;
    write_bytes = count;

    while (i < count) {
	wrote = write(comm_fd, buf, write_bytes);

	if (wrote > 0) {
	    i += wrote;
	    write_bytes -= wrote;
	    buf += wrote;
	}
    }
}




/*  Send printf formatted output to the command.  Only used for small ammounts
 *  of data.
 */
void    cprintf(unsigned char *fmt,...)
{
    va_list args;
    static unsigned char buf[1024];

    va_start(args, fmt);


    vsprintf(buf, fmt, args);
    va_end(args);
    send_string(buf, strlen(buf));
}

/***************************************************************************
 * Read a text string from the input buffer 
 **************************************************************************/
void    read_string()
{
    unsigned char *ptr, c;
    int     nl_count = 0;

    /* point to the start of the string */
    com_buf_next--;
    ptr = com_buf_next;

    while (com_buf_next < com_buf_top) {
	c = *com_buf_next;
	if (c >= ' ' || c == '\n' || c == '\r' || c == '\t') {
	    com_buf_next++;
	    if (c == '\n')
		nl_count++;

	    /* should have MyWinInfo.cheight instead of 4 */
	    /*      if(nl_count > 4) */
	    if (refresh_nl_count + nl_count > refresh_nl_limit * MyWinInfo.cheight) {
		refresh_nl_count += nl_count;
		scr_add_lines(ptr, nl_count, com_buf_next - ptr);
		return;
	    }
	}
	else {
	    refresh_nl_count += nl_count;
	    scr_add_lines(ptr, nl_count, com_buf_next - ptr);
	    return;
	}
    }
    refresh_nl_count += nl_count;
    scr_add_lines(ptr, nl_count, com_buf_next - ptr);
    return;
}


/***************************************************************************
 * Read and process output from the application
 ***************************************************************************/
void    get_token()
{
    unsigned char c;

    while (1) {
	/* Wait for something to do! */
	while ((c = get_com_char(0)) == GCC_NULL);
	/* Got a character. Figure out what to do with it. */
	if (c >= ' ' || c == '\n' || c == '\r' || c == '\t')
	    read_string();
	else {
	    switch (c) {
	    case 0:		/* NUL does nothing */
		break;
	    case EOF:
		clean_exit(0);
		break;
	    case 5:
		cprintf("\033[?1;2c");	/* I'm a VT100 w/ advanced video options */
		break;
	    case 0xb:
		scr_index(1);
		break;
	    case 0xc:
		scr_index(1);
		break;
	    case ESC:
		process_escape_sequence();
		break;
	    case '\b':
		scr_backspace();
		break;
	    case '\007':	/* bell */
#ifdef MAPALERT
		if (map_alert)
		    XMapWindow(display, main_win);
#endif
		XBell(display, 0);
		break;
	    case '\016':
		scr_choose_charset(1);
		break;
	    case '\017':
		scr_choose_charset(0);
		break;
	    }
	}
    }
}


#ifdef PRINT_PIPE
void    process_print_pipe()
{
    extern char *print_pipe;
    int     c = 0;
    char    escape_seq[4] = "\033[4i";
    char    rev_escape_seq[4] = "i4[\033";
    int     index = 0;
    FILE   *pipe_file;

    pipe_file = popen(print_pipe, "w");
    if (pipe_file == NULL) {
	fprintf(stderr, "rxvt: can't open printer pipe!\n");
	return;
    }

    while (1) {
	c = get_com_char(0);

	if (c == escape_seq[index])
	    index++;
	else if (index)
	    for ( /*no init'ln */ ; index > 0; index--)
		fputc(rev_escape_seq[index - 1], pipe_file);

	if (index == 4) {
	    fflush(pipe_file);
	    pclose(pipe_file);
	    return;
	}

	if (index == 0)
	    fputc(c, pipe_file);
    }
}

#endif

void    process_escape_sequence()
{
    int     c, a;

    c = get_com_char(0);
    switch (c) {
    case '[':
	process_csi_sequence();
	break;
    case ']':
	process_xterm_sequence();
	break;
    case '(':
	scr_set_charset(0, get_com_char(0));
	break;
    case ')':
	scr_set_charset(1, get_com_char(0));
	break;
    case '7':
	scr_save_cursor();
	break;
    case '8':
	scr_restore_cursor();
	break;
    case '=':
	app_kp_keys = 1;
	break;
    case '>':
	app_kp_keys = 0;
	break;
    case 'D':
	scr_index(1);
	break;
    case 'E':
	scr_add_lines("\n\r", 1, 2);
	break;
    case 'M':
	scr_index(-1);
	break;
    case 'Z':
	cprintf("\033[?1;2c");	/* I am a VT102 */
	break;
    case 'c':			/* restore power-on values */
	scr_power_on();
	break;
    case '#':			/* set character size */
	a = get_com_char(0);
	if (a == '8')
	    scr_E();
	break;
    case 'H':			/* horizontal tab set */
	scr_set_tab(1);
	break;
    case 'G':
	process_robs_sequence();
    default:
	return;
    }
}


void    process_csi_sequence()
{
    int     c, n, i, nargs, private = 0;
    int     arg[ARGS_MAX];
    extern char *display_name;

    arg[0] = 0;
    arg[1] = 0;

    c = get_com_char(0);
    if (c >= '<' && c <= '?') {
	private = c;
	c = get_com_char(0);
    }

    /*  read any numerical arguments
     */
    i = 0;
    do {
	n = 0;
	while (c >= '0' && c <= '9') {
	    n = n * 10 + c - '0';
	    c = get_com_char(0);
	}
	if (i < ARGS_MAX)
	    arg[i++] = n;
	if (c == ESC) {
	    process_escape_sequence();
	    return;
	}
	if (c == '\b')
	    scr_backspace();
	else if (c < ' ') {
	    scr_add_lines((unsigned char *) &c, 0, 1);
	    return;
	}
	if (c < '@')
	    c = get_com_char(0);
    } while (c < '@' && c >= ' ');
    if (c == ESC) {
	process_escape_sequence();
	return;
    }
    if (c < ' ')
	return;
    nargs = i;

    switch (c) {
#ifdef PRINT_PIPE
    case 'i':			/* start print_pipe */
	if (arg[0] == 5)
	    process_print_pipe();
	if ((nargs == 0) || (arg[0] == 0))
	    process_print_screen(0);
	break;
#endif
    case 'A':			/* cursor up */
	scr_move(0, ((arg[0] == 0) ? -1 : -arg[0]), ROW_RELATIVE | COL_RELATIVE);
	break;
    case 'B':			/* cursor down */
	scr_move(0, ((arg[0] == 0) ? 1 : arg[0]), ROW_RELATIVE | COL_RELATIVE);
	break;
    case 'C':			/* cursor forward */
	scr_move(((arg[0] == 0) ? 1 : arg[0]), 0, ROW_RELATIVE | COL_RELATIVE);
	break;
    case 'D':			/* cursor back */
	scr_move(((arg[0] == 0) ? -1 : -arg[0]), 0, ROW_RELATIVE | COL_RELATIVE);
	break;
    case 'f':
    case 'H':			/* position cursor */
	if (nargs == 0)
	    scr_move(0, 0, 0);
	else if (nargs == 1)
	    scr_move(0, ((arg[0] == 0) ? 0 : (arg[0] - 1)), 0);
	else
	    scr_move(arg[1] - 1, arg[0] - 1, 0);
	break;
    case 'J':
	scr_erase_screen(arg[0]);
	break;
    case 'K':
	scr_erase_line(arg[0]);
	break;
    case 'L':
	scr_insert_delete_lines((arg[0] == 0) ? 1 : arg[0], INSERT);
	break;
    case 'M':
	scr_insert_delete_lines((arg[0] == 0) ? 1 : arg[0], DELETE);
	break;
    case 'P':
	scr_insert_delete_characters((arg[0] == 0) ? 1 : arg[0], DELETE);
	break;
    case '@':
	scr_insert_delete_characters((arg[0] == 0) ? 1 : arg[0], INSERT);
	break;
    case 'c':
	cprintf("\033[?1;2c");	/* I am a VT102 */
	break;
    case 'h':
    case 'l':
	process_terminal_mode(c, private, nargs, arg);
	break;
    case 'm':
	process_sgr_mode(c, private, nargs, arg);
	break;
    case 'n':			/* request for information */
	switch (arg[0]) {
	case 6:
	    scr_report_position();
	    break;
	case 7:
	    cprintf(display_name);
	    break;
	}
	break;
    case 'r':			/* set top and bottom margins */
	/* what's this about? something to do with vi on ESIX systems */
	if (private == '?')
	    break;
	if (nargs < 2 || arg[0] >= arg[1])
	    scr_set_margins(0, 10000);
	else
	    scr_set_margins(arg[0] - 1, arg[1] - 1);
	break;
    case 'g':			/* tab clear */
	if ((nargs == 1) && (arg[0] == 3))
	    /* clear all tabs stops */
	    scr_set_tab(-1);
	else if ((nargs == 0) || ((nargs == 1) && (arg[0] == 0)))
	    scr_set_tab(0);
	break;
    }
}



void    process_xterm_sequence()
{
    int     c, n, i, arg;
    unsigned char string[STRING_MAX];

    c = get_com_char(0);
    n = 0;
    while (c >= '0' && c <= '9') {
	n = n * 10 + c - '0';
	c = get_com_char(0);
    }
    arg = n;

    c = get_com_char(0);
    i = 0;
    while (c != 7 && i < STRING_MAX) {
	if (c >= ' ')
	    string[i++] = c;
	c = get_com_char(0);
    }
    string[i] = 0;
    switch (arg) {
    case 0:
	change_window_name(window_name = string);
	change_icon_name(icon_name = string);
	break;
    case 1:
	change_icon_name(icon_name = string);
	break;
    case 2:
	change_window_name(window_name = string);
	break;

    }
}


void    process_terminal_mode(int c, int private, int nargs, int *arg)
{
    int     mode;
    int     w = 1, h = 1;
    extern XSizeHints sizehints;

    mode = (c == 'h') ? HIGH : LOW;
    if (private == '?') {
	switch (arg[0]) {
	case 1:
	    app_cur_keys = (mode == HIGH);
	    break;
	case 3:		/* 132 columns mode is N/A */
	    if (mode == HIGH)
		w = 132 * MyWinInfo.fwidth + sizehints.base_width;
	    else
		w = 80 * MyWinInfo.fwidth + sizehints.base_width;
	    h = MyWinInfo.cheight * MyWinInfo.fheight + sizehints.base_height;
	    sizehints.width = w;
	    sizehints.height = h;
	    sizehints.flags = PMinSize | PResizeInc | PBaseSize | PWinGravity;
	    XSetWMNormalHints(display, main_win, &sizehints);
	    XResizeWindow(display, main_win, w, h);
	    XClearWindow(display, vt_win);
	    XSync(display, 0);
	    resize_window(w, h);
	    break;
	case 4:		/* Smooth scrolling is N/A */
	    /* Could switch to single line scroll after */
	    /* receiving this, but why bother? */
	    break;
	case 5:
	    /* reverse video */
	    scr_rev_vid(mode);
	case 6:
	    scr_set_decom(mode);
	    break;
	case 7:
	    scr_set_wrap(mode);
	    break;
	case 8:		/* auto repeat is N/A */
	    break;
	case 47:		/* switch to main screen */
	    scr_change_screen(mode);
	    break;
	}
    }
    else if (private == 0) {
	if (arg[0] == 4)
	    scr_set_insert(mode);
    }
}


void    process_sgr_mode(int c, int private, int nargs, int *arg)
{
    int     i;

    if (nargs == 0)
	scr_change_rendition(1, ~RS_NONE);
    else {
	for (i = 0; i < nargs; i++) {
	    switch (arg[i]) {
	    case 0:
		scr_change_rendition(1, ~RS_NONE);
		break;
	    case 1:
		scr_change_rendition(0, RS_BOLD);
		break;
	    case 4:
		scr_change_rendition(0, RS_ULINE);
		break;
	    case 5:
		scr_change_rendition(0, RS_BLINK);
		break;
	    case 7:
		scr_change_rendition(0, RS_RVID);
		break;
	    case 22:
		scr_change_rendition(1, RS_BOLD);
		break;
	    case 24:
		scr_change_rendition(1, RS_ULINE);
		break;
	    case 25:
		scr_change_rendition(1, RS_BLINK);
		break;
	    case 27:
		scr_change_rendition(1, RS_RVID);
		break;
	    case 30:
	    case 31:
	    case 32:
	    case 33:
	    case 34:
	    case 35:
	    case 36:
	    case 37:
		scr_fore_color(arg[i]);
		break;
	    case 40:
	    case 41:
	    case 42:
	    case 43:
	    case 44:
	    case 45:
	    case 46:
	    case 47:
		scr_back_color(arg[i]);
		break;
	    }
	}
    }
}


void    process_robs_sequence(void)
{
    unsigned char c, c1;
    int     done, i, nargs, negative;
    long    args[1000];
    unsigned char text[1000];

    nargs = 0;
    done = 0;
    args[nargs] = 0;
    negative = 0;
    c1 = get_com_char(0);
    while ((!done) && (nargs < 999)) {
	while (isdigit(c = get_com_char(0)) || (c == '-')) {
	    if (c == '-')
		negative = 1 - negative;
	    args[nargs] = args[nargs] * 10 + (c - '0');
	}
	if (negative)
	    args[nargs] = -args[nargs];
	if (c != ';')
	    done = 1;
	nargs++;
	args[nargs] = 0;
    }
    i = 0;
    if ((c1 == 'T') && (nargs >= 5)) {
	while ((i < args[nargs - 1]) && (i < 999))
	    text[i++] = get_com_char(0);
    }
    text[i] = 0;
    Gr_do_graphics(c1, nargs, (int *)args, text);

}



#ifdef NO_SETSID
setsid()
{
    if (setpgrp(0, 0) < 0)
	return (-1);
    return (0);
}
#endif
