/*-
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Christos Zoulas of Cornell University.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#if !defined(lint) && !defined(SCCSID)
static char sccsid[] = "@(#)tty.c	8.1 (Berkeley) 6/4/93";
#endif /* not lint && not SCCSID */

/*
 * tty.c: tty interface stuff
 */
#include "sys.h"
#include "tty.h"
#include "el.h"

typedef struct ttymodes_t {
    char *m_name;
    int   m_value;
    int   m_type;
} ttymodes_t;

typedef struct ttymap_t {
    int nch, och;		 /* Internal and termio rep of chars */
    el_action_t bind[3]; 	/* emacs, vi, and vi-cmd */
} ttymap_t;


private ttyperm_t ttyperm = {
    {
	{ "iflag:", ICRNL, (INLCR|IGNCR) },
	{ "oflag:", (OPOST|ONLCR), ONLRET },
	{ "cflag:", 0, 0 },
	{ "lflag:", (ISIG|ICANON|ECHO|ECHOE|ECHOCTL|IEXTEN),
		    (NOFLSH|ECHONL|EXTPROC|FLUSHO) },
	{ "chars:", 	0, 0 },
    },
    {
	{ "iflag:", (INLCR|ICRNL), IGNCR },
	{ "oflag:", (OPOST|ONLCR), ONLRET },
	{ "cflag:", 0, 0 },
	{ "lflag:", ISIG,
		    (NOFLSH|ICANON|ECHO|ECHOK|ECHONL|EXTPROC|IEXTEN|FLUSHO) },
	{ "chars:", (C_SH(C_MIN)|C_SH(C_TIME)|C_SH(C_SWTCH)|C_SH(C_DSWTCH)|
		     C_SH(C_SUSP)|C_SH(C_DSUSP)|C_SH(C_EOL)|C_SH(C_DISCARD)|
		     C_SH(C_PGOFF)|C_SH(C_PAGE)|C_SH(C_STATUS)), 0 }
    },
    {
	{ "iflag:", 0, IXON | IXOFF },
	{ "oflag:", 0, 0 },
	{ "cflag:", 0, 0 },
	{ "lflag:", 0, ISIG | IEXTEN },
	{ "chars:", 0, 0 },
    }
};

private ttychar_t ttychar = {
    {
	CINTR,		 CQUIT, 	 CERASE, 	   CKILL,
	CEOF, 		 CEOL, 		 CEOL2, 	   CSWTCH,
	CDSWTCH,	 CERASE2,	 CSTART, 	   CSTOP,
	CWERASE, 	 CSUSP, 	 CDSUSP, 	   CREPRINT,
	CDISCARD, 	 CLNEXT,	 CSTATUS,	   CPAGE,
	CPGOFF,		 CKILL2, 	 CBRK, 		   CMIN,
	CTIME
    },
    {
	CINTR, 		 CQUIT, 	  CERASE, 	   CKILL,
	_POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE,
	_POSIX_VDISABLE, CERASE2,	  CSTART, 	   CSTOP,
	_POSIX_VDISABLE, CSUSP,           _POSIX_VDISABLE, _POSIX_VDISABLE,
	CDISCARD, 	 _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE,
	_POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, 1,
	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
    }
};

private ttymap_t tty_map[] = {
#ifdef VERASE
	{ C_ERASE,   VERASE,
	    { ED_DELETE_PREV_CHAR, VI_DELETE_PREV_CHAR, ED_PREV_CHAR } },
#endif /* VERASE */
#ifdef VERASE2
	{ C_ERASE2,  VERASE2,
	    { ED_DELETE_PREV_CHAR, VI_DELETE_PREV_CHAR, ED_PREV_CHAR } },
#endif /* VERASE2 */
#ifdef VKILL
    	{ C_KILL,    VKILL,
	    { EM_KILL_LINE, VI_KILL_LINE_PREV, ED_UNASSIGNED } },
#endif /* VKILL */
#ifdef VKILL2
    	{ C_KILL2,   VKILL2,
	    { EM_KILL_LINE, VI_KILL_LINE_PREV, ED_UNASSIGNED } },
#endif /* VKILL2 */
#ifdef VEOF
    	{ C_EOF,     VEOF,
	    { EM_DELETE_OR_LIST, VI_LIST_OR_EOF, ED_UNASSIGNED } },
#endif /* VEOF */
#ifdef VWERASE
    	{ C_WERASE,  VWERASE,
	    { ED_DELETE_PREV_WORD, ED_DELETE_PREV_WORD, ED_PREV_WORD } },
#endif /* VWERASE */
#ifdef VREPRINT
   	{ C_REPRINT, VREPRINT,
	    { ED_REDISPLAY, ED_INSERT, ED_REDISPLAY } },
#endif /* VREPRINT */
#ifdef VLNEXT
    	{ C_LNEXT,   VLNEXT,
	    { ED_QUOTED_INSERT, ED_QUOTED_INSERT, ED_UNASSIGNED } },
#endif /* VLNEXT */
	{ -1,	     -1,
	    { ED_UNASSIGNED, ED_UNASSIGNED, ED_UNASSIGNED } }
    };

private ttymodes_t ttymodes[] = {
# ifdef	IGNBRK
    { "ignbrk",	IGNBRK,	M_INP },
# endif /* IGNBRK */
# ifdef	BRKINT
    { "brkint",	BRKINT,	M_INP },
# endif /* BRKINT */
# ifdef	IGNPAR
    { "ignpar",	IGNPAR,	M_INP },
# endif /* IGNPAR */
# ifdef	PARMRK
    { "parmrk",	PARMRK,	M_INP },
# endif /* PARMRK */
# ifdef	INPCK
    { "inpck",	INPCK,	M_INP },
# endif /* INPCK */
# ifdef	ISTRIP
    { "istrip",	ISTRIP,	M_INP },
# endif /* ISTRIP */
# ifdef	INLCR
    { "inlcr",	INLCR,	M_INP },
# endif /* INLCR */
# ifdef	IGNCR
    { "igncr",	IGNCR,	M_INP },
# endif /* IGNCR */
# ifdef	ICRNL
    { "icrnl",	ICRNL,	M_INP },
# endif /* ICRNL */
# ifdef	IUCLC
    { "iuclc",	IUCLC,	M_INP },
# endif /* IUCLC */
# ifdef	IXON
    { "ixon",	IXON,	M_INP },
# endif /* IXON */
# ifdef	IXANY
    { "ixany",	IXANY,	M_INP },
# endif /* IXANY */
# ifdef	IXOFF
    { "ixoff",	IXOFF,	M_INP },
# endif /* IXOFF */
# ifdef  IMAXBEL
    { "imaxbel",IMAXBEL,M_INP },
# endif /* IMAXBEL */

# ifdef	OPOST
    { "opost",	OPOST,	M_OUT },
# endif /* OPOST */
# ifdef	OLCUC
    { "olcuc",	OLCUC,	M_OUT },
# endif /* OLCUC */
# ifdef	ONLCR
    { "onlcr",	ONLCR,	M_OUT },
# endif /* ONLCR */
# ifdef	OCRNL
    { "ocrnl",	OCRNL,	M_OUT },
# endif /* OCRNL */
# ifdef	ONOCR
    { "onocr",	ONOCR,	M_OUT },
# endif /* ONOCR */
# ifdef ONOEOT
    { "onoeot",	ONOEOT,	M_OUT },
# endif /* ONOEOT */
# ifdef	ONLRET
    { "onlret",	ONLRET,	M_OUT },
# endif /* ONLRET */
# ifdef	OFILL
    { "ofill",	OFILL,	M_OUT },
# endif /* OFILL */
# ifdef	OFDEL
    { "ofdel",	OFDEL,	M_OUT },
# endif /* OFDEL */
# ifdef	NLDLY
    { "nldly",	NLDLY,	M_OUT },
# endif /* NLDLY */
# ifdef	CRDLY
    { "crdly",	CRDLY,	M_OUT },
# endif /* CRDLY */
# ifdef	TABDLY
    { "tabdly",	TABDLY,	M_OUT },
# endif /* TABDLY */
# ifdef	XTABS
    { "xtabs",	XTABS,	M_OUT },
# endif /* XTABS */
# ifdef	BSDLY
    { "bsdly",	BSDLY,	M_OUT },
# endif /* BSDLY */
# ifdef	VTDLY
    { "vtdly",	VTDLY,	M_OUT },
# endif /* VTDLY */
# ifdef	FFDLY
    { "ffdly",	FFDLY,	M_OUT },
# endif /* FFDLY */
# ifdef	PAGEOUT
    { "pageout",PAGEOUT,M_OUT },
# endif /* PAGEOUT */
# ifdef	WRAP
    { "wrap",	WRAP,	M_OUT },
# endif /* WRAP */

# ifdef	CIGNORE
    { "cignore",CIGNORE,M_CTL },
# endif /* CBAUD */
# ifdef	CBAUD
    { "cbaud",	CBAUD,	M_CTL },
# endif /* CBAUD */
# ifdef	CSTOPB
    { "cstopb",	CSTOPB,	M_CTL },
# endif /* CSTOPB */
# ifdef	CREAD
    { "cread",	CREAD,	M_CTL },
# endif /* CREAD */
# ifdef	PARENB
    { "parenb",	PARENB,	M_CTL },
# endif /* PARENB */
# ifdef	PARODD
    { "parodd",	PARODD,	M_CTL },
# endif /* PARODD */
# ifdef	HUPCL
    { "hupcl",	HUPCL,	M_CTL },
# endif /* HUPCL */
# ifdef	CLOCAL
    { "clocal",	CLOCAL,	M_CTL },
# endif /* CLOCAL */
# ifdef	LOBLK
    { "loblk",	LOBLK,	M_CTL },
# endif /* LOBLK */
# ifdef	CIBAUD
    { "cibaud",	CIBAUD,	M_CTL },
# endif /* CIBAUD */
# ifdef CRTSCTS
#  ifdef CCTS_OFLOW
    { "ccts_oflow",CCTS_OFLOW,M_CTL },
#  else
    { "crtscts",CRTSCTS,M_CTL },
#  endif /* CCTS_OFLOW */
# endif /* CRTSCTS */
# ifdef CRTS_IFLOW
    { "crts_iflow",CRTS_IFLOW,M_CTL },
# endif /* CRTS_IFLOW */
# ifdef MDMBUF
    { "mdmbuf",	MDMBUF,	M_CTL },
# endif /* MDMBUF */
# ifdef RCV1EN
    { "rcv1en",	RCV1EN,	M_CTL },
# endif /* RCV1EN */
# ifdef XMT1EN
    { "xmt1en",	XMT1EN,	M_CTL },
# endif /* XMT1EN */

# ifdef	ISIG
    { "isig",	ISIG,	M_LIN },
# endif /* ISIG */
# ifdef	ICANON
    { "icanon",	ICANON,	M_LIN },
# endif /* ICANON */
# ifdef	XCASE
    { "xcase",	XCASE,	M_LIN },
# endif /* XCASE */
# ifdef	ECHO
    { "echo",	ECHO,	M_LIN },
# endif /* ECHO */
# ifdef	ECHOE
    { "echoe",	ECHOE,	M_LIN },
# endif /* ECHOE */
# ifdef	ECHOK
    { "echok",	ECHOK,	M_LIN },
# endif /* ECHOK */
# ifdef	ECHONL
    { "echonl",	ECHONL,	M_LIN },
# endif /* ECHONL */
# ifdef	NOFLSH
    { "noflsh",	NOFLSH,	M_LIN },
# endif /* NOFLSH */
# ifdef	TOSTOP
    { "tostop",	TOSTOP,	M_LIN },
# endif /* TOSTOP */
# ifdef	ECHOCTL
    { "echoctl",ECHOCTL,M_LIN },
# endif /* ECHOCTL */
# ifdef	ECHOPRT
    { "echoprt",ECHOPRT,M_LIN },
# endif /* ECHOPRT */
# ifdef	ECHOKE
    { "echoke",	ECHOKE,	M_LIN },
# endif /* ECHOKE */
# ifdef	DEFECHO
    { "defecho",DEFECHO,M_LIN },
# endif /* DEFECHO */
# ifdef	FLUSHO
    { "flusho",	FLUSHO,	M_LIN },
# endif /* FLUSHO */
# ifdef	PENDIN
    { "pendin",	PENDIN,	M_LIN },
# endif /* PENDIN */
# ifdef	IEXTEN
    { "iexten",	IEXTEN,	M_LIN },
# endif /* IEXTEN */
# ifdef	NOKERNINFO
    { "nokerninfo",NOKERNINFO,M_LIN },
# endif /* NOKERNINFO */
# ifdef	ALTWERASE
    { "altwerase",ALTWERASE,M_LIN },
# endif /* ALTWERASE */
# ifdef	EXTPROC
    { "extproc",EXTPROC, M_LIN },
# endif /* EXTPROC */

# if defined(VINTR)
    { "intr",		C_SH(C_INTR), 	M_CHAR },
# endif /* VINTR */
# if defined(VQUIT)
    { "quit",		C_SH(C_QUIT), 	M_CHAR },
# endif /* VQUIT */
# if defined(VERASE)
    { "erase",		C_SH(C_ERASE), 	M_CHAR },
# endif /* VERASE */
# if defined(VKILL)
    { "kill",		C_SH(C_KILL), 	M_CHAR },
# endif /* VKILL */
# if defined(VEOF)
    { "eof",		C_SH(C_EOF), 	M_CHAR },
# endif /* VEOF */
# if defined(VEOL)
    { "eol",		C_SH(C_EOL), 	M_CHAR },
# endif /* VEOL */
# if defined(VEOL2)
    { "eol2",		C_SH(C_EOL2), 	M_CHAR },
# endif  /* VEOL2 */
# if defined(VSWTCH)
    { "swtch",		C_SH(C_SWTCH), 	M_CHAR },
# endif /* VSWTCH */
# if defined(VDSWTCH)
    { "dswtch",		C_SH(C_DSWTCH),	M_CHAR },
# endif /* VDSWTCH */
# if defined(VERASE2)
    { "erase2",		C_SH(C_ERASE2),	M_CHAR },
# endif /* VERASE2 */
# if defined(VSTART)
    { "start",		C_SH(C_START), 	M_CHAR },
# endif /* VSTART */
# if defined(VSTOP)
    { "stop",		C_SH(C_STOP), 	M_CHAR },
# endif /* VSTOP */
# if defined(VWERASE)
    { "werase",		C_SH(C_WERASE),	M_CHAR },
# endif /* VWERASE */
# if defined(VSUSP)
    { "susp",		C_SH(C_SUSP), 	M_CHAR },
# endif /* VSUSP */
# if defined(VDSUSP)
    { "dsusp",		C_SH(C_DSUSP), 	M_CHAR },
# endif /* VDSUSP */
# if defined(VREPRINT)
    { "reprint",	C_SH(C_REPRINT),M_CHAR },
# endif /* VREPRINT */
# if defined(VDISCARD)
    { "discard",	C_SH(C_DISCARD),M_CHAR },
# endif /* VDISCARD */
# if defined(VLNEXT)
    { "lnext",		C_SH(C_LNEXT), 	M_CHAR },
# endif /* VLNEXT */
# if defined(VSTATUS)
    { "status",		C_SH(C_STATUS),	M_CHAR },
# endif /* VSTATUS */
# if defined(VPAGE)
    { "page",		C_SH(C_PAGE), 	M_CHAR },
# endif /* VPAGE */
# if defined(VPGOFF)
    { "pgoff",		C_SH(C_PGOFF), 	M_CHAR },
# endif /* VPGOFF */
# if defined(VKILL2)
    { "kill2",		C_SH(C_KILL2), 	M_CHAR },
# endif /* VKILL2 */
# if defined(VBRK)
    { "brk",		C_SH(C_BRK), 	M_CHAR },
# endif /* VBRK */
# if defined(VMIN)
    { "min",		C_SH(C_MIN), 	M_CHAR },
# endif /* VMIN */
# if defined(VTIME)
    { "time",		C_SH(C_TIME), 	M_CHAR },
# endif /* VTIME */
    { NULL, 0, -1 },
};



#define tty_getty(el, td) tcgetattr((el)->el_infd, (td))
#define tty_setty(el, td) tcsetattr((el)->el_infd, TCSADRAIN, (td))

#define tty__gettabs(td)     ((((td)->c_oflag & TAB3) == TAB3) ? 0 : 1)
#define tty__geteightbit(td) (((td)->c_cflag & CSIZE) == CS8)
#define tty__cooked_mode(td) ((td)->c_lflag & ICANON)

private void    tty__getchar	__P((struct termios *, unsigned char *));
private void    tty__setchar	__P((struct termios *, unsigned char *));
private speed_t tty__getspeed	__P((struct termios *));
private int     tty_setup	__P((EditLine *));

#define t_qu t_ts


/* tty_setup():
 *	Get the tty parameters and initialize the editing state
 */
private int
tty_setup(el)
    EditLine *el;
{
    int rst = 1;
    if (tty_getty(el, &el->el_tty.t_ed) == -1) {
#ifdef DEBUG_TTY
	(void) fprintf(el->el_errfile,
		       "tty_setup: tty_getty: %s\n", strerror(errno));
#endif /* DEBUG_TTY */
	return(-1);
    }
    el->el_tty.t_ts    = el->el_tty.t_ex = el->el_tty.t_ed;

    el->el_tty.t_speed = tty__getspeed(&el->el_tty.t_ex);
    el->el_tty.t_tabs  = tty__gettabs(&el->el_tty.t_ex);
    el->el_tty.t_eight = tty__geteightbit(&el->el_tty.t_ex);

    el->el_tty.t_ex.c_iflag &= ~el->el_tty.t_t[EX_IO][M_INP].t_clrmask;
    el->el_tty.t_ex.c_iflag |=  el->el_tty.t_t[EX_IO][M_INP].t_setmask;

    el->el_tty.t_ex.c_oflag &= ~el->el_tty.t_t[EX_IO][M_OUT].t_clrmask;
    el->el_tty.t_ex.c_oflag |=  el->el_tty.t_t[EX_IO][M_OUT].t_setmask;

    el->el_tty.t_ex.c_cflag &= ~el->el_tty.t_t[EX_IO][M_CTL].t_clrmask;
    el->el_tty.t_ex.c_cflag |=  el->el_tty.t_t[EX_IO][M_CTL].t_setmask;

    el->el_tty.t_ex.c_lflag &= ~el->el_tty.t_t[EX_IO][M_LIN].t_clrmask;
    el->el_tty.t_ex.c_lflag |=  el->el_tty.t_t[EX_IO][M_LIN].t_setmask;

    /*
     * Reset the tty chars to reasonable defaults
     * If they are disabled, then enable them.
     */
    if (rst) {
        if (tty__cooked_mode(&el->el_tty.t_ts)) {
            tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]);
            /*
             * Don't affect CMIN and CTIME for the editor mode
             */
            for (rst = 0; rst < C_NCC - 2; rst++)
                if (el->el_tty.t_c[TS_IO][rst] != el->el_tty.t_vdisable &&
                    el->el_tty.t_c[ED_IO][rst] != el->el_tty.t_vdisable)
                    el->el_tty.t_c[ED_IO][rst]  = el->el_tty.t_c[TS_IO][rst];
            for (rst = 0; rst < C_NCC; rst++)
                if (el->el_tty.t_c[TS_IO][rst] != el->el_tty.t_vdisable &&
                    el->el_tty.t_c[EX_IO][rst] != el->el_tty.t_vdisable)
                    el->el_tty.t_c[EX_IO][rst]  = el->el_tty.t_c[TS_IO][rst];
        }
        tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]);
        if (tty_setty(el, &el->el_tty.t_ex) == -1) {
#ifdef DEBUG_TTY
            (void) fprintf(el->el_errfile, "tty_setup: tty_setty: %s\n",
			   strerror(errno));
#endif /* DEBUG_TTY */
            return(-1);
        }
    }
    else
        tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]);

    el->el_tty.t_ed.c_iflag &= ~el->el_tty.t_t[ED_IO][M_INP].t_clrmask;
    el->el_tty.t_ed.c_iflag |=  el->el_tty.t_t[ED_IO][M_INP].t_setmask;

    el->el_tty.t_ed.c_oflag &= ~el->el_tty.t_t[ED_IO][M_OUT].t_clrmask;
    el->el_tty.t_ed.c_oflag |=  el->el_tty.t_t[ED_IO][M_OUT].t_setmask;

    el->el_tty.t_ed.c_cflag &= ~el->el_tty.t_t[ED_IO][M_CTL].t_clrmask;
    el->el_tty.t_ed.c_cflag |=  el->el_tty.t_t[ED_IO][M_CTL].t_setmask;

    el->el_tty.t_ed.c_lflag &= ~el->el_tty.t_t[ED_IO][M_LIN].t_clrmask;
    el->el_tty.t_ed.c_lflag |=  el->el_tty.t_t[ED_IO][M_LIN].t_setmask;

    tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]);
    return 0;
}

protected int
tty_init(el)
    EditLine *el;
{
    el->el_tty.t_mode     = EX_IO;
    el->el_tty.t_vdisable = _POSIX_VDISABLE;
    (void) memcpy(el->el_tty.t_t, ttyperm, sizeof(ttyperm_t));
    (void) memcpy(el->el_tty.t_c, ttychar, sizeof(ttychar_t));
    return tty_setup(el);
} /* end tty_init */


/* tty_end():
 *	Restore the tty to its original settings
 */
protected void
/*ARGSUSED*/
tty_end(el)
    EditLine *el;
{
    /* XXX: Maybe reset to an initial state? */
}


/* tty__getspeed():
 *	Get the tty speed
 */
private speed_t
tty__getspeed(td)
    struct termios *td;
{
    speed_t spd;

    if ((spd = cfgetispeed(td)) == 0)
	spd = cfgetospeed(td);
    return spd;
} /* end tty__getspeed */


/* tty__getchar():
 *	Get the tty characters
 */
private void
tty__getchar(td, s)
    struct termios *td;
    unsigned char *s;
{
# ifdef VINTR
    s[C_INTR]	= td->c_cc[VINTR];
# endif /* VINTR */
# ifdef VQUIT
    s[C_QUIT]	= td->c_cc[VQUIT];
# endif /* VQUIT */
# ifdef VERASE
    s[C_ERASE]	= td->c_cc[VERASE];
# endif /* VERASE */
# ifdef VKILL
    s[C_KILL]	= td->c_cc[VKILL];
# endif /* VKILL */
# ifdef VEOF
    s[C_EOF]	= td->c_cc[VEOF];
# endif /* VEOF */
# ifdef VEOL
    s[C_EOL]	= td->c_cc[VEOL];
# endif /* VEOL */
# ifdef VEOL2
    s[C_EOL2]	= td->c_cc[VEOL2];
# endif  /* VEOL2 */
# ifdef VSWTCH
    s[C_SWTCH]	= td->c_cc[VSWTCH];
# endif /* VSWTCH */
# ifdef VDSWTCH
    s[C_DSWTCH]	= td->c_cc[VDSWTCH];
# endif /* VDSWTCH */
# ifdef VERASE2
    s[C_ERASE2]	= td->c_cc[VERASE2];
# endif /* VERASE2 */
# ifdef VSTART
    s[C_START]	= td->c_cc[VSTART];
# endif /* VSTART */
# ifdef VSTOP
    s[C_STOP]	= td->c_cc[VSTOP];
# endif /* VSTOP */
# ifdef VWERASE
    s[C_WERASE]	= td->c_cc[VWERASE];
# endif /* VWERASE */
# ifdef VSUSP
    s[C_SUSP]	= td->c_cc[VSUSP];
# endif /* VSUSP */
# ifdef VDSUSP
    s[C_DSUSP]	= td->c_cc[VDSUSP];
# endif /* VDSUSP */
# ifdef VREPRINT
    s[C_REPRINT]= td->c_cc[VREPRINT];
# endif /* VREPRINT */
# ifdef VDISCARD
    s[C_DISCARD]= td->c_cc[VDISCARD];
# endif /* VDISCARD */
# ifdef VLNEXT
    s[C_LNEXT]	= td->c_cc[VLNEXT];
# endif /* VLNEXT */
# ifdef VSTATUS
    s[C_STATUS]	= td->c_cc[VSTATUS];
# endif /* VSTATUS */
# ifdef VPAGE
    s[C_PAGE]	= td->c_cc[VPAGE];
# endif /* VPAGE */
# ifdef VPGOFF
    s[C_PGOFF]	= td->c_cc[VPGOFF];
# endif /* VPGOFF */
# ifdef VKILL2
    s[C_KILL2]	= td->c_cc[VKILL2];
# endif /* KILL2 */
# ifdef VMIN
    s[C_MIN]	= td->c_cc[VMIN];
# endif /* VMIN */
# ifdef VTIME
    s[C_TIME]	= td->c_cc[VTIME];
# endif /* VTIME */
} /* tty__getchar */


/* tty__setchar():
 *	Set the tty characters
 */
private void
tty__setchar(td, s)
    struct termios *td;
    unsigned char *s;
{
# ifdef VINTR
    td->c_cc[VINTR]	= s[C_INTR];
# endif /* VINTR */
# ifdef VQUIT
    td->c_cc[VQUIT]	= s[C_QUIT];
# endif /* VQUIT */
# ifdef VERASE
    td->c_cc[VERASE]	= s[C_ERASE];
# endif /* VERASE */
# ifdef VKILL
    td->c_cc[VKILL]	= s[C_KILL];
# endif /* VKILL */
# ifdef VEOF
    td->c_cc[VEOF]	= s[C_EOF];
# endif /* VEOF */
# ifdef VEOL
    td->c_cc[VEOL]	= s[C_EOL];
# endif /* VEOL */
# ifdef VEOL2
    td->c_cc[VEOL2]	= s[C_EOL2];
# endif  /* VEOL2 */
# ifdef VSWTCH
    td->c_cc[VSWTCH]	= s[C_SWTCH];
# endif /* VSWTCH */
# ifdef VDSWTCH
    td->c_cc[VDSWTCH]	= s[C_DSWTCH];
# endif /* VDSWTCH */
# ifdef VERASE2
    td->c_cc[VERASE2]	= s[C_ERASE2];
# endif /* VERASE2 */
# ifdef VSTART
    td->c_cc[VSTART]	= s[C_START];
# endif /* VSTART */
# ifdef VSTOP
    td->c_cc[VSTOP]	= s[C_STOP];
# endif /* VSTOP */
# ifdef VWERASE
    td->c_cc[VWERASE]	= s[C_WERASE];
# endif /* VWERASE */
# ifdef VSUSP
    td->c_cc[VSUSP]	= s[C_SUSP];
# endif /* VSUSP */
# ifdef VDSUSP
    td->c_cc[VDSUSP]	= s[C_DSUSP];
# endif /* VDSUSP */
# ifdef VREPRINT
    td->c_cc[VREPRINT]	= s[C_REPRINT];
# endif /* VREPRINT */
# ifdef VDISCARD
    td->c_cc[VDISCARD]	= s[C_DISCARD];
# endif /* VDISCARD */
# ifdef VLNEXT
    td->c_cc[VLNEXT]	= s[C_LNEXT];
# endif /* VLNEXT */
# ifdef VSTATUS
    td->c_cc[VSTATUS]	= s[C_STATUS];
# endif /* VSTATUS */
# ifdef VPAGE
    td->c_cc[VPAGE]	= s[C_PAGE];
# endif /* VPAGE */
# ifdef VPGOFF
    td->c_cc[VPGOFF]	= s[C_PGOFF];
# endif /* VPGOFF */
# ifdef VKILL2
    td->c_cc[VKILL2]	= s[C_KILL2];
# endif /* VKILL2 */
# ifdef VMIN
    td->c_cc[VMIN]	= s[C_MIN];
# endif /* VMIN */
# ifdef VTIME
    td->c_cc[VTIME]	= s[C_TIME];
# endif /* VTIME */
} /* tty__setchar */


/* tty_bind_char():
 *	Rebind the editline functions
 */
protected void
tty_bind_char(el, force)
    EditLine *el;
    int force;
{
    unsigned char *t_n = el->el_tty.t_c[ED_IO];
    unsigned char *t_o = el->el_tty.t_ed.c_cc;
    char new[2], old[2];
    ttymap_t *tp;
    el_action_t  *dmap, *dalt, *map, *alt;
    new[1] = old[1] = '\0';


    map = el->el_map.key;
    alt = el->el_map.alt;
    if (el->el_map.type == MAP_VI) {
	dmap = el->el_map.vii;
	dalt = el->el_map.vic;
    }
    else {
	dmap = el->el_map.emacs;
	dalt = NULL;
    }

    for (tp = tty_map; tp->nch != -1; tp++) {
	new[0] = t_n[tp->nch];
	old[0] = t_o[tp->och];
	if (new[0] == old[0] && !force)
	    continue;
	/* Put the old default binding back, and set the new binding */
	key_clear(el, map, old);
	map[old[0]] = dmap[old[0]];
	key_clear(el, map, new);
	/* MAP_VI == 1, MAP_EMACS == 0... */
	map[new[0]] = tp->bind[el->el_map.type];
	if (dalt) {
	    key_clear(el, alt, old);
	    alt[old[0]] = dalt[old[0]];
	    key_clear(el, alt, new);
	    alt[new[0]] = tp->bind[el->el_map.type+1];
	}
    }
}

/* tty_rawmode():
 * 	Set terminal into 1 character at a time mode.
 */
protected int
tty_rawmode(el)
    EditLine *el;
{
    if (el->el_tty.t_mode == ED_IO)
	return (0);

    if (tty_getty(el, &el->el_tty.t_ts) == -1) {
#ifdef DEBUG_TTY
	(void) fprintf(el->el_errfile, "tty_rawmode: tty_getty: %s\n", strerror(errno));
#endif /* DEBUG_TTY */
	return(-1);
    }

    /*
     * We always keep up with the eight bit setting and the speed of the
     * tty. But only we only believe changes that are made to cooked mode!
     */
    el->el_tty.t_eight = tty__geteightbit(&el->el_tty.t_ts);
    el->el_tty.t_speed = tty__getspeed(&el->el_tty.t_ts);

    if (tty__getspeed(&el->el_tty.t_ex) != el->el_tty.t_speed ||
	tty__getspeed(&el->el_tty.t_ed) != el->el_tty.t_speed) {
	(void) cfsetispeed(&el->el_tty.t_ex, el->el_tty.t_speed);
	(void) cfsetospeed(&el->el_tty.t_ex, el->el_tty.t_speed);
	(void) cfsetispeed(&el->el_tty.t_ed, el->el_tty.t_speed);
	(void) cfsetospeed(&el->el_tty.t_ed, el->el_tty.t_speed);
    }

    if (tty__cooked_mode(&el->el_tty.t_ts)) {
	if (el->el_tty.t_ts.c_cflag != el->el_tty.t_ex.c_cflag) {
	    el->el_tty.t_ex.c_cflag  = el->el_tty.t_ts.c_cflag;
	    el->el_tty.t_ex.c_cflag &= ~el->el_tty.t_t[EX_IO][M_CTL].t_clrmask;
	    el->el_tty.t_ex.c_cflag |=  el->el_tty.t_t[EX_IO][M_CTL].t_setmask;

	    el->el_tty.t_ed.c_cflag  = el->el_tty.t_ts.c_cflag;
	    el->el_tty.t_ed.c_cflag &= ~el->el_tty.t_t[ED_IO][M_CTL].t_clrmask;
	    el->el_tty.t_ed.c_cflag |=  el->el_tty.t_t[ED_IO][M_CTL].t_setmask;
	}

	if ((el->el_tty.t_ts.c_lflag != el->el_tty.t_ex.c_lflag) &&
	    (el->el_tty.t_ts.c_lflag != el->el_tty.t_ed.c_lflag)) {
	    el->el_tty.t_ex.c_lflag = el->el_tty.t_ts.c_lflag;
	    el->el_tty.t_ex.c_lflag &= ~el->el_tty.t_t[EX_IO][M_LIN].t_clrmask;
	    el->el_tty.t_ex.c_lflag |=  el->el_tty.t_t[EX_IO][M_LIN].t_setmask;

	    el->el_tty.t_ed.c_lflag = el->el_tty.t_ts.c_lflag;
	    el->el_tty.t_ed.c_lflag &= ~el->el_tty.t_t[ED_IO][M_LIN].t_clrmask;
	    el->el_tty.t_ed.c_lflag |=  el->el_tty.t_t[ED_IO][M_LIN].t_setmask;
	}

	if ((el->el_tty.t_ts.c_iflag != el->el_tty.t_ex.c_iflag) &&
	    (el->el_tty.t_ts.c_iflag != el->el_tty.t_ed.c_iflag)) {
	    el->el_tty.t_ex.c_iflag = el->el_tty.t_ts.c_iflag;
	    el->el_tty.t_ex.c_iflag &= ~el->el_tty.t_t[EX_IO][M_INP].t_clrmask;
	    el->el_tty.t_ex.c_iflag |=  el->el_tty.t_t[EX_IO][M_INP].t_setmask;

	    el->el_tty.t_ed.c_iflag = el->el_tty.t_ts.c_iflag;
	    el->el_tty.t_ed.c_iflag &= ~el->el_tty.t_t[ED_IO][M_INP].t_clrmask;
	    el->el_tty.t_ed.c_iflag |=  el->el_tty.t_t[ED_IO][M_INP].t_setmask;
	}

	if ((el->el_tty.t_ts.c_oflag != el->el_tty.t_ex.c_oflag) &&
	    (el->el_tty.t_ts.c_oflag != el->el_tty.t_ed.c_oflag)) {
	    el->el_tty.t_ex.c_oflag = el->el_tty.t_ts.c_oflag;
	    el->el_tty.t_ex.c_oflag &= ~el->el_tty.t_t[EX_IO][M_OUT].t_clrmask;
	    el->el_tty.t_ex.c_oflag |=  el->el_tty.t_t[EX_IO][M_OUT].t_setmask;

	    el->el_tty.t_ed.c_oflag = el->el_tty.t_ts.c_oflag;
	    el->el_tty.t_ed.c_oflag &= ~el->el_tty.t_t[ED_IO][M_OUT].t_clrmask;
	    el->el_tty.t_ed.c_oflag |=  el->el_tty.t_t[ED_IO][M_OUT].t_setmask;
	}

	if (tty__gettabs(&el->el_tty.t_ex) == 0)
	    el->el_tty.t_tabs = 0;
	else
	    el->el_tty.t_tabs = EL_CAN_TAB ? 1 : 0;

	{
	    int i;

	    tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]);
	    /*
	     * Check if the user made any changes.
	     * If he did, then propagate the changes to the
	     * edit and execute data structures.
	     */
	    for (i = 0; i < C_NCC; i++)
		if (el->el_tty.t_c[TS_IO][i] != el->el_tty.t_c[EX_IO][i])
		    break;

	    if (i != C_NCC) {
		/*
		 * Propagate changes only to the unprotected chars
		 * that have been modified just now.
		 */
		for (i = 0; i < C_NCC; i++) {
		    if (!((el->el_tty.t_t[ED_IO][M_CHAR].t_setmask & C_SH(i)))
		      && (el->el_tty.t_c[TS_IO][i] != el->el_tty.t_c[EX_IO][i]))
			el->el_tty.t_c[ED_IO][i] = el->el_tty.t_c[TS_IO][i];
		    if (el->el_tty.t_t[ED_IO][M_CHAR].t_clrmask & C_SH(i))
			el->el_tty.t_c[ED_IO][i] = el->el_tty.t_vdisable;
		}
		tty_bind_char(el, 0);
		tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]);

		for (i = 0; i < C_NCC; i++) {
		    if (!((el->el_tty.t_t[EX_IO][M_CHAR].t_setmask & C_SH(i)))
		      && (el->el_tty.t_c[TS_IO][i] != el->el_tty.t_c[EX_IO][i]))
			el->el_tty.t_c[EX_IO][i] = el->el_tty.t_c[TS_IO][i];
		    if (el->el_tty.t_t[EX_IO][M_CHAR].t_clrmask & C_SH(i))
			el->el_tty.t_c[EX_IO][i] = el->el_tty.t_vdisable;
		}
		tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]);
	    }

	}
    }

    if (tty_setty(el, &el->el_tty.t_ed) == -1) {
#ifdef DEBUG_TTY
	(void) fprintf(el->el_errfile, "tty_rawmode: tty_setty: %s\n",
		       strerror(errno));
#endif /* DEBUG_TTY */
	return -1;
    }
    el->el_tty.t_mode = ED_IO;
    return (0);
} /* end tty_rawmode */


/* tty_cookedmode():
 *	Set the tty back to normal mode
 */
protected int
tty_cookedmode(el)
    EditLine *el;
{				/* set tty in normal setup */
    if (el->el_tty.t_mode == EX_IO)
	return (0);

    if (tty_setty(el, &el->el_tty.t_ex) == -1) {
#ifdef DEBUG_TTY
	(void) fprintf(el->el_errfile, "tty_cookedmode: tty_setty: %s\n",
		       strerror(errno));
#endif /* DEBUG_TTY */
	return -1;
    }
    el->el_tty.t_mode = EX_IO;
    return (0);
} /* end tty_cookedmode */


/* tty_quotemode():
 *	Turn on quote mode
 */
protected int
tty_quotemode(el)
    EditLine *el;
{
    if (el->el_tty.t_mode == QU_IO)
	return 0;

    el->el_tty.t_qu = el->el_tty.t_ed;

    el->el_tty.t_qu.c_iflag &= ~el->el_tty.t_t[QU_IO][M_INP].t_clrmask;
    el->el_tty.t_qu.c_iflag |=  el->el_tty.t_t[QU_IO][M_INP].t_setmask;

    el->el_tty.t_qu.c_oflag &= ~el->el_tty.t_t[QU_IO][M_OUT].t_clrmask;
    el->el_tty.t_qu.c_oflag |=  el->el_tty.t_t[QU_IO][M_OUT].t_setmask;

    el->el_tty.t_qu.c_cflag &= ~el->el_tty.t_t[QU_IO][M_CTL].t_clrmask;
    el->el_tty.t_qu.c_cflag |=  el->el_tty.t_t[QU_IO][M_CTL].t_setmask;

    el->el_tty.t_qu.c_lflag &= ~el->el_tty.t_t[QU_IO][M_LIN].t_clrmask;
    el->el_tty.t_qu.c_lflag |=  el->el_tty.t_t[QU_IO][M_LIN].t_setmask;

    if (tty_setty(el, &el->el_tty.t_qu) == -1) {
#ifdef DEBUG_TTY
	(void) fprintf(el->el_errfile, "QuoteModeOn: tty_setty: %s\n",
		       strerror(errno));
#endif /* DEBUG_TTY */
	return -1;
    }
    el->el_tty.t_mode = QU_IO;
    return 0;
} /* end tty_quotemode */


/* tty_noquotemode():
 *	Turn off quote mode
 */
protected int
tty_noquotemode(el)
    EditLine *el;
{
    if (el->el_tty.t_mode != QU_IO)
	return 0;
    if (tty_setty(el, &el->el_tty.t_ed) == -1) {
#ifdef DEBUG_TTY
	(void) fprintf(el->el_errfile, "QuoteModeOff: tty_setty: %s\n",
		       strerror(errno));
#endif /* DEBUG_TTY */
	return -1;
    }
    el->el_tty.t_mode = ED_IO;
    return 0;
}

/* tty_stty():
 *	Stty builtin
 */
protected int
/*ARGSUSED*/
tty_stty(el, argc, argv)
    EditLine *el;
    int argc;
    char **argv;
{
    ttymodes_t *m;
    char x, *d;
    int aflag = 0;
    char *s;
    char *name;
    int z = EX_IO;

    if (argv == NULL)
	return -1;
    name = *argv++;

    while (argv && *argv && argv[0][0] == '-' && argv[0][2] == '\0')
	switch (argv[0][1]) {
	case 'a':
	    aflag++;
	    argv++;
	    break;
	case 'd':
	    argv++;
	    z = ED_IO;
	    break;
	case 'x':
	    argv++;
	    z = EX_IO;
	    break;
	case 'q':
	    argv++;
	    z = QU_IO;
	    break;
	default:
	    (void) fprintf(el->el_errfile, "%s: Unknown switch `%c'.\n",
			   name, argv[0][1]);
	    return -1;
	}

    if (!argv || !*argv) {
	int i = -1;
	int len = 0, st = 0, cu;
	for (m = ttymodes; m->m_name; m++) {
	    if (m->m_type != i) {
		(void) fprintf(el->el_outfile, "%s%s", i != -1 ? "\n" : "",
			el->el_tty.t_t[z][m->m_type].t_name);
		i = m->m_type;
		st = len = strlen(el->el_tty.t_t[z][m->m_type].t_name);
	    }

	    x = (el->el_tty.t_t[z][i].t_setmask & m->m_value) ? '+' : '\0';
	    x = (el->el_tty.t_t[z][i].t_clrmask & m->m_value) ? '-' : x;

	    if (x != '\0' || aflag) {

		cu = strlen(m->m_name) + (x != '\0') + 1;

		if (len + cu >= el->el_term.t_size.h) {
		    (void) fprintf(el->el_outfile, "\n%*s", st, "");
		    len = st + cu;
		}
		else
		    len += cu;

		if (x != '\0')
		    (void) fprintf(el->el_outfile, "%c%s ", x, m->m_name);
		else
		    (void) fprintf(el->el_outfile, "%s ", m->m_name);
	    }
	}
	(void) fprintf(el->el_outfile, "\n");
	return 0;
    }

    while (argv && (s = *argv++)) {
	switch (*s) {
	case '+':
	case '-':
	    x = *s++;
	    break;
	default:
	    x = '\0';
	    break;
	}
	d = s;
	for (m = ttymodes; m->m_name; m++)
	    if (strcmp(m->m_name, d) == 0)
		break;

	if (!m->m_name)  {
	    (void) fprintf(el->el_errfile, "%s: Invalid argument `%s'.\n",
			   name, d);
	    return -1;
	}

	switch (x) {
	case '+':
	    el->el_tty.t_t[z][m->m_type].t_setmask |= m->m_value;
	    el->el_tty.t_t[z][m->m_type].t_clrmask &= ~m->m_value;
	    break;
	case '-':
	    el->el_tty.t_t[z][m->m_type].t_setmask &= ~m->m_value;
	    el->el_tty.t_t[z][m->m_type].t_clrmask |= m->m_value;
	    break;
	default:
	    el->el_tty.t_t[z][m->m_type].t_setmask &= ~m->m_value;
	    el->el_tty.t_t[z][m->m_type].t_clrmask &= ~m->m_value;
	    break;
	}
    }
    return 0;
} /* end tty_stty */


#ifdef notyet
/* tty_printchar():
 *	DEbugging routine to print the tty characters
 */
private void
tty_printchar(el, s)
    EditLine *el;
    unsigned char *s;
{
    ttyperm_t *m;
    int i;

    for (i = 0; i < C_NCC; i++) {
	for (m = el->el_tty.t_t; m->m_name; m++)
	    if (m->m_type == M_CHAR && C_SH(i) == m->m_value)
		break;
	if (m->m_name)
	    (void) fprintf(el->el_errfile, "%s ^%c ", m->m_name, s[i] + 'A'-1);
	if (i % 5 == 0)
	    (void) fprintf(el->el_errfile, "\n");
    }
    (void) fprintf(el->el_errfile, "\n");
}
#endif /* notyet */
