/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */

#include "config.h"
#include <sys/ioctl.h>
#include "file.h"
#include "err.h"
#include "tty.h"

#define generic GENERIC *

static int ioc(fd,req,arg) /* non-interruptable ioctl */
int fd;
unsigned long req;
generic arg;
{
 int result;

 do
   result = ioctl(fd,req,(char *) arg);
 while ((result == -1) && (errno == EINTR));
 return result;
}

int tty_getctrl()
{
 int fd;
 int dummy;

#define ISTTY(f) (ioc(f,(unsigned long) TIOCGPGRP,(generic) &dummy) == 0)

 if (ISTTY(3))
   return 3;
 if (((fd = open("/dev/tty",O_RDWR)) != -1) && ISTTY(fd))
   return fd;
 if (((fd = dup(0)) != -1) && ISTTY(fd))
   return fd;
 if (((fd = dup(1)) != -1) && ISTTY(fd))
   return fd;
 return -1;
}

tty_setexcl(fd)
int fd;
{
 return ioc(fd,(unsigned long) TIOCEXCL,(generic) 0);
 /* setting exclusive use is a bit unusual but it works */
 /* opening /dev/tty should still be allowed, though */
}

tty_setpgrp(fd,pgrp)
int fd;
int pgrp;
{
 return ioc(fd,(unsigned long) TIOCSPGRP,(generic) &pgrp);
}

tty_dissoc(fd)
int fd;
{
 return ioc(fd,(unsigned long) TIOCNOTTY,(generic) 0);
}

tty_getmodes(fd,tmo)
int fd;
struct ttymodes *tmo;
{
 if (ioc(fd,(unsigned long) TIOCGETD,(generic) &(tmo->di)) == -1) return -1;
 if (ioc(fd,(unsigned long) TIOCGETP,(generic) &(tmo->sg)) == -1) return -1;
 if (ioc(fd,(unsigned long) TIOCGETC,(generic) &(tmo->tc)) == -1) return -1;
 if (ioc(fd,(unsigned long) TIOCLGET,(generic) &(tmo->lb)) == -1) return -1;
 if (ioc(fd,(unsigned long) TIOCGLTC,(generic) &(tmo->lt)) == -1) return -1;
#ifdef TTY_WINDOWS
 if (ioc(fd,(unsigned long) TIOCGWINSZ,(generic) &(tmo->ws)) == -1) return -1;
#endif
#ifdef TTY_AUXCHARS
 if (ioc(fd,(unsigned long) TIOCGAUXC,(generic) &(tmo->au)) == -1) return -1;
#endif
 return 0;
}

tty_setmodes(fd,tmo)
int fd;
struct ttymodes *tmo;
{
 if (ioc(fd,(unsigned long) TIOCSETD,(generic) &(tmo->di)) == -1) return -1;
 if (ioc(fd,(unsigned long) TIOCSETP,(generic) &(tmo->sg)) == -1) return -1;
 if (ioc(fd,(unsigned long) TIOCSETC,(generic) &(tmo->tc)) == -1) return -1;
 if (ioc(fd,(unsigned long) TIOCLSET,(generic) &(tmo->lb)) == -1) return -1;
 if (ioc(fd,(unsigned long) TIOCSLTC,(generic) &(tmo->lt)) == -1) return -1;
#ifdef TTY_WINDOWS
 if (ioc(fd,(unsigned long) TIOCSWINSZ,(generic) &(tmo->ws)) == -1) return -1;
#endif
#ifdef TTY_AUXCHARS
 if (ioc(fd,(unsigned long) TIOCSAUXC,(generic) &(tmo->au)) == -1) return -1;
#endif
 return 0;
}

tty_modifymodes(fd,tmonew,tmoold)
int fd;
struct ttymodes *tmonew;
struct ttymodes *tmoold;
{
 if (tmonew->di != tmoold->di)
   if (ioc(fd,(unsigned long) TIOCSETD,(generic) &(tmonew->di)) == -1) return -1;
 if ((tmonew->sg.sg_flags != tmoold->sg.sg_flags)
   ||(tmonew->sg.sg_ispeed != tmoold->sg.sg_ispeed)
   ||(tmonew->sg.sg_ospeed != tmoold->sg.sg_ospeed)
   ||(tmonew->sg.sg_erase != tmoold->sg.sg_erase)
   ||(tmonew->sg.sg_kill != tmoold->sg.sg_kill))
   if (ioc(fd,(unsigned long) TIOCSETP,(generic) &(tmonew->sg)) == -1) return -1;
 if ((tmonew->tc.t_intrc != tmoold->tc.t_intrc)
   ||(tmonew->tc.t_quitc != tmoold->tc.t_quitc)
   ||(tmonew->tc.t_startc != tmoold->tc.t_startc)
   ||(tmonew->tc.t_stopc != tmoold->tc.t_stopc)
   ||(tmonew->tc.t_eofc != tmoold->tc.t_eofc)
   ||(tmonew->tc.t_brkc != tmoold->tc.t_brkc))
   if (ioc(fd,(unsigned long) TIOCSETC,(generic) &(tmonew->tc)) == -1) return -1;
 if (tmonew->lb != tmoold->lb)
   if (ioc(fd,(unsigned long) TIOCLSET,(generic) &(tmonew->lb)) == -1) return -1;
 if ((tmonew->lt.t_suspc != tmoold->lt.t_suspc)
   ||(tmonew->lt.t_dsuspc != tmoold->lt.t_dsuspc)
   ||(tmonew->lt.t_rprntc != tmoold->lt.t_rprntc)
   ||(tmonew->lt.t_flushc != tmoold->lt.t_flushc)
   ||(tmonew->lt.t_werasc != tmoold->lt.t_werasc)
   ||(tmonew->lt.t_lnextc != tmoold->lt.t_lnextc))
   if (ioc(fd,(unsigned long) TIOCSLTC,(generic) &(tmonew->lt)) == -1) return -1;
#ifdef TTY_WINDOWS
 if ((tmonew->ws.ws_xpixel != tmoold->ws.ws_xpixel)
   ||(tmonew->ws.ws_ypixel != tmoold->ws.ws_ypixel)
   ||(tmonew->ws.ws_row != tmoold->ws.ws_row)
   ||(tmonew->ws.ws_col != tmoold->ws.ws_col))
   if (ioc(fd,(unsigned long)TIOCSWINSZ,(generic)&(tmonew->ws))==-1) return -1;
#endif
#ifdef TTY_AUXCHARS
 if ((tmonew->au.t_usemap != tmoold->au.t_usemap)
   ||(tmonew->au.t_usest != tmoold->au.t_usest))
   if (ioc(fd,(unsigned long)TIOCSAUXC,(generic)&(tmonew->au))==-1) return -1;
#endif
 return 0;
}

/* XXX: Should use copy() for this. */
void tty_copymodes(tmonew,tmoold)
struct ttymodes *tmonew;
struct ttymodes *tmoold;
{
 tmonew->di = tmoold->di;
 tmonew->sg.sg_ispeed = tmoold->sg.sg_ispeed;
 tmonew->sg.sg_ospeed = tmoold->sg.sg_ospeed;
 tmonew->sg.sg_erase = tmoold->sg.sg_erase;
 tmonew->sg.sg_kill = tmoold->sg.sg_kill;
 tmonew->sg.sg_flags = tmoold->sg.sg_flags;
 tmonew->tc.t_intrc = tmoold->tc.t_intrc;
 tmonew->tc.t_quitc = tmoold->tc.t_quitc;
 tmonew->tc.t_startc = tmoold->tc.t_startc;
 tmonew->tc.t_stopc = tmoold->tc.t_stopc;
 tmonew->tc.t_eofc = tmoold->tc.t_eofc;
 tmonew->tc.t_brkc = tmoold->tc.t_brkc;
 tmonew->lb = tmoold->lb;
 tmonew->lt.t_suspc = tmoold->lt.t_suspc;
 tmonew->lt.t_dsuspc = tmoold->lt.t_dsuspc;
 tmonew->lt.t_rprntc = tmoold->lt.t_rprntc;
 tmonew->lt.t_flushc = tmoold->lt.t_flushc;
 tmonew->lt.t_werasc = tmoold->lt.t_werasc;
 tmonew->lt.t_lnextc = tmoold->lt.t_lnextc;
#ifdef TTY_WINDOWS
 tmonew->ws.ws_xpixel = tmoold->ws.ws_xpixel;
 tmonew->ws.ws_ypixel = tmoold->ws.ws_ypixel;
 tmonew->ws.ws_row = tmoold->ws.ws_row;
 tmonew->ws.ws_col = tmoold->ws.ws_col;
#endif
#ifdef TTY_AUXCHARS
 tmonew->au.t_usest = tmoold->au.t_usest;
 tmonew->au.t_usemap = tmoold->au.t_usemap;
#endif
}

void tty_copywin(tmonew,tmoold)
struct ttymodes *tmonew;
struct ttymodes *tmoold;
{
 ;
#ifdef TTY_WINDOWS
 tmonew->ws.ws_xpixel = tmoold->ws.ws_xpixel;
 tmonew->ws.ws_ypixel = tmoold->ws.ws_ypixel;
 tmonew->ws.ws_row = tmoold->ws.ws_row;
 tmonew->ws.ws_col = tmoold->ws.ws_col;
#endif
}

void tty_charmode(tmo)
struct ttymodes *tmo;
{
 tty_mungemodes(tmo,3,0,2,0,3,0);
}

void tty_mungemodes(tmo,cbreak,new,echo,crmod,raw,crt)
struct ttymodes *tmo;
int cbreak;
int new;
int echo;
int crmod;
int raw;
int crt;
{
 if (new >= 2)
   tmo->di = ((new == 3) ? NTTYDISC : OTTYDISC);
 if (crmod >= 2)
   tmo->sg.sg_flags = (tmo->sg.sg_flags & ~CRMOD) | (CRMOD * (crmod == 3));
 if (echo >= 2)
   tmo->sg.sg_flags = (tmo->sg.sg_flags & ~ECHO) | (ECHO * (echo == 3));
 if (raw >= 2)
   tmo->sg.sg_flags = (tmo->sg.sg_flags & ~RAW) | (RAW * (raw == 3));
 if (cbreak >= 2)
   tmo->sg.sg_flags = (tmo->sg.sg_flags & ~CBREAK) | (CBREAK * (cbreak == 3));
 if (crt >= 2)
   tmo->lb = (tmo->lb & ~(CRTBS | CRTERA | CRTKIL | CTLECH))
                      | ((CRTBS | CRTERA | CRTKIL | CTLECH) * (crt == 3));
}

void tty_initmodes(tmo,cbreak,new,echo,crmod,raw,crt)
struct ttymodes *tmo;
int cbreak;
int new;
int echo;
int crmod;
int raw;
int crt;
{
 /* Here we specify Ye Standard BSD Terminal Settings. */

 tmo->di = ((new % 2) ? NTTYDISC : OTTYDISC);
 tmo->sg.sg_ispeed = EXTB;
 tmo->sg.sg_ospeed = EXTB;
 tmo->sg.sg_erase = 127; /* del */
 tmo->sg.sg_kill = 21; /* ^U */
 tmo->sg.sg_flags = EVENP | ODDP | XTABS
   | (CRMOD * (crmod % 2)) | (ECHO * (echo % 2))
   | (RAW * (raw % 2)) | (CBREAK * (cbreak % 2));
 tmo->tc.t_intrc = 3; /* ^C */
 tmo->tc.t_quitc = 28; /* ^\ */
 tmo->tc.t_startc = 17; /* ^Q */
 tmo->tc.t_stopc = 19; /* ^S */
 tmo->tc.t_eofc = 4; /* ^D */
 tmo->tc.t_brkc = -1; /* undef */
 tmo->lb = ((CRTBS | CRTERA | CRTKIL | CTLECH) * (crt % 2)) | DECCTQ;
 tmo->lt.t_suspc = 26; /* ^Z */
 tmo->lt.t_dsuspc = 25; /* ^Y */
 tmo->lt.t_rprntc = 18; /* ^R */
 tmo->lt.t_flushc = 15; /* ^O */
 tmo->lt.t_werasc = 23; /* ^W */
 tmo->lt.t_lnextc = 22; /* ^V */
#ifdef TTY_WINDOWS
 tmo->ws.ws_xpixel = 0; /* Or read from TERMCAP? Hmmm */
 tmo->ws.ws_ypixel = 0;
 tmo->ws.ws_row = 0;
 tmo->ws.ws_col = 0;
#endif
#ifdef TTY_AUXCHARS
 tmo->au.t_usest = 20; /* ^T */
 tmo->au.t_usemap = UST_LOAD1 | UST_LOAD5 | UST_LOAD15 | UST_RAWCPU
   | UST_UPTIME | UST_PGRP | UST_CHILDS | UST_PCPU | UST_STATE;
#endif
}
