/*	@(#)stty.c	2.1		*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/termio.h>

#include <sys/stermio.h>
#define ASYNC	0
#define SYNC	1
extern char *getenv();

struct
{
	char	*string;
	int	speed;
} speeds[] = {
	"0",	B0,
	"50",	B50,
	"75",	B75,
	"110",	B110,
	"134",	B134,
	"134.5",B134,
	"150",	B150,
	"200",	B200,
	"300",	B300,
	"600",	B600,
	"1200",	B1200,
	"1800",	B1800,
	"2400",	B2400,
	"4800",	B4800,
	"9600",	B9600,
	"exta",	EXTA,
	"extb",	EXTB,
	0,
};
struct mds {
	char	*string;
	int	set;
	int	reset;
};

struct mds cmodes[] = {
	"-parity", CS8, PARENB|CSIZE,
	"-evenp", CS8, PARENB|CSIZE,
	"-oddp", CS8, PARENB|PARODD|CSIZE,
	"parity", PARENB|CS7, PARODD|CSIZE,
	"evenp", PARENB|CS7, PARODD|CSIZE,
	"oddp", PARENB|PARODD|CS7, CSIZE,
	"parenb", PARENB, 0,
	"-parenb", 0, PARENB,
	"parodd", PARODD, 0,
	"-parodd", 0, PARODD,
	"cs8", CS8, CSIZE,
	"cs7", CS7, CSIZE,
	"cs6", CS6, CSIZE,
	"cs5", CS5, CSIZE,
	"cstopb", CSTOPB, 0,
	"-cstopb", 0, CSTOPB,
	"hupcl", HUPCL, 0,
	"hup", HUPCL, 0,
	"-hupcl", 0, HUPCL,
	"-hup", 0, HUPCL,
	"clocal", CLOCAL, 0,
	"-clocal", 0, CLOCAL,
	"cread", CREAD, 0,
	"-cread", 0, CREAD,
	"raw", CS8, (CSIZE|PARENB),
	"-raw", (CS7|PARENB), CSIZE,
	"cooked", (CS7|PARENB), CSIZE,
	"sane", (CS7|PARENB|CREAD), (CSIZE|PARODD|CLOCAL),
	0
};

struct mds imodes[] = {
	"ignbrk", IGNBRK, 0,
	"-ignbrk", 0, IGNBRK,
	"brkint", BRKINT, 0,
	"-brkint", 0, BRKINT,
	"ignpar", IGNPAR, 0,
	"-ignpar", 0, IGNPAR,
	"parmrk", PARMRK, 0,
	"-parmrk", 0, PARMRK,
	"inpck", INPCK, 0,
	"-inpck", 0,INPCK,
	"istrip", ISTRIP, 0,
	"-istrip", 0, ISTRIP,
	"inlcr", INLCR, 0,
	"-inlcr", 0, INLCR,
	"igncr", IGNCR, 0,
	"-igncr", 0, IGNCR,
	"icrnl", ICRNL, 0,
	"-icrnl", 0, ICRNL,
	"-nl", ICRNL, (INLCR|IGNCR),
	"nl", 0, ICRNL,
	"iuclc", IUCLC, 0,
	"-iuclc", 0, IUCLC,
	"lcase", IUCLC, 0,
	"-lcase", 0, IUCLC,
	"LCASE", IUCLC, 0,
	"-LCASE", 0, IUCLC,
	"ixon", IXON, 0,
	"-ixon", 0, IXON,
	"ixany", IXANY, 0,
	"-ixany", 0, IXANY,
	"ixoff", IXOFF, 0,
	"-ixoff", 0, IXOFF,
	"raw", 0, -1,
	"-raw", (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON), 0,
	"cooked", (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON), 0,
	"sane", (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON),
		(IGNBRK|PARMRK|INPCK|INLCR|IGNCR|IUCLC|IXOFF),
	0
};

struct mds lmodes[] = {
	"isig", ISIG, 0,
	"-isig", 0, ISIG,
	"icanon", ICANON, 0,
	"-icanon", 0, ICANON,
	"xcase", XCASE, 0,
	"-xcase", 0, XCASE,
	"lcase", XCASE, 0,
	"-lcase", 0, XCASE,
	"LCASE", XCASE, 0,
	"-LCASE", 0, XCASE,
	"echo", ECHO, 0,
	"-echo", 0, ECHO,
	"echoe", ECHOE, 0,
	"-echoe", 0, ECHOE,
	"echok", ECHOK, 0,
	"-echok", 0, ECHOK,
	"lfkc", ECHOK, 0,
	"-lfkc", 0, ECHOK,
	"echonl", ECHONL, 0,
	"-echonl", 0, ECHONL,
	"noflsh", NOFLSH, 0,
	"-noflsh", 0, NOFLSH,
	"raw", 0, (ISIG|ICANON|XCASE),
	"-raw", (ISIG|ICANON), 0,
	"cooked", (ISIG|ICANON), 0,
	"sane", (ISIG|ICANON|ECHO|ECHOK),
		(XCASE|ECHOE|ECHONL|NOFLSH|STFLUSH|STWRAP|STAPPL),
	"stflush", STFLUSH, 0,
	"-stflush", 0, STFLUSH,
	"stwrap", STWRAP, 0,
	"-stwrap", 0, STWRAP,
	"stappl", STAPPL, 0,
	"-stappl", 0, STAPPL,
	0,
};

struct mds omodes[] = {
	"opost", OPOST, 0,
	"-opost", 0, OPOST,
	"olcuc", OLCUC, 0,
	"-olcuc", 0, OLCUC,
	"lcase", OLCUC, 0,
	"-lcase", 0, OLCUC,
	"LCASE", OLCUC, 0,
	"-LCASE", 0, OLCUC,
	"onlcr", ONLCR, 0,
	"-onlcr", 0, ONLCR,
	"-nl", ONLCR, (OCRNL|ONLRET),
	"nl", 0, ONLCR,
	"ocrnl", OCRNL, 0,
	"-ocrnl",0, OCRNL,
	"onocr", ONOCR, 0,
	"-onocr", 0, ONOCR,
	"onlret", ONLRET, 0,
	"-onlret", 0, ONLRET,
	"fill", OFILL, OFDEL,
	"-fill", 0, OFILL|OFDEL,
	"nul-fill", OFILL, OFDEL,
	"del-fill", OFILL|OFDEL, 0,
	"ofill", OFILL, 0,
	"-ofill", 0, OFILL,
	"ofdel", OFDEL, 0,
	"-ofdel", 0, OFDEL,
	"cr0", CR0, CRDLY,
	"cr1", CR1, CRDLY,
	"cr2", CR2, CRDLY,
	"cr3", CR3, CRDLY,
	"tab0", TAB0, TABDLY,
	"tabs", TAB0, TABDLY,
	"tab1", TAB1, TABDLY,
	"tab2", TAB2, TABDLY,
	"tab3", TAB3, TABDLY,
	"-tabs", TAB3, TABDLY,
	"nl0", NL0, NLDLY,
	"nl1", NL1, NLDLY,
	"ff0", FF0, FFDLY,
	"ff1", FF1, FFDLY,
	"vt0", VT0, VTDLY,
	"vt1", VT1, VTDLY,
	"bs0", BS0, BSDLY,
	"bs1", BS1, BSDLY,
	"raw", 0, OPOST,
	"-raw", OPOST, 0,
	"cooked", OPOST, 0,
	"tty33", CR1, (CRDLY|TABDLY|NLDLY|FFDLY|VTDLY|BSDLY),
	"tn300", CR1, (CRDLY|TABDLY|NLDLY|FFDLY|VTDLY|BSDLY),
	"ti700", CR2, (CRDLY|TABDLY|NLDLY|FFDLY|VTDLY|BSDLY),
	"vt05", NL1, (CRDLY|TABDLY|NLDLY|FFDLY|VTDLY|BSDLY),
	"tek", FF1, (CRDLY|TABDLY|NLDLY|FFDLY|VTDLY|BSDLY),
	"tty37", (FF1|VT1|CR2|TAB1|NL1), (NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY),
	"sane", (OPOST|ONLCR), (OLCUC|OCRNL|ONOCR|ONLRET|OFILL|OFDEL|
			NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY),
	0,
};

char	*arg;
int	match;
char	*STTY="stty: ";
char	*USAGE="usage: stty [-ag] [modes]\n";
int	pitt = 0;
struct termio cb;
struct stio stio;
int term;

main(argc, argv)
char	*argv[];
{
	register i;
	char *ptr;

	if(ioctl(0, STGET, &stio) == -1) {
		term = ASYNC;
		if(ioctl(0, TCGETA, &cb) == -1) {
			perror(STTY);
			exit(2);
		}
	}
	else {
		term = SYNC;
		cb.c_cc[7] = stio.tab;
		cb.c_lflag = stio.lmode;
		cb.c_oflag = stio.omode;
		cb.c_iflag = stio.imode;
	}

	if (argc == 1) {
		prmodes();
		exit(0);
	}
	if ((argc == 2) && (argv[1][0] == '-') && (argv[1][2] == '\0'))
	switch(argv[1][1]) {
		case 'a':
			pramodes();
			exit(0);
		case 'g':
			prencode();
			exit(0);
		default:
			fprintf(stderr, "%s", USAGE);
			exit(2);
	}
	while(--argc > 0) {

		arg = *++argv;
		match = 0;
		if (term == ASYNC) {
			if (eq("erase") && --argc)
				cb.c_cc[VERASE] = gct(*++argv);
			else if (eq("intr") && --argc)
				cb.c_cc[VINTR] = gct(*++argv);
			else if (eq("quit") && --argc)
				cb.c_cc[VQUIT] = gct(*++argv);
			else if (eq("eof") && --argc)
				cb.c_cc[VEOF] = gct(*++argv);
			else if (eq("min") && --argc)
				cb.c_cc[VMIN] = gct(*++argv);
			else if (eq("eol") && --argc)
				cb.c_cc[VEOL] = gct(*++argv);
			else if (eq("time") && --argc)
				cb.c_cc[VTIME] = gct(*++argv);
			else if (eq("kill") && --argc)
				cb.c_cc[VKILL] = gct(*++argv);
			else if (eq("ek")) {
				cb.c_cc[VERASE] = CERASE;
				cb.c_cc[VKILL] = CKILL;
			}
			else if (eq("line") && --argc)
				cb.c_line = atoi(*++argv);
			else if (eq("raw")) {
				cb.c_cc[VMIN] = 1;
				cb.c_cc[VTIME] = 1;
			}
			else if (eq("-raw") | eq("cooked")) {
				cb.c_cc[VEOF] = CEOF;
				cb.c_cc[VEOL] = CNUL;
			}
			else if(eq("sane")) {
				cb.c_cc[VERASE] = CERASE;
				cb.c_cc[VKILL] = CKILL;
				cb.c_cc[VQUIT] = CQUIT;
				cb.c_cc[VINTR] = CINTR;
				cb.c_cc[VEOF] = CEOF;
				cb.c_cc[VEOL] = CNUL;
			}
			for(i=0; speeds[i].string; i++)
				if(eq(speeds[i].string)) {
					cb.c_cflag &= ~CBAUD;
					cb.c_cflag |= speeds[i].speed&CBAUD;
				}
		}
		if (term == SYNC && eq("ctab") && --argc)
			cb.c_cc[7] = gct(*++argv);
		for(i=0; imodes[i].string; i++)
			if(eq(imodes[i].string)) {
				cb.c_iflag &= ~imodes[i].reset;
				cb.c_iflag |= imodes[i].set;
			}
		for(i=0; omodes[i].string; i++)
			if(eq(omodes[i].string)) {
				cb.c_oflag &= ~omodes[i].reset;
				cb.c_oflag |= omodes[i].set;
			}
		if(term == SYNC && eq("sane"))
			cb.c_oflag |= TAB3;
		for(i=0; cmodes[i].string; i++)
			if(eq(cmodes[i].string)) {
				cb.c_cflag &= ~cmodes[i].reset;
				cb.c_cflag |= cmodes[i].set;
			}
		for(i=0; lmodes[i].string; i++)
			if(eq(lmodes[i].string)) {
				cb.c_lflag &= ~lmodes[i].reset;
				cb.c_lflag |= lmodes[i].set;
			}
		if(!match)
			if(!encode(arg)) {
				fprintf(stderr, "unknown mode: %s\n", arg);
				exit(2);
			}
	}
	if (term == ASYNC) {
		if(ioctl(0, TCSETAW, &cb) == -1) {
			perror(STTY);
			exit(2);
		}
	} else {
		stio.imode = cb.c_iflag;
		stio.omode = cb.c_oflag;
		stio.lmode = cb.c_lflag;
		stio.tab = cb.c_cc[7];
		if (ioctl(0, STSET, &stio) == -1) {
			perror (STTY);
			exit(2);
		}
	}
	exit(0);
}

eq(string)
char *string;
{
	register i;

	if(!arg)
		return(0);
	i = 0;
loop:
	if(arg[i] != string[i])
		return(0);
	if(arg[i++] != '\0')
		goto loop;
	match++;
	return(1);
}

prmodes()
{
	register m;

	if (term == SYNC) {
		m = stio.imode;
		if (m & IUCLC) printf ("iuclc ");
		else printf ("-iuclc ");
		m = stio.omode;
		if (m & OLCUC) printf ("olcuc ");
		else printf ("-olcuc ");
		if (m & TAB3) printf ("tab3 ");
		m = stio.lmode;
		if (m & XCASE) printf ("xcase ");
		else printf ("-xcase ");
		if (m & STFLUSH) printf ("stflush ");
		else printf ("-stflush ");
		if (m & STWRAP) printf ("stwrap ");
		else printf ("-stwrap ");
		if (m & STAPPL) printf ("stappl ");
		else printf ("-stappl ");
		printf ("\n");
	}
	if (term == ASYNC) {
		m = cb.c_cflag;
		prspeed("speed ", m&CBAUD);
		if (m&PARENB)
			if (m&PARODD)
				printf("oddp ");
			else
				printf("evenp ");
		else
			printf("-parity ");
		if(((m&PARENB) && !(m&CS7)) || (!(m&PARENB) && !(m&CS8)))
			printf("cs%c ",'5'+(m&CSIZE)/CS6);
		if (m&CSTOPB)
			printf("cstopb ");
		if (m&HUPCL)
			printf("hupcl ");
		if (!(m&CREAD))
			printf("cread ");
		if (m&CLOCAL)
			printf("clocal ");
		printf("\n");
		if(cb.c_line != 0)
			printf("line = %d; ", cb.c_line);
		if(cb.c_cc[VINTR] != CINTR)
			pit(cb.c_cc[VINTR], "intr", "; ");
		if(cb.c_cc[VQUIT] != CQUIT)
			pit(cb.c_cc[VQUIT], "quit", "; ");
		if(cb.c_cc[VERASE] != CERASE)
			pit(cb.c_cc[VERASE], "erase", "; ");
		if(cb.c_cc[VKILL] != CKILL)
			pit(cb.c_cc[VKILL], "kill", "; ");
		if(cb.c_cc[VEOF] != CEOF)
			pit(cb.c_cc[VEOF], "eof", "; ");
		if(cb.c_cc[VEOL] != CNUL)
			pit(cb.c_cc[VEOL], "eol", "; ");
		if(pitt) printf("\n");
		m = cb.c_iflag;
		if (m&IGNBRK)
			printf("ignbrk ");
		else if (m&BRKINT)
			printf("brkint ");
		if (!(m&INPCK))
			printf("-inpck ");
		else if (m&IGNPAR)
			printf("ignpar ");
		if (m&PARMRK)
			printf("parmrk ");
		if (!(m&ISTRIP))
			printf("-istrip ");
		if (m&INLCR)
			printf("inlcr ");
		if (m&IGNCR)
			printf("igncr ");
		if (m&ICRNL)
			printf("icrnl ");
		if (m&IUCLC)
			printf("iuclc ");
		if (!(m&IXON))
			printf("-ixon ");
		else if (!(m&IXANY))
			printf("-ixany ");
		if (m&IXOFF)
			printf("ixoff ");
		m = cb.c_oflag;
		if (!(m&OPOST))
			printf("-opost ");
		else {
		if (m&OLCUC)
			printf("olcuc ");
		if (m&ONLCR)
			printf("onlcr ");
		if (m&OCRNL)
			printf("ocrnl ");
		if (m&ONOCR)
			printf("onocr ");
		if (m&ONLRET)
			printf("onlret ");
		if (m&OFILL)
			if (m&OFDEL)
				printf("del-fill ");
			else
				printf("nul-fill ");
		delay((m&CRDLY)/CR1, "cr");
		delay((m&NLDLY)/NL1, "nl");
		delay((m&TABDLY)/TAB1, "tab");
		delay((m&BSDLY)/BS1, "bs");
		delay((m&VTDLY)/VT1, "vt");
		delay((m&FFDLY)/FF1, "ff");
		}
		printf("\n");
		m = cb.c_lflag;
		if (!(m&ISIG))
			printf("-isig ");
		if (!(m&ICANON))
			printf("-icanon ");
		if (m&XCASE)
			printf("xcase ");
		printf("-echo "+((m&ECHO)!=0));
		printf("-echoe "+((m&ECHOE)!=0));
		printf("-echok "+((m&ECHOK)!=0));
		if (m&ECHONL)
			printf("echonl ");
		if (m&NOFLSH)
			printf("noflsh ");
		printf("\n");
	}
}

pramodes()
{
	register m;

	if(term == ASYNC) {
		prspeed("speed ", cb.c_cflag&CBAUD);
		printf("line = %d; ", cb.c_line);
		pit(cb.c_cc[VINTR], "intr", "; ");
		pit(cb.c_cc[VQUIT], "quit", "; ");
		pit(cb.c_cc[VERASE], "erase", "; ");
		pit(cb.c_cc[VKILL], "kill", "; ");
		pit(cb.c_cc[VEOF], "eof", "; ");
		pit(cb.c_cc[VEOL], "eol", "\n");
	} else
		pit(stio.tab, "ctab", "\n");
	m = cb.c_cflag;
	printf("-parenb "+((m&PARENB)!=0));
	printf("-parodd "+((m&PARODD)!=0));
	printf("cs%c ",'5'+(m&CSIZE)/CS6);
	printf("-cstopb "+((m&CSTOPB)!=0));
	printf("-hupcl "+((m&HUPCL)!=0));
	printf("-cread "+((m&CREAD)!=0));
	printf("-clocal "+((m&CLOCAL)!=0));
	printf("\n");
	m = cb.c_iflag;
	printf("-ignbrk "+((m&IGNBRK)!=0));
	printf("-brkint "+((m&BRKINT)!=0));
	printf("-ignpar "+((m&IGNPAR)!=0));
	printf("-parmrk "+((m&PARMRK)!=0));
	printf("-inpck "+((m&INPCK)!=0));
	printf("-istrip "+((m&ISTRIP)!=0));
	printf("-inlcr "+((m&INLCR)!=0));
	printf("-igncr "+((m&IGNCR)!=0));
	printf("-icrnl "+((m&ICRNL)!=0));
	printf("-iuclc "+((m&IUCLC)!=0));
	printf("\n");
	printf("-ixon "+((m&IXON)!=0));
	printf("-ixany "+((m&IXANY)!=0));
	printf("-ixoff "+((m&IXOFF)!=0));
	printf("\n");
	m = cb.c_lflag;
	printf("-isig "+((m&ISIG)!=0));
	printf("-icanon "+((m&ICANON)!=0));
	printf("-xcase "+((m&XCASE)!=0));
	printf("-echo "+((m&ECHO)!=0));
	printf("-echoe "+((m&ECHOE)!=0));
	printf("-echok "+((m&ECHOK)!=0));
	printf("-echonl "+((m&ECHONL)!=0));
	printf("-noflsh "+((m&NOFLSH)!=0));
	if(term == SYNC) {
		printf("-stflush "+((m&STFLUSH)!=0));
		printf("-stwrap "+((m&STWRAP)!=0));
		printf("-stappl "+((m&STAPPL)!=0));
	}
	printf("\n");
	m = cb.c_oflag;
	printf("-opost "+((m&OPOST)!=0));
	printf("-olcuc "+((m&OLCUC)!=0));
	printf("-onlcr "+((m&ONLCR)!=0));
	printf("-ocrnl "+((m&OCRNL)!=0));
	printf("-onocr "+((m&ONOCR)!=0));
	printf("-onlret "+((m&ONLRET)!=0));
	printf("-ofill "+((m&OFILL)!=0));
	printf("-ofdel "+((m&OFDEL)!=0));
	delay((m&CRDLY)/CR1, "cr");
	delay((m&NLDLY)/NL1, "nl");
	delay((m&TABDLY)/TAB1, "tab");
	delay((m&BSDLY)/BS1, "bs");
	delay((m&VTDLY)/VT1, "vt");
	delay((m&FFDLY)/FF1, "ff");
	printf("\n");
}

gct(cp)
register char *cp;
{
	register c;

	c = *cp++;
	if (c == '^') {
		c = *cp;
		if (c == '?')
			c = 0177;
		else if (c == '-')
			c = 0377;
		else
			c &= 037;
	}
	return(c);
}

pit(what, itsname, sep)
	unsigned char what;
	char *itsname, *sep;
{

	pitt++;
	printf("%s", itsname);
	if (what == 0377) {
		printf(" <undef>%s", sep);
		return;
	}
	printf(" = ");
	if (what & 0200) {
		printf("-");
		what &= ~ 0200;
	}
	if (what == 0177) {
		printf("DEL%s", sep);
		return;
	} else if (what < ' ') {
		printf("^");
		what += '`';
	}
	printf("%c%s", what, sep);
}

delay(m, s)
char *s;
{
	if(m)
		printf("%s%d ", s, m);
}

int	speed[] = {
	0,50,75,110,134,150,200,300,600,1200,1800,2400,4800,9600,0,0
};

prspeed(c, s)
char *c;
{

	printf("%s%d baud; ", c, speed[s]);
}

prencode()
{
	printf("%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x\n",
	cb.c_iflag,cb.c_oflag,cb.c_cflag,cb.c_lflag,cb.c_cc[0],
	cb.c_cc[1],cb.c_cc[2],cb.c_cc[3],cb.c_cc[4],cb.c_cc[5],
	cb.c_cc[6],cb.c_cc[7]);
}

encode(arg)
char *arg;
{
	int grab[12], i;
	i = sscanf(arg, "%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x",
	&grab[0],&grab[1],&grab[2],&grab[3],&grab[4],&grab[5],&grab[6],
	&grab[7],&grab[8],&grab[9],&grab[10],&grab[11]);

	if(i != 12) return(0);

	cb.c_iflag = (ushort) grab[0];
	cb.c_oflag = (ushort) grab[1];
	cb.c_cflag = (ushort) grab[2];
	cb.c_lflag = (ushort) grab[3];

	for(i=0; i<8; i++)
		cb.c_cc[i] = (char) grab[i+4];
	return(1);
}
