4.3BSD-Reno/src/bin/stty/stty.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright (c) 1980, 1989 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1980, 1989 Regents of the University of California.\n\
 All rights reserved.\n";
#endif not lint

#ifndef lint
static char sccsid[] = "@(#)stty.c	5.16 (Berkeley) 6/29/90";
#endif not lint

/*
 * set teletype modes
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/syslog.h>
#define KERNEL
#include <sys/tty.h>
#undef KERNEL
#include <sys/termios.h>
#include <sys/file.h>
#include <errno.h>
#include <ctype.h>
#include <stdio.h>

#define eq(s1, s2)	(strcmp((s1), (s2)) == 0)
#define WRAPCOL 65

struct modes {
	char *name;
	long set;
	long unset;
};

struct modes 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,
	"ixon",		IXON, 0,
	"-ixon",	0, IXON,
	"flow",		IXON, 0,
	"-flow",	0, IXON,
	"ixoff",	IXOFF, 0,
	"-ixoff",	0, IXOFF,
	"tandem",	IXOFF, 0,
	"-tandem",	0, IXOFF,
	"ixany",	IXANY, 0,
	"-ixany",	0, IXANY,
	"decctlq",	0, IXANY,
	"-decctlq",	IXANY, 0,
	"imaxbel",	IMAXBEL, 0,
	"-imaxbel",	0, IMAXBEL,
	0
};

struct modes omodes[] = {
	"opost",	OPOST, 0,
	"-opost",	0, OPOST,
	"-litout",	OPOST, 0,
	"litout",	0, OPOST,
	"onlcr",	ONLCR, 0,
	"-onlcr",	0, ONLCR,
	"tabs",		0, OXTABS,	/* "preserve" tabs */
	"-tabs",	OXTABS, 0,
	"xtabs",	OXTABS, 0,
	"-xtabs",	0, OXTABS,
	"oxtabs",	OXTABS, 0,
	"-oxtabs",	0, OXTABS,
	0
};

struct modes cmodes[] = {
	"cs5",		CS5, CSIZE,
	"cs6",		CS6, CSIZE,
	"cs7",		CS7, CSIZE,
	"cs8",		CS8, CSIZE,
	"cstopb",	CSTOPB, 0,
	"-cstopb",	0, CSTOPB,
	"cread",	CREAD, 0,
	"-cread",	0, CREAD,
	"parenb",	PARENB, 0,
	"-parenb",	0, PARENB,
	"parodd",	PARODD, 0,
	"-parodd",	0, PARODD,
	"parity",	PARENB | CS7, PARODD | CSIZE,
	"evenp",	PARENB | CS7, PARODD | CSIZE,
	"oddp",		PARENB | CS7 | PARODD, CSIZE,
	"-parity",	CS8, PARODD | PARENB | CSIZE,
	"pass8",	CS8, PARODD | PARENB | CSIZE,
	"-evenp",	CS8, PARODD | PARENB | CSIZE,
	"-oddp",	CS8, PARODD | PARENB | CSIZE,
	"hupcl",	HUPCL, 0,
	"-hupcl",	0, HUPCL,
	"hup",		HUPCL, 0,
	"-hup",		0, HUPCL,
	"clocal",	CLOCAL, 0,
	"-clocal",	0, CLOCAL,
	"crtscts",	CRTSCTS, 0,
	"-crtscts",	0, CRTSCTS,
	0
};

struct modes lmodes[] = {
	"echo",		ECHO, 0,
	"-echo",	0, ECHO,
	"echoe",	ECHOE, 0,
	"-echoe",	0, ECHOE,
	"crterase",	ECHOE, 0,
	"-crterase",	0, ECHOE,
	"crtbs",	ECHOE, 0,   /* crtbs not supported, close enough */
	"-crtbs",	0, ECHOE,
	"echok",	ECHOK, 0,
	"-echok",	0, ECHOK,
	"echoke",	ECHOKE, 0,
	"-echoke",	0, ECHOKE,
	"crtkill",	ECHOKE, 0,
	"-crtkill",	0, ECHOKE,
	"altwerase",	ALTWERASE, 0,
	"-altwerase",	0, ALTWERASE,
	"iexten",	IEXTEN, 0,
	"-iexten",	0, IEXTEN,
	"echonl",	ECHONL, 0,
	"-echonl",	0, ECHONL,
	"echoctl",	ECHOCTL, 0,
	"-echoctl",	0, ECHOCTL,
	"ctlecho",	ECHOCTL, 0,
	"-ctlecho",	0, ECHOCTL,
	"echoprt",	ECHOPRT, 0,
	"-echoprt",	0, ECHOPRT,
	"prterase",	ECHOPRT, 0,
	"-prterase",	0, ECHOPRT,
	"isig",		ISIG, 0,
	"-isig",	0, ISIG,
	"icanon",	ICANON, 0,
	"-icanon",	0, ICANON,
	"noflsh",	NOFLSH, 0,
	"-noflsh",	0, NOFLSH,
	"tostop",	TOSTOP, 0,
	"-tostop",	0, TOSTOP,
	"mdmbuf",	MDMBUF, 0,
	"-mdmbuf",	0, MDMBUF,
	"flusho",	FLUSHO, 0,
	"-flusho",	0, FLUSHO,
	"pendin",	PENDIN, 0,
	"-pendin",	0, PENDIN,
	"crt",		ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT,
	"-crt",		ECHOK, ECHOE|ECHOKE|ECHOCTL,
	"newcrt",	ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT,
	"-newcrt",	ECHOK, ECHOE|ECHOKE|ECHOCTL, 
	0
};

/*
 * Special control characters.
 *
 * Each entry has a list of names.  The first is the primary name
 * and is used when printing the control character in the "name = val;"
 * form.  The second is an abbreviation which is guaranteed to be less
 * than or equal to four characters in length and is primarily used
 * when printing the values in columunar form (guarantees all will
 * fit within 80 cols).  The rest are optional aliases.
 * All names are recognized on the command line.
 */
#define MAXNAMES 3
struct {
	char	*names[MAXNAMES+1];
	int	sub;
	u_char	def;
} cchars[] = {
	{{ "erase", "era" },		VERASE,	CERASE, },
	{{ "werase", "wera" },		VWERASE, CWERASE, },
	{{ "kill", "kill" },		VKILL,	CKILL, },
	{{ "intr", "int" },		VINTR,	CINTR, },
	{{ "quit", "quit" },		VQUIT,	CQUIT, },
	{{ "susp", "susp" },		VSUSP,	CSUSP, },
	{{ "dsusp", "dsus" },		VDSUSP,	CDSUSP, },
	{{ "eof", "eof" },		VEOF,	CEOF, },
	{{ "eol", "eol", "brk" },	VEOL,	CEOL, },
	{{ "eol2", "eol2" },		VEOL2,	CEOL, },
	{{ "stop", "stop", "xoff" },	VSTOP,	CSTOP, },
	{{ "start", "star", "xon" },	VSTART,	CSTART, },
	{{ "lnext", "lnxt" },		VLNEXT,	CLNEXT, },
	{{ "discard", "disc", "flush" },	VDISCARD, CDISCARD, },
	{{ "reprint", "rpnt", "rprnt" },	VREPRINT, CREPRINT, },
	{{ "status", "stat" },		VSTATUS, CSTATUS, },
	0
};

struct winsize win;
int ldisc;
int dodisc;
int debug = 0;
int trace, dotrace;

#define OUT	stdout		/* informational output stream */
#define ERR	stderr		/* error message stream */
#define CTL	0		/* default control descriptor */
int ctl = CTL;

extern errno;

#define NORMAL	0	/* only print modes differing from defaults */
#define ALL	1	/* print all modes - POSIX standard format */
#define ALL_BSD	2	/* print all modes - using BSD shorthand for cc's */
#define	GFMT	3	/* print modes in form suitable to be re-input */

main(argc, argv) 
	char *argv[];
{
	struct termios t;
	int i, fmt = NORMAL;
	extern char *optarg;
	extern int optind;
	int ch;

	argc--, argv++;
	if (argc > 0 && eq(argv[0], "-a")) {
		fmt = ALL;
		argc--, argv++;
	}
	if (argc > 0 && eq(argv[0], "-g")) {
		fmt = GFMT;
		argc--, argv++;
	}
	if (argc > 0 && eq(argv[0], "-f")) {
		argc--, argv++;
		if ((ctl = open(argv[0], O_RDONLY | O_NONBLOCK)) < 0)
			syserrexit(*argv);
		argc--, argv++;
	}
	if (ioctl(ctl, TIOCGETD, &ldisc) < 0)
		syserrexit("TIOCGETD");
	if (tcgetattr(ctl, &t) < 0)
		syserrexit("tcgetattr");
	if (ioctl(ctl, TIOCGWINSZ, &win) < 0)
		warning("TIOCGWINSZ: %s", strerror(errno));
	checkredirect();	/* conversion aid */

	if (argc == 0 || fmt) {
		prmode(&t, ldisc, fmt);
		exit(0);
	}
	
	while (*argv) {
		if (eq("everything", *argv)) {
			prmode(&t, ldisc, ALL_BSD);
			exit(0);
		}
		if (eq("all", *argv)) {
			prmode(&t, ldisc, ALL);
			exit(0);
		}
		if (eq("old", *argv)) {
			goto next;
		}
		if (eq("new", *argv)) {
			goto next;
		}
		if (eq("nl", *argv)) {
			t.c_iflag &= ~ICRNL;
			t.c_oflag &= ~ONLCR;
			goto next;
		}
		if (eq("-nl", *argv)) {
			t.c_iflag |= ICRNL;
			t.c_oflag |= ONLCR;
			goto next;
		}
		if (eq("dec", *argv)){
			t.c_cc[VERASE] = (u_char)0177;
			t.c_cc[VKILL] = CTRL('u');
			t.c_cc[VINTR] = CTRL('c');
			t.c_lflag &= ~ECHOPRT;
			t.c_lflag |= ECHOE|ECHOKE|ECHOCTL;
			t.c_iflag &= ~IXANY;
			goto next;
		}
		if (eq("raw", *argv)) {
			cfmakeraw(&t);
			t.c_cflag &= ~(CSIZE|PARENB);
			t.c_cflag |= CS8;
			goto next;
		}
		if (eq("cooked", *argv) || eq("-raw", *argv) ||
		    eq("sane", *argv)) {
			t.c_cflag = TTYDEF_CFLAG | (t.c_cflag & CLOCAL);
			t.c_iflag = TTYDEF_IFLAG;
			t.c_iflag |= ICRNL;
			/* preserve user-preference flags in lflag */
#define	LKEEP	(ECHOKE|ECHOE|ECHOK|ECHOPRT|ECHOCTL|ALTWERASE|TOSTOP|NOFLSH)
			t.c_lflag = TTYDEF_LFLAG | (t.c_lflag & LKEEP);
			t.c_oflag = TTYDEF_OFLAG;
			goto next;
		}
		if (eq("rows", *argv)) {
			if (*(argv+1) == 0)
				goto setit;
			win.ws_row = atoi(*++argv);
			goto next;
		}
		if (eq("ispeed", *argv)) {
			int code;
			if (*(argv+1) == 0)
				errexit("missing ispeed");
			cfsetispeed(&t, atoi(*++argv));
			goto next;
		}
		if (eq("ospeed", *argv)) {
			if (*(argv+1) == 0)
				errexit("missing ospeed");
			cfsetospeed(&t, atoi(*++argv));
			goto next;
		}
		if (eq("cols", *argv) || eq("columns", *argv)) {
			if (*(argv+1) == 0)
				goto setit;
			win.ws_col = atoi(*++argv);
			goto next;
		}
		if (eq("size", *argv)) {
			put("%d %d\n", win.ws_row, win.ws_col);
			exit(0);
		}
		if (eq("speed", *argv)) {
			put("%d\n", cfgetospeed(&t));
			exit(0);
		}
		for (i=0; imodes[i].name; i++)
			if (eq(imodes[i].name, *argv)) {
				t.c_iflag &= ~imodes[i].unset;
				t.c_iflag |= imodes[i].set;
				goto next;
			}
		for (i=0; omodes[i].name; i++)
			if (eq(omodes[i].name, *argv)) {
				t.c_oflag &= ~omodes[i].unset;
				t.c_oflag |= omodes[i].set;
				goto next;
			}
		for (i=0; cmodes[i].name; i++)
			if (eq(cmodes[i].name, *argv)) {
				t.c_cflag &= ~cmodes[i].unset;
				t.c_cflag |= cmodes[i].set;
				goto next;
			}
		for (i=0; lmodes[i].name; i++)
			if (eq(lmodes[i].name, *argv)) {
				t.c_lflag &= ~lmodes[i].unset;
				t.c_lflag |= lmodes[i].set;
				goto next;
			}
		for (i=0; *cchars[i].names; i++) {
			char **cp = cchars[i].names;
			while (*cp) {
				if (eq(*cp, *argv)) {
					if (*++argv == 0)
						goto setit;
					if (eq(*argv, "undef") || 
					    eq(*argv, "disable"))
						t.c_cc[cchars[i].sub] = 
						   _POSIX_VDISABLE;
					else if (**argv == '^')
						t.c_cc[cchars[i].sub] = 
						    ((*argv)[1] == '?') ? 0177 :
						    ((*argv)[1] == '-') ?
						     _POSIX_VDISABLE :
						     (*argv)[1] & 037;
					else
						t.c_cc[cchars[i].sub] = **argv;
					goto next;
				}
				cp++;
			}
		}
		if (isdigit(**argv)) {
			cfsetospeed(&t, atoi(*argv));
			cfsetispeed(&t, atoi(*argv));
			goto next;
		}
		if (strncmp(*argv, "-gfmt", sizeof ("-gfmt") - 1) == 0) {
			gfmtset(&t, *argv);
			goto next;
		}
		/* didn't match anything */
		errexit("unknown option: %s", *argv);
		exit(1);
next:
		argv++;
	}
setit:
	if (tcsetattr(ctl, 0, &t) < 0)
		syserrexit("tcsetattr");
	if (ioctl(ctl, TIOCSWINSZ, &win) < 0)
		warning("can't set window size");

	exit(0);
}

gfmtset(tp, s) 
	register struct termios *tp;
	char *s;
{
	register int cnt;
	char sep;
	char *saves = s;
	int cval;
#define advance(c)	while (*(s) && *(s) != (c)) (s)++; if (*s) (s)++ ; \
				else \
					errexit("bad gfmt operand: %s", saves)
#define chkeq(string)	if (strncmp(s, (string), strlen(string))) \
				errexit("bad gfmt operand: %s", saves)

	if (s == NULL)
		errexit("missing gfmt string");
	advance(':');
	chkeq("iflag=");
	advance('=');
	sscanf(s, "%x", &tp->c_iflag);

	advance(':');
	chkeq("oflag");
	advance('=');
	sscanf(s, "%x", &tp->c_oflag);

	advance(':');
	chkeq("cflag");
	advance('=');
	sscanf(s, "%x", &tp->c_cflag);

	advance(':');
	chkeq("lflag");
	advance('=');
	sscanf(s, "%x", &tp->c_lflag);

	advance(':');
	chkeq("cc=");

	for (cnt = 0, sep = '='; cnt < NCCS; cnt++, sep = ',') {
		advance(sep);
		sscanf(s, "%o", &cval);
		tp->c_cc[cnt] = cval;
	}

	advance(':');
	chkeq("ispeed=");
	advance('=');
	sscanf(s, "%d", &tp->c_ispeed);

	advance(':');
	chkeq("ospeed=");
	advance('=');
	sscanf(s, "%d", &tp->c_ospeed);
}

prmode(tp, ldisc, fmt)
	struct termios *tp;
{
	long	i = tp->c_iflag,
		o = tp->c_oflag,
		c = tp->c_cflag,
		l = tp->c_lflag;
	u_char	*cc = tp->c_cc;
	int	ispeed = cfgetispeed(tp),
		ospeed = cfgetospeed(tp);
	char	unknown[32],
		*ld;
	char *ccval();

	if (fmt == GFMT) {
		int	cnt;
		char	sep;

		printf("-gfmt:iflag=%x:oflag=%x:cflag=%x:lflag=%x:cc",
			i, o, c, l);
		for (cnt = 0, sep = '='; cnt < NCCS; cnt++, sep = ',')
			printf("%c%o", sep, cc[cnt]);
		printf(":ispeed=%d:ospeed=%d:\n", ispeed, ospeed);
		return;
	}
	
	/*
	 * line discipline
	 */
	if (ldisc != TTYDISC) {
		switch(ldisc) {
		case TABLDISC:	
			ld = "tablet"; 
			break;
		case SLIPDISC:	
			ld = "slip"; 
			break;
		default:	
			sprintf(unknown, "#%d", ldisc);
			ld = unknown;
		}
		put("%s disc; ", ld);
	}
	/*
	 * line speed
	 */
	if (ispeed != ospeed)
		put("ispeed %d baud; ospeed %d baud;",
		     ispeed, ospeed);
	else
		put("speed %d baud;", ispeed);
	if (fmt) 
		put(" %d rows; %d columns;", win.ws_row, win.ws_col);
	put("\n");

#define lput(n, f, d) if (fmt || on(f) != d) mdput(n+on(f))
	/*
	 * "local" flags
	 */
#define on(f)	((l&f) != 0)
	if (debug) mdput("LFLAG: ");
	lput("-icanon ",ICANON, 1);
	lput("-isig ", ISIG, 1);
	lput("-iexten ", IEXTEN, 1);
	lput("-echo ",ECHO, 1);
	lput("-echoe ",ECHOE, 0);
	lput("-echok ",ECHOK, 0);
	lput("-echoke ",ECHOKE, 0);
	lput("-echonl ",ECHONL, 0);
	lput("-echoctl ",ECHOCTL, 0);
	lput("-echoprt ",ECHOPRT, 0);
	lput("-altwerase ",ALTWERASE, 0);
	lput("-noflsh ",NOFLSH, 0);
	lput("-tostop ",TOSTOP, 0);
	lput("-mdmbuf ",MDMBUF, 0);
	lput("-flusho ",FLUSHO, 0);
	lput("-pendin ",PENDIN, 0);
	/*
	 * input flags
	 */
#undef on
#define on(f)	((i&f) != 0)
	mdput(0);
	if (debug) mdput("IFLAG: ");
	lput("-istrip ", ISTRIP, 0);
	lput("-icrnl ", ICRNL, 1);
	lput("-inlcr ", INLCR, 0);
	lput("-igncr ", IGNCR, 0);
	lput("-ixon ", IXON, 1);
	lput("-ixoff ", IXOFF, 0);
	lput("-ixany ", IXANY, 1);
	lput("-imaxbel ", IMAXBEL, 1);
	lput("-ignbrk ", IGNBRK, 0);
	lput("-brkint ", BRKINT, 1);
	lput("-inpck ", INPCK, 0);
	lput("-ignpar ", IGNPAR, 0);
	lput("-parmrk ", PARMRK, 0);
#undef on
	/*
	 * output flags
	 */
#define on(f)	((o&f) != 0)
	mdput(0);
	if (debug) mdput("OFLAG: ");
	lput("-opost ", OPOST, 1);
	lput("-onlcr ", ONLCR, 1);
	lput("-oxtabs ", OXTABS, 1);
#undef on
	/*
	 * control flags (hardware state)
	 */
#define on(f)	((c&f) != 0)
	mdput(0);
	if (debug) mdput("CFLAG: ");
	lput("-cread ", CREAD, 1);
	switch(c&CSIZE) {
	case CS5: mdput("cs5 "); break;
	case CS6: mdput("cs6 "); break;
	case CS7: mdput("cs7 "); break;
	case CS8: mdput("cs8 "); break;
	}
	mdput("-parenb "+on(PARENB));
	lput("-parodd ", PARODD, 0);
	lput("-hupcl ", HUPCL, 1);
	lput("-clocal ", CLOCAL, 0);
	lput("-cstopb ", CSTOPB, 0);
	lput("-crtscts ", CRTSCTS, 0);
	mdput(0);
#undef on
	/*
	 * special control characters
	 */
	if (debug) mdput("CCHARS: ");
	if (fmt != 2) {
		for (i=0; *cchars[i].names; i++) {
			char temp[64];

			if (fmt || cc[cchars[i].sub] != cchars[i].def) {
				sprintf(temp, "%s = %s; ", *cchars[i].names,
					ccval(cc[cchars[i].sub]), fmt);
				mdput(temp);
			}
		}
		mdput(0);
	} else {
		for (i=0; *cchars[i].names; i++)
			put("%*s", strlen(*(cchars[i].names+1)) + (i>0?1:0),
				*(cchars[i].names+1));
		printf("\n");
		for (i=0; *cchars[i].names; i++)
			put("%*s", strlen(*(cchars[i].names+1)) + (i>0?1:0),
				ccval(cc[cchars[i].sub], fmt));
		printf("\n");
	}
}

/*
 * gross, but since we're changing the control descriptor
 * from 1 to 0, most users will be probably be doing
 * "stty > /dev/sometty" by accident. If 1 and 2 are both ttys, 
 * but not the same, assume that 1 was incorrectly redirected.
 */
checkredirect() {
	struct stat st1, st2;

	if (isatty(1) && isatty(2) && fstat(1, &st1) != -1 && 
	    fstat(2, &st2) != -1 && (st1.st_rdev != st2.st_rdev))
warning("stdout appears redirected, but stdin is the control descriptor");
}

char *
ccval(c, fmt)
	unsigned char c;
{
	static char buf[128];
	char *bp;

	*buf = 0, bp = buf;
	if (c == _POSIX_VDISABLE)
		if (fmt == 2)
			return("<u>");
		else
			return("<undef>");
	if (c & 0200) {
		strcat(buf, "M-");
		*bp++ = 'M';
		*bp++ = '-';
		c &= 0177;
	}
	if (c == 0177) {
		*bp++ = '^';
		*bp++ = '?';
	}
	else if (c < 040) {
		*bp++ = '^';
		*bp++ = c + '@';
	}
	else
		*bp++ = c;
	*bp = 0;
	return(buf);
}


mdput(s)
	char *s;
{
	static int col = 0;

	if (s == (char *)0) {
		if (col) {
			put("\n");
			col = 0;
		}
		return;
	}
	if ((col += strlen(s)) > WRAPCOL) {
		put("\n");
		col = strlen(s);
	}
	put(s);
}

#include <varargs.h>

put(va_alist)
	va_dcl
{
	char *fmt;
	va_list ap;

	va_start(ap);
	fmt = va_arg(ap, char *);
	(void) vfprintf(OUT, fmt, ap);
	va_end(ap);
}


warning(va_alist)
	va_dcl
{
	char *fmt;
	va_list ap;

	fprintf(ERR, "stty: warning: ");
	va_start(ap);
	fmt = va_arg(ap, char *);
	(void) vfprintf(ERR, fmt, ap);
	va_end(ap);
	fprintf(ERR, "\n");
}


errexit(va_alist)
	va_dcl
{
	char *fmt;
	va_list ap;

	fprintf(ERR, "stty: ");
	va_start(ap);
	fmt = va_arg(ap, char *);
	(void) vfprintf(ERR, fmt, ap);
	va_end(ap);
	fprintf(ERR, "\n");
	exit(1);
}


syserrexit(va_alist)
	va_dcl
{
	char *fmt;
	va_list ap;

	fprintf(ERR, "stty: ");
	va_start(ap);
	fmt = va_arg(ap, char *);
	(void) vfprintf(ERR, fmt, ap);
	va_end(ap);
	fprintf(ERR, ": %s\n", strerror(errno));
	exit(1);
}