xref: /original-bsd/bin/stty/stty.c (revision 5b560bbe)
1 /*
2  * Copyright (c) 1980, 1989, 1991 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1980, 1989, 1991 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif not lint
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)stty.c	5.19 (Berkeley) 02/06/91";
15 #endif not lint
16 
17 /*
18  * set teletype modes
19  */
20 
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <sys/ioctl.h>
24 #include <sys/syslog.h>
25 #define KERNEL
26 #include <sys/tty.h>
27 #undef KERNEL
28 #include <sys/termios.h>
29 #include <sys/file.h>
30 #include <errno.h>
31 #include <ctype.h>
32 #include <stdio.h>
33 
34 #define eq(s1, s2)	(strcmp((s1), (s2)) == 0)
35 #define WRAPCOL 65
36 
37 struct modes {
38 	char *name;
39 	long set;
40 	long unset;
41 };
42 
43 struct modes imodes[] = {
44 	"ignbrk",	IGNBRK, 0,
45 	"-ignbrk",	0, IGNBRK,
46 	"brkint",	BRKINT, 0,
47 	"-brkint",	0, BRKINT,
48 	"ignpar",	IGNPAR, 0,
49 	"-ignpar",	0, IGNPAR,
50 	"parmrk",	PARMRK, 0,
51 	"-parmrk",	0, PARMRK,
52 	"inpck",	INPCK, 0,
53 	"-inpck",	0, INPCK,
54 	"istrip",	ISTRIP, 0,
55 	"-istrip",	0, ISTRIP,
56 	"inlcr",	INLCR, 0,
57 	"-inlcr",	0, INLCR,
58 	"igncr",	IGNCR, 0,
59 	"-igncr",	0, IGNCR,
60 	"icrnl",	ICRNL, 0,
61 	"-icrnl",	0, ICRNL,
62 	"ixon",		IXON, 0,
63 	"-ixon",	0, IXON,
64 	"flow",		IXON, 0,
65 	"-flow",	0, IXON,
66 	"ixoff",	IXOFF, 0,
67 	"-ixoff",	0, IXOFF,
68 	"tandem",	IXOFF, 0,
69 	"-tandem",	0, IXOFF,
70 	"ixany",	IXANY, 0,
71 	"-ixany",	0, IXANY,
72 	"decctlq",	0, IXANY,
73 	"-decctlq",	IXANY, 0,
74 	"imaxbel",	IMAXBEL, 0,
75 	"-imaxbel",	0, IMAXBEL,
76 	0
77 };
78 
79 struct modes omodes[] = {
80 	"opost",	OPOST, 0,
81 	"-opost",	0, OPOST,
82 	"-litout",	OPOST, 0,
83 	"litout",	0, OPOST,
84 	"onlcr",	ONLCR, 0,
85 	"-onlcr",	0, ONLCR,
86 	"tabs",		0, OXTABS,	/* "preserve" tabs */
87 	"-tabs",	OXTABS, 0,
88 	"xtabs",	OXTABS, 0,
89 	"-xtabs",	0, OXTABS,
90 	"oxtabs",	OXTABS, 0,
91 	"-oxtabs",	0, OXTABS,
92 	0
93 };
94 
95 struct modes cmodes[] = {
96 	"cs5",		CS5, CSIZE,
97 	"cs6",		CS6, CSIZE,
98 	"cs7",		CS7, CSIZE,
99 	"cs8",		CS8, CSIZE,
100 	"cstopb",	CSTOPB, 0,
101 	"-cstopb",	0, CSTOPB,
102 	"cread",	CREAD, 0,
103 	"-cread",	0, CREAD,
104 	"parenb",	PARENB, 0,
105 	"-parenb",	0, PARENB,
106 	"parodd",	PARODD, 0,
107 	"-parodd",	0, PARODD,
108 	"parity",	PARENB | CS7, PARODD | CSIZE,
109 	"evenp",	PARENB | CS7, PARODD | CSIZE,
110 	"oddp",		PARENB | CS7 | PARODD, CSIZE,
111 	"-parity",	CS8, PARODD | PARENB | CSIZE,
112 	"pass8",	CS8, PARODD | PARENB | CSIZE,
113 	"-evenp",	CS8, PARODD | PARENB | CSIZE,
114 	"-oddp",	CS8, PARODD | PARENB | CSIZE,
115 	"hupcl",	HUPCL, 0,
116 	"-hupcl",	0, HUPCL,
117 	"hup",		HUPCL, 0,
118 	"-hup",		0, HUPCL,
119 	"clocal",	CLOCAL, 0,
120 	"-clocal",	0, CLOCAL,
121 	"crtscts",	CRTSCTS, 0,
122 	"-crtscts",	0, CRTSCTS,
123 	0
124 };
125 
126 struct modes lmodes[] = {
127 	"echo",		ECHO, 0,
128 	"-echo",	0, ECHO,
129 	"echoe",	ECHOE, 0,
130 	"-echoe",	0, ECHOE,
131 	"crterase",	ECHOE, 0,
132 	"-crterase",	0, ECHOE,
133 	"crtbs",	ECHOE, 0,   /* crtbs not supported, close enough */
134 	"-crtbs",	0, ECHOE,
135 	"echok",	ECHOK, 0,
136 	"-echok",	0, ECHOK,
137 	"echoke",	ECHOKE, 0,
138 	"-echoke",	0, ECHOKE,
139 	"crtkill",	ECHOKE, 0,
140 	"-crtkill",	0, ECHOKE,
141 	"altwerase",	ALTWERASE, 0,
142 	"-altwerase",	0, ALTWERASE,
143 	"iexten",	IEXTEN, 0,
144 	"-iexten",	0, IEXTEN,
145 	"echonl",	ECHONL, 0,
146 	"-echonl",	0, ECHONL,
147 	"echoctl",	ECHOCTL, 0,
148 	"-echoctl",	0, ECHOCTL,
149 	"ctlecho",	ECHOCTL, 0,
150 	"-ctlecho",	0, ECHOCTL,
151 	"echoprt",	ECHOPRT, 0,
152 	"-echoprt",	0, ECHOPRT,
153 	"prterase",	ECHOPRT, 0,
154 	"-prterase",	0, ECHOPRT,
155 	"isig",		ISIG, 0,
156 	"-isig",	0, ISIG,
157 	"icanon",	ICANON, 0,
158 	"-icanon",	0, ICANON,
159 	"noflsh",	NOFLSH, 0,
160 	"-noflsh",	0, NOFLSH,
161 	"tostop",	TOSTOP, 0,
162 	"-tostop",	0, TOSTOP,
163 	"mdmbuf",	MDMBUF, 0,
164 	"-mdmbuf",	0, MDMBUF,
165 	"flusho",	FLUSHO, 0,
166 	"-flusho",	0, FLUSHO,
167 	"pendin",	PENDIN, 0,
168 	"-pendin",	0, PENDIN,
169 	"crt",		ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT,
170 	"-crt",		ECHOK, ECHOE|ECHOKE|ECHOCTL,
171 	"newcrt",	ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT,
172 	"-newcrt",	ECHOK, ECHOE|ECHOKE|ECHOCTL,
173 	"nokerninfo",	NOKERNINFO, 0,
174 	"-nokerninfo",	0, NOKERNINFO,
175 	"kerninfo",	0, NOKERNINFO,
176 	"-kerninfo",	NOKERNINFO, 0,
177 	0
178 };
179 
180 /*
181  * Special control characters.
182  *
183  * Each entry has a list of names.  The first is the primary name
184  * and is used when printing the control character in the "name = val;"
185  * form.  The second is an abbreviation which is guaranteed to be less
186  * than or equal to four characters in length and is primarily used
187  * when printing the values in columunar form (guarantees all will
188  * fit within 80 cols).  The rest are optional aliases.
189  * All names are recognized on the command line.
190  */
191 #define MAXNAMES 3
192 struct {
193 	char	*names[MAXNAMES+1];
194 	int	sub;
195 	u_char	def;
196 } cchars[] = {
197 	{{ "erase", "era" },		VERASE,	CERASE, },
198 	{{ "werase", "wera" },		VWERASE, CWERASE, },
199 	{{ "kill", "kill" },		VKILL,	CKILL, },
200 	{{ "intr", "int" },		VINTR,	CINTR, },
201 	{{ "quit", "quit" },		VQUIT,	CQUIT, },
202 	{{ "susp", "susp" },		VSUSP,	CSUSP, },
203 	{{ "dsusp", "dsus" },		VDSUSP,	CDSUSP, },
204 	{{ "eof", "eof" },		VEOF,	CEOF, },
205 	{{ "eol", "eol", "brk" },	VEOL,	CEOL, },
206 	{{ "eol2", "eol2" },		VEOL2,	CEOL, },
207 	{{ "stop", "stop", "xoff" },	VSTOP,	CSTOP, },
208 	{{ "start", "star", "xon" },	VSTART,	CSTART, },
209 	{{ "lnext", "lnxt" },		VLNEXT,	CLNEXT, },
210 	{{ "discard", "disc", "flush" },	VDISCARD, CDISCARD, },
211 	{{ "reprint", "rpnt", "rprnt" },	VREPRINT, CREPRINT, },
212 	{{ "status", "stat" },		VSTATUS, CSTATUS, },
213 	0
214 };
215 
216 struct winsize win;
217 int ldisc;
218 int debug = 0;
219 int trace, dotrace;
220 int extproc;
221 
222 #define OUT	stdout		/* informational output stream */
223 #define ERR	stderr		/* error message stream */
224 #define CTL	0		/* default control descriptor */
225 int ctl = CTL;
226 
227 extern errno;
228 
229 #define NORMAL	0	/* only print modes differing from defaults */
230 #define ALL	1	/* print all modes - POSIX standard format */
231 #define ALL_BSD	2	/* print all modes - using BSD shorthand for cc's */
232 #define	GFMT	3	/* print modes in form suitable to be re-input */
233 
234 main(argc, argv)
235 	char *argv[];
236 {
237 	struct termios t;
238 	int i, fmt = NORMAL;
239 	extern char *optarg;
240 	extern int optind;
241 	int ch;
242 
243 	argc--, argv++;
244 	if (argc > 0 && eq(argv[0], "-a")) {
245 		fmt = ALL;
246 		argc--, argv++;
247 	}
248 	if (argc > 0 && eq(argv[0], "-g")) {
249 		fmt = GFMT;
250 		argc--, argv++;
251 	}
252 	if (argc > 0 && eq(argv[0], "-f")) {
253 		argc--, argv++;
254 		if ((ctl = open(argv[0], O_RDONLY | O_NONBLOCK)) < 0)
255 			syserrexit(*argv);
256 		argc--, argv++;
257 	}
258 	if (ioctl(ctl, TIOCGETD, &ldisc) < 0)
259 		syserrexit("TIOCGETD");
260 	if (tcgetattr(ctl, &t) < 0)
261 		syserrexit("tcgetattr");
262 	if (ioctl(ctl, TIOCGWINSZ, &win) < 0)
263 		warning("TIOCGWINSZ: %s", strerror(errno));
264 	checkredirect();	/* conversion aid */
265 
266 	if (argc == 0 || fmt) {
267 		prmode(&t, ldisc, fmt);
268 		exit(0);
269 	}
270 
271 	while (*argv) {
272 		if (eq("everything", *argv)) {
273 			prmode(&t, ldisc, ALL_BSD);
274 			exit(0);
275 		}
276 		if (eq("all", *argv)) {
277 			prmode(&t, ldisc, ALL);
278 			exit(0);
279 		}
280 		if (eq("tty", *argv) || eq("old", *argv) || eq("new", *argv)) {
281 			int nldisc = TTYDISC;
282 
283 			if (ioctl(0, TIOCSETD, &nldisc) < 0)
284 				syserrexit("TIOCSETD");
285 			goto next;
286 		}
287 		if (eq("nl", *argv)) {
288 			t.c_iflag &= ~ICRNL;
289 			t.c_oflag &= ~ONLCR;
290 			goto next;
291 		}
292 		if (eq("-nl", *argv)) {
293 			t.c_iflag |= ICRNL;
294 			t.c_oflag |= ONLCR;
295 			goto next;
296 		}
297 		if (eq("dec", *argv)){
298 			t.c_cc[VERASE] = (u_char)0177;
299 			t.c_cc[VKILL] = CTRL('u');
300 			t.c_cc[VINTR] = CTRL('c');
301 			t.c_lflag &= ~ECHOPRT;
302 			t.c_lflag |= ECHOE|ECHOKE|ECHOCTL;
303 			t.c_iflag &= ~IXANY;
304 			goto next;
305 		}
306 		if (eq("raw", *argv)) {
307 			cfmakeraw(&t);
308 			t.c_cflag &= ~(CSIZE|PARENB);
309 			t.c_cflag |= CS8;
310 			goto next;
311 		}
312 		if (eq("cbreak", *argv)) {
313 			t.c_iflag |  BRKINT|IXON|IMAXBEL;
314 			t.c_oflag |= OPOST;
315 			t.c_lflag |= ISIG|IEXTEN;
316 			t.c_lflag &= ~ICANON;
317 		}
318 		if (eq("cooked", *argv) || eq("-raw", *argv) ||
319 		    eq("sane", *argv) || eq("-cbreak", *argv)) {
320 			t.c_cflag = TTYDEF_CFLAG | (t.c_cflag & CLOCAL);
321 			t.c_iflag = TTYDEF_IFLAG;
322 			t.c_iflag |= ICRNL;
323 			/* preserve user-preference flags in lflag */
324 #define	LKEEP	(ECHOKE|ECHOE|ECHOK|ECHOPRT|ECHOCTL|ALTWERASE|TOSTOP|NOFLSH)
325 			t.c_lflag = TTYDEF_LFLAG | (t.c_lflag & LKEEP);
326 			t.c_oflag = TTYDEF_OFLAG;
327 			goto next;
328 		}
329 		if (eq("rows", *argv)) {
330 			if (*(argv+1) == 0)
331 				goto setit;
332 			win.ws_row = atoi(*++argv);
333 			goto next;
334 		}
335 		if (eq("ispeed", *argv)) {
336 			int code;
337 			if (*(argv+1) == 0)
338 				errexit("missing ispeed");
339 			cfsetispeed(&t, atoi(*++argv));
340 			goto next;
341 		}
342 		if (eq("ospeed", *argv)) {
343 			if (*(argv+1) == 0)
344 				errexit("missing ospeed");
345 			cfsetospeed(&t, atoi(*++argv));
346 			goto next;
347 		}
348 		if (eq("cols", *argv) || eq("columns", *argv)) {
349 			if (*(argv+1) == 0)
350 				goto setit;
351 			win.ws_col = atoi(*++argv);
352 			goto next;
353 		}
354 		if (eq("size", *argv)) {
355 			put("%d %d\n", win.ws_row, win.ws_col);
356 			exit(0);
357 		}
358 		if (eq("extrpc", *argv) || eq("-extproc", *argv)) {
359 			if (**argv == '-')
360 				extproc = 0;
361 			else
362 				extproc = 1;
363 			ioctl(ctl, TIOCEXT, &extproc);
364 		}
365 		if (eq("speed", *argv)) {
366 			put("%d\n", cfgetospeed(&t));
367 			exit(0);
368 		}
369 		for (i=0; imodes[i].name; i++)
370 			if (eq(imodes[i].name, *argv)) {
371 				t.c_iflag &= ~imodes[i].unset;
372 				t.c_iflag |= imodes[i].set;
373 				goto next;
374 			}
375 		for (i=0; omodes[i].name; i++)
376 			if (eq(omodes[i].name, *argv)) {
377 				t.c_oflag &= ~omodes[i].unset;
378 				t.c_oflag |= omodes[i].set;
379 				goto next;
380 			}
381 		for (i=0; cmodes[i].name; i++)
382 			if (eq(cmodes[i].name, *argv)) {
383 				t.c_cflag &= ~cmodes[i].unset;
384 				t.c_cflag |= cmodes[i].set;
385 				goto next;
386 			}
387 		for (i=0; lmodes[i].name; i++)
388 			if (eq(lmodes[i].name, *argv)) {
389 				t.c_lflag &= ~lmodes[i].unset;
390 				t.c_lflag |= lmodes[i].set;
391 				goto next;
392 			}
393 		for (i=0; *cchars[i].names; i++) {
394 			char **cp = cchars[i].names;
395 			while (*cp) {
396 				if (eq(*cp, *argv)) {
397 					if (*++argv == 0)
398 						goto setit;
399 					if (eq(*argv, "undef") ||
400 					    eq(*argv, "disable"))
401 						t.c_cc[cchars[i].sub] =
402 						   _POSIX_VDISABLE;
403 					else if (**argv == '^')
404 						t.c_cc[cchars[i].sub] =
405 						    ((*argv)[1] == '?') ? 0177 :
406 						    ((*argv)[1] == '-') ?
407 						     _POSIX_VDISABLE :
408 						     (*argv)[1] & 037;
409 					else
410 						t.c_cc[cchars[i].sub] = **argv;
411 					goto next;
412 				}
413 				cp++;
414 			}
415 		}
416 		if (isdigit(**argv)) {
417 			cfsetospeed(&t, atoi(*argv));
418 			cfsetispeed(&t, atoi(*argv));
419 			goto next;
420 		}
421 		if (strncmp(*argv, "-gfmt", sizeof ("-gfmt") - 1) == 0) {
422 			gfmtset(&t, *argv);
423 			goto next;
424 		}
425 		/* didn't match anything */
426 		errexit("unknown option: %s", *argv);
427 		exit(1);
428 next:
429 		argv++;
430 	}
431 setit:
432 	if (tcsetattr(ctl, 0, &t) < 0)
433 		syserrexit("tcsetattr");
434 	if (ioctl(ctl, TIOCSWINSZ, &win) < 0)
435 		warning("can't set window size");
436 
437 	exit(0);
438 }
439 
440 gfmtset(tp, s)
441 	register struct termios *tp;
442 	char *s;
443 {
444 	register int cnt;
445 	char sep;
446 	char *saves = s;
447 	int cval;
448 #define advance(c)	while (*(s) && *(s) != (c)) (s)++; if (*s) (s)++ ; \
449 				else \
450 					errexit("bad gfmt operand: %s", saves)
451 #define chkeq(string)	if (strncmp(s, (string), strlen(string))) \
452 				errexit("bad gfmt operand: %s", saves)
453 
454 	if (s == NULL)
455 		errexit("missing gfmt string");
456 	advance(':');
457 	chkeq("iflag=");
458 	advance('=');
459 	sscanf(s, "%x", &tp->c_iflag);
460 
461 	advance(':');
462 	chkeq("oflag");
463 	advance('=');
464 	sscanf(s, "%x", &tp->c_oflag);
465 
466 	advance(':');
467 	chkeq("cflag");
468 	advance('=');
469 	sscanf(s, "%x", &tp->c_cflag);
470 
471 	advance(':');
472 	chkeq("lflag");
473 	advance('=');
474 	sscanf(s, "%x", &tp->c_lflag);
475 
476 	advance(':');
477 	chkeq("cc=");
478 
479 	for (cnt = 0, sep = '='; cnt < NCCS; cnt++, sep = ',') {
480 		advance(sep);
481 		sscanf(s, "%o", &cval);
482 		tp->c_cc[cnt] = cval;
483 	}
484 
485 	advance(':');
486 	chkeq("ispeed=");
487 	advance('=');
488 	sscanf(s, "%d", &tp->c_ispeed);
489 
490 	advance(':');
491 	chkeq("ospeed=");
492 	advance('=');
493 	sscanf(s, "%d", &tp->c_ospeed);
494 }
495 
496 prmode(tp, ldisc, fmt)
497 	struct termios *tp;
498 {
499 	long	i = tp->c_iflag,
500 		o = tp->c_oflag,
501 		c = tp->c_cflag,
502 		l = tp->c_lflag;
503 	u_char	*cc = tp->c_cc;
504 	int	ispeed = cfgetispeed(tp),
505 		ospeed = cfgetospeed(tp);
506 	char	unknown[32],
507 		*ld;
508 	char *ccval();
509 
510 	if (fmt == GFMT) {
511 		int	cnt;
512 		char	sep;
513 
514 		printf("-gfmt:iflag=%x:oflag=%x:cflag=%x:lflag=%x:cc",
515 			i, o, c, l);
516 		for (cnt = 0, sep = '='; cnt < NCCS; cnt++, sep = ',')
517 			printf("%c%o", sep, cc[cnt]);
518 		printf(":ispeed=%d:ospeed=%d:\n", ispeed, ospeed);
519 		return;
520 	}
521 
522 	/*
523 	 * line discipline
524 	 */
525 	if (ldisc != TTYDISC) {
526 		switch(ldisc) {
527 		case TABLDISC:
528 			ld = "tablet";
529 			break;
530 		case SLIPDISC:
531 			ld = "slip";
532 			break;
533 		default:
534 			sprintf(unknown, "#%d", ldisc);
535 			ld = unknown;
536 			break;
537 		}
538 		put("%s disc; ", ld);
539 	}
540 	/*
541 	 * line speed
542 	 */
543 	if (ispeed != ospeed)
544 		put("ispeed %d baud; ospeed %d baud;",
545 		     ispeed, ospeed);
546 	else
547 		put("speed %d baud;", ispeed);
548 	if (fmt)
549 		put(" %d rows; %d columns;", win.ws_row, win.ws_col);
550 	put("\n");
551 
552 #define lput(n, f, d) if (fmt || on(f) != d) mdput(n+on(f))
553 	/*
554 	 * "local" flags
555 	 */
556 #define on(f)	((l&f) != 0)
557 	if (debug) mdput("LFLAG: ");
558 	lput("-icanon ",ICANON, 1);
559 	lput("-isig ", ISIG, 1);
560 	lput("-iexten ", IEXTEN, 1);
561 	lput("-echo ",ECHO, 1);
562 	lput("-echoe ",ECHOE, 0);
563 	lput("-echok ",ECHOK, 0);
564 	lput("-echoke ",ECHOKE, 0);
565 	lput("-echonl ",ECHONL, 0);
566 	lput("-echoctl ",ECHOCTL, 0);
567 	lput("-echoprt ",ECHOPRT, 0);
568 	lput("-altwerase ",ALTWERASE, 0);
569 	lput("-noflsh ",NOFLSH, 0);
570 	lput("-tostop ",TOSTOP, 0);
571 	lput("-mdmbuf ",MDMBUF, 0);
572 	lput("-flusho ",FLUSHO, 0);
573 	lput("-pendin ",PENDIN, 0);
574 	lput("-nokerninfo ",NOKERNINFO, 0);
575 	lput("-extproc ",EXTPROC, 0);
576 	/*
577 	 * input flags
578 	 */
579 #undef on
580 #define on(f)	((i&f) != 0)
581 	mdput(0);
582 	if (debug) mdput("IFLAG: ");
583 	lput("-istrip ", ISTRIP, 0);
584 	lput("-icrnl ", ICRNL, 1);
585 	lput("-inlcr ", INLCR, 0);
586 	lput("-igncr ", IGNCR, 0);
587 	lput("-ixon ", IXON, 1);
588 	lput("-ixoff ", IXOFF, 0);
589 	lput("-ixany ", IXANY, 1);
590 	lput("-imaxbel ", IMAXBEL, 1);
591 	lput("-ignbrk ", IGNBRK, 0);
592 	lput("-brkint ", BRKINT, 1);
593 	lput("-inpck ", INPCK, 0);
594 	lput("-ignpar ", IGNPAR, 0);
595 	lput("-parmrk ", PARMRK, 0);
596 #undef on
597 	/*
598 	 * output flags
599 	 */
600 #define on(f)	((o&f) != 0)
601 	mdput(0);
602 	if (debug) mdput("OFLAG: ");
603 	lput("-opost ", OPOST, 1);
604 	lput("-onlcr ", ONLCR, 1);
605 	lput("-oxtabs ", OXTABS, 1);
606 #undef on
607 	/*
608 	 * control flags (hardware state)
609 	 */
610 #define on(f)	((c&f) != 0)
611 	mdput(0);
612 	if (debug) mdput("CFLAG: ");
613 	lput("-cread ", CREAD, 1);
614 	switch(c&CSIZE) {
615 	case CS5: mdput("cs5 "); break;
616 	case CS6: mdput("cs6 "); break;
617 	case CS7: mdput("cs7 "); break;
618 	case CS8: mdput("cs8 "); break;
619 	}
620 	mdput("-parenb "+on(PARENB));
621 	lput("-parodd ", PARODD, 0);
622 	lput("-hupcl ", HUPCL, 1);
623 	lput("-clocal ", CLOCAL, 0);
624 	lput("-cstopb ", CSTOPB, 0);
625 	lput("-crtscts ", CRTSCTS, 0);
626 	mdput(0);
627 #undef on
628 	/*
629 	 * special control characters
630 	 */
631 	if (debug) mdput("CCHARS: ");
632 	if (fmt != 2) {
633 		for (i=0; *cchars[i].names; i++) {
634 			char temp[64];
635 
636 			if (fmt || cc[cchars[i].sub] != cchars[i].def) {
637 				sprintf(temp, "%s = %s; ", *cchars[i].names,
638 					ccval(cc[cchars[i].sub]), fmt);
639 				mdput(temp);
640 			}
641 		}
642 		mdput(0);
643 	} else {
644 		for (i=0; *cchars[i].names; i++)
645 			put("%*s", strlen(*(cchars[i].names+1)) + (i>0?1:0),
646 				*(cchars[i].names+1));
647 		printf("\n");
648 		for (i=0; *cchars[i].names; i++)
649 			put("%*s", strlen(*(cchars[i].names+1)) + (i>0?1:0),
650 				ccval(cc[cchars[i].sub], fmt));
651 		printf("\n");
652 	}
653 }
654 
655 /*
656  * gross, but since we're changing the control descriptor
657  * from 1 to 0, most users will be probably be doing
658  * "stty > /dev/sometty" by accident. If 1 and 2 are both ttys,
659  * but not the same, assume that 1 was incorrectly redirected.
660  */
661 checkredirect() {
662 	struct stat st1, st2;
663 
664 	if (isatty(1) && isatty(2) && fstat(1, &st1) != -1 &&
665 	    fstat(2, &st2) != -1 && (st1.st_rdev != st2.st_rdev))
666 warning("stdout appears redirected, but stdin is the control descriptor");
667 }
668 
669 char *
670 ccval(c, fmt)
671 	unsigned char c;
672 {
673 	static char buf[128];
674 	char *bp;
675 
676 	*buf = 0, bp = buf;
677 	if (c == _POSIX_VDISABLE)
678 		if (fmt == 2)
679 			return("<u>");
680 		else
681 			return("<undef>");
682 	if (c & 0200) {
683 		strcat(buf, "M-");
684 		*bp++ = 'M';
685 		*bp++ = '-';
686 		c &= 0177;
687 	}
688 	if (c == 0177) {
689 		*bp++ = '^';
690 		*bp++ = '?';
691 	}
692 	else if (c < 040) {
693 		*bp++ = '^';
694 		*bp++ = c + '@';
695 	}
696 	else
697 		*bp++ = c;
698 	*bp = 0;
699 	return(buf);
700 }
701 
702 
703 mdput(s)
704 	char *s;
705 {
706 	static int col = 0;
707 
708 	if (s == (char *)0) {
709 		if (col) {
710 			put("\n");
711 			col = 0;
712 		}
713 		return;
714 	}
715 	if ((col += strlen(s)) > WRAPCOL) {
716 		put("\n");
717 		col = strlen(s);
718 	}
719 	put(s);
720 }
721 
722 #include <varargs.h>
723 
724 put(va_alist)
725 	va_dcl
726 {
727 	char *fmt;
728 	va_list ap;
729 
730 	va_start(ap);
731 	fmt = va_arg(ap, char *);
732 	(void) vfprintf(OUT, fmt, ap);
733 	va_end(ap);
734 }
735 
736 
737 warning(va_alist)
738 	va_dcl
739 {
740 	char *fmt;
741 	va_list ap;
742 
743 	fprintf(ERR, "stty: warning: ");
744 	va_start(ap);
745 	fmt = va_arg(ap, char *);
746 	(void) vfprintf(ERR, fmt, ap);
747 	va_end(ap);
748 	fprintf(ERR, "\n");
749 }
750 
751 
752 errexit(va_alist)
753 	va_dcl
754 {
755 	char *fmt;
756 	va_list ap;
757 
758 	fprintf(ERR, "stty: ");
759 	va_start(ap);
760 	fmt = va_arg(ap, char *);
761 	(void) vfprintf(ERR, fmt, ap);
762 	va_end(ap);
763 	fprintf(ERR, "\n");
764 	exit(1);
765 }
766 
767 
768 syserrexit(va_alist)
769 	va_dcl
770 {
771 	char *fmt;
772 	va_list ap;
773 
774 	fprintf(ERR, "stty: ");
775 	va_start(ap);
776 	fmt = va_arg(ap, char *);
777 	(void) vfprintf(ERR, fmt, ap);
778 	va_end(ap);
779 	fprintf(ERR, ": %s\n", strerror(errno));
780 	exit(1);
781 }
782