1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1992-2010 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                 Glenn Fowler <gsf@research.att.com>                  *
18*                  David Korn <dgk@research.att.com>                   *
19*                                                                      *
20***********************************************************************/
21#pragma prototyped
22/*
23 * stty.c
24 * Written by David Korn
25 * Tue Apr  4 10:46:00 EDT 1995
26 */
27
28static const char usage[] =
29"[-?@(#)$Id: stty (AT&T Research) 2008-11-10 $\n]"
30USAGE_LICENSE
31"[+NAME?stty - set or get terminal modes]"
32"[+DESCRIPTION?\bstty\b sets certain terminal I/O modes for the device "
33	"that is the current standard input; without arguments, it writes "
34	"the settings of certain modes to standard output.]"
35
36"[a:all?Writes to standard output all of the mode settings.]"
37"[g:save?Writes the current settings to standard output in a form that can "
38	"be used as an argument to another \bstty\b command.  The \brows\b "
39	"and \bcolumns\b values are not included.]"
40"[t:terminal-group?Print the terminal group id of the device, -1 if unknown.]"
41"\n"
42"\n[mode ...]\n"
43"\n"
44"[+EXTENDED DESCRIPTION?Modes are specified either as a single name or "
45	"as a name followed by a value.  As indicated below, many of the "
46	"mode names can be preceded by a \b-\b to negate its meaning.  "
47	"Modes are listed by group corresponding to field in the "
48	"\btermios\b structure defined in \b<termios.h>\b.  Modes "
49	"in the last group are implemented using options in the previous "
50	"groups.  Note that many combinations of modes make no sense, but "
51	"no sanity checking is performed.  The modes are selected from the "
52	"following:]{\fabc\f}"
53
54"[+EXIT STATUS?]{"
55      "[+0?All modes reported or set successfully.]"
56        "[+>0?Standard input not a terminaol or one or more modes failed.]"
57"}"
58"[+SEE ALSO?\btegetattr\b(2), \btcsetattr\b(2), \bioctl\b(2)]"
59;
60
61#include	<cmd.h>
62#include	<ccode.h>
63#include	<ctype.h>
64#include	<ast_tty.h>
65#if _sys_ioctl
66#include	<sys/ioctl.h>
67#endif
68
69#define C(x)	ERROR_catalog(x)
70
71#ifndef _POSIX_VDISABLE
72#   define _POSIX_VDISABLE 0
73#endif
74
75#ifndef NCCS
76#   ifdef NCC
77#	define NCCS	NCC
78#   else
79#	define NCCS	elementsof(((struct termio*)0)->c_cc)
80#   endif
81#endif
82
83/* command options */
84#define A_FLAG	1
85#define G_FLAG	2
86#define T_FLAG	4
87
88/* termios fields */
89#define C_FLAG	1
90#define C_LINE	2
91#define C_SPEED	3
92#define I_FLAG	4
93#define O_FLAG	5
94#define L_FLAG	6
95#define T_CHAR	7
96#define W_SIZE	8
97
98#define BIT	1
99#define BITS	2
100#define NUM	3
101#define CHAR	4
102#define SPEED	5
103#define SIZE	6
104#define MIXED	7
105#define SANE	8
106#define COOKED	9
107#define CASE	10
108#define TABS	11
109#define WIND	12
110
111#undef	SS			/* who co-opted this namespace?	*/
112
113#define IG	0x0001		/* ignore display		*/
114#define NL	0x0002		/* entry ends line of display	*/
115#define SS	0x0004		/* set in sane mode		*/
116#define US	0x0010		/* unset in sane mode		*/
117
118typedef struct tty_s
119{
120	const char	name[8];
121	unsigned char	type;
122	unsigned char	field;
123	short		flags;
124	unsigned long	mask;
125	unsigned long	val;
126	const char	description[76];
127} Tty_t;
128
129static const Tty_t Ttable[] =
130{
131#ifdef CBAUD
132{ "ispeed",	NUM,	C_SPEED,0,	CBAUD, 0, C("\an\a is the input baud rate") },
133{ "ospeed",	NUM,	C_SPEED,0,	CBAUD, 0, C("\an\a is the output baud rate") },
134{ "speed",	NUM,	C_SPEED,IG,	CBAUD },
135#endif
136{ "0",		SPEED,	C_FLAG,	0,	B0 },
137{ "50",		SPEED,	C_FLAG,	0,	B50 },
138{ "75",		SPEED,	C_FLAG,	0,	B75 },
139{ "110",	SPEED,	C_FLAG,	0,	B110 },
140{ "134",	SPEED,	C_FLAG,	0,	B134 },
141{ "150",	SPEED,	C_FLAG,	0,	B150 },
142{ "200",	SPEED,	C_FLAG,	0,	B200 },
143{ "300",	SPEED,	C_FLAG,	0,	B300 },
144{ "600",	SPEED,	C_FLAG,	0,	B600 },
145{ "1200",	SPEED,	C_FLAG,	0,	B1200 },
146{ "1800",	SPEED,	C_FLAG,	0,	B1800 },
147{ "2400",	SPEED,	C_FLAG,	0,	B2400 },
148{ "4800",	SPEED,	C_FLAG,	0,	B4800 },
149{ "9600",	SPEED,	C_FLAG,	0,	B9600 },
150{ "19200",	SPEED,	C_FLAG,	0,	B19200 },
151{ "38400",	SPEED,	C_FLAG,	0,	B38400 },
152
153#ifdef TIOCSWINSZ
154{ "rows",	WIND,	W_SIZE,	IG,	0, 24, C("\an\a is the number of lines for display") },
155{ "cols",	WIND,	W_SIZE,	IG,	1, 80, C("\an\a is the number of columns for display") },
156{ "columns",	WIND,	W_SIZE,	IG,	1, 80, C("Same as \bcols\b") },
157#endif
158{ "intr",	CHAR,	T_CHAR,	SS,	VINTR, 'C', C("Send an interrupt signal") },
159{ "quit",	CHAR,	T_CHAR,	SS,	VQUIT, '|', C("Send a quit signal") },
160{ "erase",	CHAR,	T_CHAR,	SS,	VERASE, 'H', C("Erase the last character entered") },
161{ "kill",	CHAR,	T_CHAR,	NL|SS,	VKILL, 'U', C("Erase the current line") },
162{ "eof",	CHAR,	T_CHAR,	SS,	VEOF, 'D', C("Send an end of file") },
163#ifdef VEOL2
164{ "eol2",	CHAR,	T_CHAR,	US,	VEOL2, _POSIX_VDISABLE, C("Alternate character to end the line") },
165#endif /* VEOL2 */
166#ifdef VSWTCH
167{ "swtch",	CHAR,	T_CHAR,	US,	VSWTCH, _POSIX_VDISABLE, C("Switch to a different shell layer") },
168#endif /* VSWTCH */
169{ "eol",	CHAR,	T_CHAR,	NL|US,	VEOL, _POSIX_VDISABLE, C("End the line") },
170#ifdef VSTART
171{ "start",	CHAR,	T_CHAR,	SS,	VSTART, 'Q', C("Restart the output after stopping it") },
172#endif /* VSTART */
173#ifdef VSTOP
174{ "stop",	CHAR,	T_CHAR,	SS,	VSTOP, 'S', C("Stop the output") },
175#endif /* VSTOP */
176#ifdef VDSUSP
177{ "dsusp",	CHAR,	T_CHAR,	SS,	VDSUSP, 'Y', C("Send a terminal stop signal after flushing the input") },
178#endif /* VDSUSP */
179#ifdef VSUSP
180{ "susp",	CHAR,	T_CHAR,	NL|SS,	VSUSP, 'Z', C("Send a terminal stop signal") },
181#endif /* VSUSP */
182#ifdef VREPRINT
183{ "rprnt",	CHAR,	T_CHAR,	SS,	VREPRINT, 'R', C("Redraw the current line") },
184#endif /* VREPRINT */
185#ifdef VDISCARD
186{ "flush",	CHAR,	T_CHAR,	SS,	VDISCARD, 'O', C("Discard output") },
187#endif /* VDISCARD */
188#ifdef VWERASE
189{ "werase",	CHAR,	T_CHAR,	SS,	VWERASE, 'W', C("Erase the last word entered") },
190#endif /* VWERASE */
191#ifdef VLNEXT
192{ "lnext",	CHAR,	T_CHAR,	NL|SS,	VLNEXT, 'V', C("Enter the next input character literally") },
193#endif /* VLNEXT */
194
195#if _mem_c_line_termios
196{ "line",	NUM,	C_LINE,	0,	0, 0, C("Line discipline number") },
197#endif
198{ "min",	NUM,	T_CHAR,	0,	VMIN, 0, C("Mininmum number of characters to read in raw mode") },
199{ "time",	NUM,	T_CHAR,	0,	VTIME, 0, C("Number of .1 second intervals with raw mode") },
200
201{ "parenb",	BIT,	C_FLAG,	0,	PARENB,	PARENB, C("Enable (disable) parity generation and detection") },
202{ "parodd",	BIT,	C_FLAG,	0,	PARODD, PARODD, C("Use odd (even) parity") },
203#ifdef PAREXT
204{ "parext",	BIT,	C_FLAG,	0,	PAREXT, PAREXT },
205#endif /* PAREXT */
206#ifdef CREAD
207{ "cread",	BIT,	C_FLAG,	SS,	CREAD, CREAD, C("Enable (disable) input") },
208#endif /* CREAD */
209{ "cs5",	SIZE,	C_FLAG,	0,	CSIZE,	CS5 , C("Char size 5") },
210{ "cs6",	SIZE,	C_FLAG,	0,	CSIZE,	CS6 , C("Char size 6") },
211{ "cs7",	SIZE,	C_FLAG,	0,	CSIZE,	CS7 , C("Char size 7") },
212{ "cs8",	SIZE,	C_FLAG,	0,	CSIZE,	CS8 , C("Char size 8") },
213{ "hupcl",	BIT,	C_FLAG,	0,	HUPCL, HUPCL, C("Hangup (do not hangup) connection on last close") },
214{ "hup",	BIT,	C_FLAG,	IG,	HUPCL, HUPCL, C("Same as \bhupcl\b") },
215{ "cstopb",	BIT,	C_FLAG,	0,	CSTOPB, CSTOPB, C("Use two (one) stop bits") },
216#ifdef CRTSCTS
217{ "crtscts",	BIT,	C_FLAG,	0,	CRTSCTS, CRTSCTS, C("Enable (disable) RTS/CTS handshaking") },
218#endif /* CRTSCTS */
219{ "clocal",	BIT,	C_FLAG,	NL,	CLOCAL, CLOCAL, C("Disable (enable) modem control signals") },
220
221{ "ignbrk",	BIT,	I_FLAG,	US,	IGNBRK, IGNBRK, C("Ignore (do not ignore) break characters") },
222{ "brkint",	BIT,	I_FLAG,	SS,	BRKINT, BRKINT, C("Generate (do not generate) INTR signal on break") },
223{ "ignpar",	BIT,	I_FLAG,	0,	IGNPAR, IGNPAR, C("Ignore (do not ignore) characters with parity errors") },
224{ "parmrk",	BIT,	I_FLAG,	0,	PARMRK, PARMRK, C("Mark (do not mark) parity errors") },
225{ "inpck",	BIT,	I_FLAG,	0,	INPCK, INPCK, C("Enable (disable) input parity checking") },
226{ "istrip",	BIT,	I_FLAG,	0,	ISTRIP, ISTRIP, C("Clear (do not clear) high bit of input characters") },
227{ "inlcr",	BIT,	I_FLAG,	US,	INLCR, INLCR, C("Translate (do not translate) carriage return to newline") },
228{ "igncr",	BIT,	I_FLAG,	US,	IGNCR, IGNCR, C("Ignore (do not ignore) carriage return") },
229#ifdef IUCLC
230{ "iuclc",	BIT,	I_FLAG,	US,	IUCLC, IUCLC, C("Map (do not map) upper-case to lower case") },
231#endif /* IUCLC */
232{ "ixon",	BIT,	I_FLAG,	0,	IXON, IXON, C("Enable (disable) XON/XOFF flow control. \bstop\b character stops output") },
233#ifdef IXANY
234{ "ixany",	BIT,	I_FLAG,	US,	IXANY, IXANY, C("Any character (only start character) can restart output.") },
235{ "decctlq",	BIT,	I_FLAG,	IG,	IXANY, 0, C("Same as \b-ixany\b") },
236#endif /* IXANY */
237{ "ixoff",	BIT,	I_FLAG,	US,	IXOFF, IXOFF, C("Disable (enable) XON/XOFF flow control") },
238#ifdef IMAXBEL
239{ "imaxbel",	BIT,	I_FLAG,	SS,	IMAXBEL, IMAXBEL, C("Beep (do not beep) if a character arrives with full input buffer") },
240#endif /* IMAXBEL */
241{ "icrnl",	BIT,	I_FLAG,	NL|SS,	ICRNL, ICRNL, C("Translate (do not translate) carriage return to newline") },
242
243{ "isig",	BIT,	L_FLAG,	SS,	ISIG, ISIG, C("Enable (disable) \bintr\b, \bquit\b, and \bsusp\b special characters") },
244{ "icanon",	BIT,	L_FLAG,	SS,	ICANON, ICANON, C("Enable (disable) \berase\b, \bkill\b, \bwerase\b, and \brprnt\b special characters") },
245{ "icannon",	BIT,	L_FLAG,	SS,	ICANON, ICANON },
246#ifdef IEXTEN
247{ "iexten",	BIT,	L_FLAG,	SS,	IEXTEN, IEXTEN, C("Enable (disable) non-POSIX special characters") },
248#endif /* IEXTEN */
249{ "echo",	BIT,	L_FLAG,	SS,	ECHO|ECHONL, ECHO|ECHONL, C("Echo (do not echo) input characters") },
250{ "echoe",	BIT,	L_FLAG,	SS,	ECHOE, ECHOE, C("Echo (do not echo) erase characters as backspace-space-backspace") },
251{ "echok",	BIT,	L_FLAG,	SS,	ECHOK, ECHOK, C("Echo (do not echo) a newline after a kill character") },
252#ifdef ECHOKE
253{ "echoke",	BIT,	L_FLAG,	SS,	ECHOKE, ECHOKE, C("Echo (do not echo) a newline after a kill character") },
254#endif
255{ "lfkc",	BIT,	L_FLAG,	IG,	ECHOK, ECHOK, C("Same as \bechok\b (\b-echok\b); obsolete") },
256{ "echonl",	BIT,	L_FLAG,	SS,	ECHONL, ECHONL,"Echo (do not echo) newline even if not echoing other character" },
257#ifdef ECHOCTL
258{ "echoctl",	BIT,	L_FLAG,	SS,	ECHOCTL, ECHOCTL, C("Echo (do not echo) control characters as \b^\b\ac\a") },
259#else
260#define ECHOCTL		0
261#endif /* ECHOCTL */
262#ifdef ECHOPRT
263{ "echoprt",	BIT,	L_FLAG,	US,	ECHOPRT, ECHOPRT, C("Echo (do not echo) erased characters backward, between '\\' and '/'") },
264#else
265#define ECHOPRT		0
266#endif /* ECHOPRT */
267#ifdef XCASE
268{ "xcase",	BIT,	L_FLAG,	US,	XCASE, XCASE, C("Enable (disable) \bicanon\b uppercase as lowercase with '\\' prefix") },
269#endif /* XCASE */
270#ifdef DEFECHO
271{ "defecho",	BIT,	L_FLAG,	0,	DEFECHO, DEFECHO },
272#endif /* DEFECHO */
273#ifdef FLUSHO
274{ "flusho",	BIT,	L_FLAG,	0,	FLUSHO, FLUSHO, C("Discard (do not discard) written data. Cleared by subsequent input") },
275#endif /* FLUSHO */
276#ifdef PENDIN
277{ "pendin",	BIT,	L_FLAG,	0,	PENDIN, PENDIN, C("Redisplay pending input at next read and then automatically clear \bpendin\b") },
278#endif /* PENDIN */
279{ "noflsh",	BIT,	L_FLAG,	US,	NOFLSH, NOFLSH, C("Disable (enable) flushing after \bintr\b and \bquit\b special characters") },
280#ifdef TOSTOP
281{ "tostop",	BIT,	L_FLAG,	NL|US,	TOSTOP, TOSTOP, C("Stop (do not stop) background jobs that try to write to the terminal") },
282#endif /* TOSTOP */
283#ifdef OLCUC
284{ "olcuc",	BIT,	O_FLAG,	US,	OLCUC, OLCUC, C("Translate (do not translate) lowercase characters to uppercase") },
285#endif /* OLCUC */
286#ifdef ONLCR
287{ "onlcr",	BIT,	O_FLAG,	SS,	ONLCR, ONLCR, C("Translate (do not translate) newline to carriage return-newline") },
288#endif /* ONLCR */
289#ifdef ONLRET
290{ "onlret",	BIT,	O_FLAG,	US,	ONLRET, ONLRET, C("Newline performs (does not perform) a carriage return") },
291#endif /* ONLRET */
292#ifdef OCRNL
293{ "ocrnl",	BIT,	O_FLAG,	US,	OCRNL, OCRNL, C("Translate (do not translate) carriage return to newline") },
294#endif /* OCRNL */
295#ifdef ONOCR
296{ "onocr",	BIT,	O_FLAG,	US,	ONOCR, ONOCR, C("Do not (do) print carriage returns in the first column") },
297#endif /* ONOCR */
298#ifdef OFILL
299{ "ofill",	BIT,	O_FLAG,	US,	OFILL, OFILL, C("Use fill characters (use timing) for delays") },
300#endif /* OFILL */
301#ifdef OFDEL
302{ "ofdel",	BIT,	O_FLAG,	US,	OFDEL, OFDEL, C("Use DEL (NUL) as fill characters for delays") },
303#endif /* OFDEL */
304{ "opost",	BIT,	O_FLAG,	SS,	OPOST, OPOST, C(" Postprocess (do not postprocess) output") },
305#ifdef CRDLY
306{ "cr0",	BITS,	O_FLAG,	IG|SS,	CRDLY, CR0  },
307{ "cr1",	BITS,	O_FLAG,	US,	CRDLY, CR1  },
308{ "cr2",	BITS,	O_FLAG,	US,	CRDLY, CR2  },
309{ "cr3",	BITS,	O_FLAG,	US,	CRDLY, CR3  },
310#endif
311#ifdef NLDLY
312{ "nl0",	BITS,	O_FLAG,	IG|US,	NLDLY, NL0  },
313{ "nl1",	BITS,	O_FLAG,	US,	NLDLY, NL1  },
314#endif
315#ifdef TABDLY
316{ "tabs",	TABS,	O_FLAG,	IG,	TABDLY, TAB3, C("Preserve (expand to spaces) tabs") },
317#ifdef TAB0
318{ "tab0",	BITS,	O_FLAG,	IG|SS,	TABDLY, TAB0  },
319#endif
320#ifdef TAB1
321{ "tab1",	BITS,	O_FLAG,	US,	TABDLY, TAB1  },
322#endif
323#ifdef TAB2
324{ "tab2",	BITS,	O_FLAG,	US,	TABDLY, TAB2  },
325#endif
326{ "tab3",	BITS,	O_FLAG,	US,	TABDLY, TAB3  },
327#endif
328#ifdef BSDLY
329{ "bs0",	BITS,	O_FLAG,	IG|SS,	BSDLY, BS0 },
330{ "bs1",	BITS,	O_FLAG,	US,	BSDLY, BS1  },
331#endif
332#ifdef VTDLY
333{ "vt0",	BITS,	O_FLAG,	IG|SS,	VTDLY, VT0  },
334{ "vt1",	BITS,	O_FLAG,	US,	VTDLY, VT1  },
335#endif
336#ifdef FFDLY
337{ "ff0",	BITS,	O_FLAG,	IG|SS,	FFDLY, FF0 },
338{ "ff1",	BITS,	O_FLAG,	US,	FFDLY, FF1 },
339#endif
340{ "",		MIXED,	O_FLAG,	NL|IG },
341
342{ "evenp",	MIXED,	C_FLAG,	IG,	PARENB, 0, C("Same as \bparenb -parodd cs7\b") },
343{ "oddp",	MIXED,	C_FLAG,	IG,	PARODD, 0, C("Same as \bparenb parodd cs7\b") },
344{ "parity",	MIXED,	C_FLAG,	IG,	0, 0, C("Same as parenb \b-parodd cs7\b") },
345{ "ek",		MIXED,	C_FLAG,	IG,	0, 0, C("Reset the \berase\b and \bkill\b special characters to their default values") },
346{ "sane",	SANE,	C_FLAG,	IG,	0, 0, C("Reset all modes to some reasonable values") },
347{ "cooked",	COOKED,	C_FLAG,	IG,	0, 0, C("Disable raw input and output") },
348{ "raw",	COOKED,	C_FLAG,	IG,	0, 0, C("Enable raw input and output") },
349{ "lcase",	CASE,	C_FLAG,	IG,	0 , 0, C("Set \bxcase\b, \biuclc\b, and \bolcuc\b") },
350{ "LCASE",	CASE,	C_FLAG,	IG,	0 , 0, C("Same as \blcase\b") }
351};
352
353#if CC_NATIVE == CC_ASCII
354#define cntl(x)		(((x)=='?')?0177:((x)&037))
355#else
356#define cntl(x)		(((x)=='?')?ccmapc(0177,CC_ASCII,CC_NATIVE):ccmapc(ccmapc(x,CC_NATIVE,CC_ASCII)&037,CC_ASCII,CC_NATIVE))
357#endif
358
359static void sane(register struct termios *sp)
360{
361	register const Tty_t*	tp;
362
363	for (tp = Ttable; tp < &Ttable[elementsof(Ttable)]; tp++)
364		if (tp->flags & (SS|US))
365			switch (tp->type)
366			{
367			case BIT:
368			case BITS:
369				switch (tp->field)
370				{
371				case C_FLAG:
372					if (tp->flags & SS)
373						sp->c_cflag |= tp->mask;
374					else
375						sp->c_cflag &= ~tp->mask;
376					break;
377				case I_FLAG:
378					if (tp->flags & SS)
379						sp->c_iflag |= tp->mask;
380					else
381						sp->c_iflag &= ~tp->mask;
382					break;
383				case O_FLAG:
384					if (tp->flags & SS)
385						sp->c_oflag |= tp->mask;
386					else
387						sp->c_oflag &= ~tp->mask;
388					break;
389				case L_FLAG:
390					if (tp->flags & SS)
391						sp->c_lflag |= tp->mask;
392					else
393						sp->c_lflag &= ~tp->mask;
394					break;
395				}
396				break;
397			case CHAR:
398				sp->c_cc[tp->mask] = cntl(tp->val);
399				break;
400			}
401}
402
403static int gin(char *arg,struct termios *sp)
404{
405	register int i;
406	if(*arg++ != ':')
407		return(0);
408	sp->c_iflag = strtol(arg,&arg,16);
409	if(*arg++ != ':')
410		return(0);
411	sp->c_oflag = strtol(arg,&arg,16);
412	if(*arg++ != ':')
413		return(0);
414	sp->c_cflag = strtol(arg,&arg,16);
415	if(*arg++ != ':')
416		return(0);
417	sp->c_lflag = strtol(arg,&arg,16);
418	if(*arg++ != ':')
419		return(0);
420	for(i=0;i< NCCS; i++)
421	{
422		sp->c_cc[i] = strtol(arg,&arg,16);
423		if(*arg++ != ':')
424			return(0);
425	}
426#if _mem_c_line_termios
427	sp->c_line =
428#endif
429		strtol(arg,&arg,16);
430	if(*arg++ != ':')
431		return(0);
432	i = strtol(arg,&arg,16);
433	if(*arg++ != ':')
434		return(0);
435	cfsetispeed(sp, i);
436	i = strtol(arg,&arg,16);
437	if(*arg++ != ':')
438		return(0);
439	cfsetospeed(sp, i);
440	if(*arg)
441		return(0);
442	return(1);
443}
444
445static void gout(struct termios *sp)
446{
447	register int i;
448	sfprintf(sfstdout,":%x",sp->c_iflag);
449	sfprintf(sfstdout,":%x",sp->c_oflag);
450	sfprintf(sfstdout,":%x",sp->c_cflag);
451	sfprintf(sfstdout,":%x",sp->c_lflag);
452	for(i=0;i< NCCS; i++)
453		sfprintf(sfstdout,":%x",sp->c_cc[i]);
454#if _mem_c_line_termios
455	sfprintf(sfstdout,":%x", sp->c_line);
456#else
457	sfprintf(sfstdout,":%x", 0);
458#endif
459	sfprintf(sfstdout,":%x",cfgetispeed(sp));
460	sfprintf(sfstdout,":%x",cfgetospeed(sp));
461	sfprintf(sfstdout,":\n");
462}
463
464static void output(struct termios *sp, int flags)
465{
466	const Tty_t *tp;
467	struct termios tty;
468	register int delim = ' ';
469	register int i,off,off2;
470	char schar[2];
471	unsigned int ispeed = cfgetispeed(sp);
472	unsigned int ospeed = cfgetospeed(sp);
473	if(flags&G_FLAG)
474	{
475		gout(sp);
476		return;
477	}
478	tty = *sp;
479	sane(&tty);
480	for(i=0; i < elementsof(Ttable); i++)
481	{
482		tp= &Ttable[i];
483		if(tp->flags&IG)
484		{
485			if(tp->flags&NL)
486				sfputc(sfstdout,'\n');
487			continue;
488		}
489		switch(tp->type)
490		{
491		    case BIT:
492		    case BITS:
493			off = off2 = 1;
494			switch(tp->field)
495			{
496			    case C_FLAG:
497				if(sp->c_cflag&tp->mask)
498					off = 0;
499				if(tty.c_cflag&tp->mask)
500					off2 = 0;
501				break;
502			    case I_FLAG:
503				if(sp->c_iflag&tp->mask)
504					off = 0;
505				if(tty.c_iflag&tp->mask)
506					off2 = 0;
507				break;
508			    case O_FLAG:
509				if((sp->c_oflag&tp->mask)==tp->val)
510					off = 0;
511				if(tty.c_oflag&tp->mask)
512					off2 = 0;
513				break;
514			    case L_FLAG:
515				if(sp->c_lflag&tp->mask)
516					off = 0;
517				if(tty.c_lflag&tp->mask)
518					off2 = 0;
519			}
520			if(tp->flags&NL)
521				delim = '\n';
522			if(!flags && off==off2)
523				continue;
524			if(!off)
525				sfprintf(sfstdout,"%s%c",tp->name,delim);
526			else if(tp->type==BIT)
527				sfprintf(sfstdout,"-%s%c",tp->name,delim);
528			delim = ' ';
529			break;
530
531		    case CHAR:
532			off = sp->c_cc[tp->mask];
533			if(tp->flags&NL)
534				delim = '\n';
535			if(!flags && off==(unsigned char)tty.c_cc[tp->mask])
536				continue;
537			if(off==_POSIX_VDISABLE)
538				sfprintf(sfstdout,"%s = <undef>;%c",tp->name,delim);
539			else if(isprint(off&0xff))
540				sfprintf(sfstdout,"%s = %c;%c",tp->name,off,delim);
541			else
542#if CC_NATIVE == CC_ASCII
543			sfprintf(sfstdout,"%s = ^%c;%c",tp->name,off==0177?'?':(off^0100),delim);
544#else
545			{
546				off = ccmapc(off, CC_NATIVE, CC_ASCII);
547				sfprintf(sfstdout,"%s = ^%c;%c",tp->name,off==0177?'?':ccmapc(off^0100,CC_ASCII,CC_NATIVE),delim);
548			}
549#endif
550			delim = ' ';
551			break;
552		    case SIZE:
553			if((sp->c_cflag&CSIZE)!=tp->mask)
554				continue;
555			if(flags || (sp->c_cflag&CSIZE) != (tty.c_cflag&CSIZE))
556				sfprintf(sfstdout,"%s ",tp->name);
557			break;
558		    case SPEED:
559			if(tp->mask==ispeed)
560			{
561				if(ispeed!=ospeed)
562					schar[0]='i';
563				else
564					schar[0]=0;
565			}
566			else if(tp->mask==ospeed)
567				schar[0]='o';
568			else
569				continue;
570			schar[1] = 0;
571#ifdef TIOCSWINSZ
572			{
573				struct winsize win;
574				off = ioctl(0,TIOCGWINSZ,&win);
575				if(off>=0)
576					sfprintf(sfstdout,"%sspeed %s baud; rows %d; columns %d;\n",schar,tp->name,win.ws_row,win.ws_col);
577			}
578			if(off<0)
579#endif
580				sfprintf(sfstdout,"%sspeed %s baud;\n",schar,tp->name);
581		}
582	}
583	if(delim=='\n')
584		sfputc(sfstdout,'\n');
585}
586
587static const Tty_t *lookup(const char *name)
588{
589	register int i;
590	for(i=0; i < elementsof(Ttable); i++)
591	{
592		if(strcmp(Ttable[i].name,name)==0)
593			return(&Ttable[i]);
594	}
595	return(0);
596
597}
598
599static const Tty_t *getspeed(unsigned long val)
600{
601	register int i;
602	for(i=0; i < elementsof(Ttable); i++)
603	{
604		if(Ttable[i].type==SPEED && Ttable[i].mask==val)
605			return(&Ttable[i]);
606	}
607	return(0);
608}
609
610static int gettchar(register const char *cp)
611{
612	if(*cp==0)
613		return(-1);
614	if(cp[1]==0)
615		return((unsigned)cp[0]);
616	if(*cp=='^' && cp[1] && cp[2]==0)
617	{
618		switch(cp[1])
619		{
620		    case '-':
621			return(-1);
622		    default:
623			return(cntl(cp[1]));
624		}
625	}
626	if(streq(cp,"undef") || streq(cp,"<undef>"))
627		return(-1);
628	return(*((unsigned char*)cp));
629}
630
631static void set(char *argv[], struct termios *sp)
632{
633	const Tty_t *tp;
634	register int c,off;
635	char *cp;
636	char *ep;
637	while(cp = *argv++)
638	{
639		off = 0;
640		if(*cp=='-')
641		{
642			cp++;
643			off=1;
644		}
645		if(!(tp=lookup(cp)) || (off && (tp->type!=BIT) && (tp->type!=TABS)))
646			error(ERROR_exit(1),"%s: unknown mode",cp);
647		switch(tp->type)
648		{
649		    case CHAR:
650			if(off)
651				error(ERROR_exit(1),"%s: unknown mode",cp);
652			if(!*argv)
653				error(ERROR_exit(1),"missing argument to %s",cp);
654			c = gettchar(*argv++);
655			if(c>=0)
656				sp->c_cc[tp->mask] = c;
657			else
658				sp->c_cc[tp->mask] = _POSIX_VDISABLE;
659			break;
660		    case BIT: case BITS:
661			switch(tp->field)
662			{
663			    case C_FLAG:
664				if(off)
665					sp->c_cflag &= ~tp->mask;
666				else
667					sp->c_cflag |= tp->mask;
668				break;
669			    case I_FLAG:
670				if(off)
671					sp->c_iflag &= ~tp->mask;
672				else
673					sp->c_iflag |= tp->mask;
674				break;
675			    case O_FLAG:
676				sp->c_oflag &= ~tp->mask;
677				sp->c_oflag |= tp->val;
678				break;
679			    case L_FLAG:
680				if(off)
681					sp->c_lflag &= ~tp->mask;
682				else
683					sp->c_lflag |= tp->mask;
684				break;
685			}
686			break;
687		    case TABS:
688			sp->c_oflag &= ~tp->mask;
689			if(off)
690				sp->c_oflag |= tp->val;
691			break;
692#ifdef TIOCSWINSZ
693		    case WIND:
694		    {
695			struct winsize win;
696			int n;
697			if(ioctl(0,TIOCGWINSZ,&win)<0)
698				error(ERROR_system(1),"cannot set %s",tp->name);
699			if(!(cp= *argv))
700			{
701				sfprintf(sfstdout,"%d\n",tp->mask?win.ws_col:win.ws_row);
702				break;
703			}
704			argv++;
705			n=strtol(cp,&cp,10);
706			if(*cp)
707				error(ERROR_system(1),"%d: invalid number of %s",argv[-1],tp->name);
708			if(tp->mask)
709				win.ws_col = n;
710			else
711				win.ws_row = n;
712			if(ioctl(0,TIOCSWINSZ,&win)<0)
713				error(ERROR_system(1),"cannot set %s",tp->name);
714			break;
715		    }
716#endif
717		    case NUM:
718			cp = *argv;
719			if (!cp)
720			{
721				if (tp->field == C_SPEED)
722				{
723					if (tp = getspeed(*tp->name == 'i' ? cfgetispeed(sp) : cfgetospeed(sp)))
724						sfprintf(sfstdout, "%s\n", tp->name);
725					break;
726				}
727				error(ERROR_exit(1), "%s: missing numeric argument", tp->name);
728			}
729			argv++;
730			c = (int)strtol(cp, &ep, 10);
731			if (*ep)
732				error(ERROR_exit(1), "%s: %s: numeric argument expected", tp->name, cp);
733			switch (tp->field)
734			{
735#if _mem_c_line_termios
736			case C_LINE:
737				sp->c_line = c;
738				break;
739#endif
740			case C_SPEED:
741				if(getspeed(c))
742				{
743					if (*tp->name != 'o')
744						cfsetispeed(sp, c);
745					if (*tp->name != 'i')
746						cfsetospeed(sp, c);
747				}
748				else
749					error(ERROR_exit(1), "%s: %s: invalid speed", tp->name, cp);
750				break;
751			case T_CHAR:
752				sp->c_cc[tp->mask] = c;
753				break;
754			}
755			break;
756		    case SPEED:
757			cfsetospeed(sp, tp->mask);
758			cfsetispeed(sp, tp->mask);
759			break;
760		    case SIZE:
761			sp->c_cflag &= ~CSIZE;
762			sp->c_cflag |= tp->mask;
763			break;
764		    case SANE:
765			sane(sp);
766			break;
767#if defined(OLCUC) && defined(IUCLC)
768		    case CASE:
769			if(off)
770			{
771				sp->c_iflag |= IUCLC;
772				sp->c_oflag |= OLCUC;
773			}
774			else
775			{
776				sp->c_iflag &= ~IUCLC;
777				sp->c_oflag &= ~OLCUC;
778			}
779			break;
780#endif /* OLCUC && IUCLC */
781		}
782	}
783}
784
785
786static void listchars(Sfio_t *sp,int type)
787{
788	int i,c;
789	c = (type==CHAR?'c':'n');
790	for(i=0; i < elementsof(Ttable); i++)
791	{
792		if(Ttable[i].type==type && *Ttable[i].description)
793			sfprintf(sp,"[+%s \a%c\a?%s.]",Ttable[i].name,c,Ttable[i].description);
794	}
795}
796
797static void listgroup(Sfio_t *sp,int type, const char *description)
798{
799	int i;
800	sfprintf(sp,"[+");
801	for(i=0; i < elementsof(Ttable); i++)
802	{
803		if(Ttable[i].type==type)
804			sfprintf(sp,"%s ",Ttable[i].name);
805	}
806	sfprintf(sp,"?%s.]",description);
807}
808
809static void listmask(Sfio_t *sp,unsigned int mask,const char *description)
810{
811	int i;
812	sfprintf(sp,"[+");
813	for(i=0; i < elementsof(Ttable); i++)
814	{
815		if(Ttable[i].mask==mask && Ttable[i].type==BITS)
816			sfprintf(sp,"%s ",Ttable[i].name);
817	}
818	sfprintf(sp,"?%s.]",description);
819}
820
821static void listfields(Sfio_t *sp,int field)
822{
823	int i;
824	for(i=0; i < elementsof(Ttable); i++)
825	{
826		if(Ttable[i].field==field &&  Ttable[i].type==BIT && *Ttable[i].description)
827			sfprintf(sp,"[+%s (-%s)?%s.]",Ttable[i].name,Ttable[i].name,Ttable[i].description);
828	}
829}
830
831static void listmode(Sfio_t *sp,const char *name)
832{
833	sfprintf(sp,"[+%s?%s.]",name,lookup(name)->description);
834}
835
836static int infof(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp)
837{
838	NoP(op);
839	NoP(s);
840	NoP(dp);
841	sfprintf(sp,"[+Control Modes.]{");
842	listfields(sp,C_FLAG);
843	listgroup(sp,SPEED,"Attempt to set input and output baud rate to number given.  A value of \b0\b causes immediate hangup");
844	listchars(sp,NUM);
845	listgroup(sp,SIZE,"Number of bits in a character");
846	sfprintf(sp,"}[+Input Modes.]{");
847	listfields(sp,I_FLAG);
848	sfprintf(sp,"}[+Output Modes.]{");
849	listfields(sp,O_FLAG);
850#ifdef CRDLY
851	listmask(sp,CRDLY,"Carriage return delay style");
852#endif
853#ifdef NLDLY
854	listmask(sp,NLDLY,"Newline delay style");
855#endif
856#ifdef TABDLY
857	listmask(sp,TABDLY,"Horizontal tab delay style");
858#endif
859#ifdef BSDLY
860	listmask(sp,BSDLY,"Backspace delay style");
861#endif
862#ifdef FFDLY
863	listmask(sp,FFDLY,"Form feed delay style");
864#endif
865#ifdef VTDLY
866	listmask(sp,VTDLY,"Vertical tab delay style");
867#endif
868	sfprintf(sp,"}[+Local Modes.]{");
869	listfields(sp,L_FLAG);
870	sfprintf(sp,"}[+Control Assignments.?If \ac\a is \bundef\b or an empty "
871		"string then the control assignment is disabled.]{");
872	listchars(sp,WIND);
873	listchars(sp,CHAR);
874	sfprintf(sp,"}[+Combination Modes.]{");
875	listmode(sp,"ek");
876	listmode(sp,"evenp");
877	listmode(sp,"lcase");
878	listmode(sp,"oddp");
879	listmode(sp,"parity");
880	listmode(sp,"sane");
881	listmode(sp,"tabs");
882	listmode(sp,"LCASE");
883	sfputc(sp,'}');
884	return(1);
885}
886
887#ifndef _lib_tcgetpgrp
888#  ifdef TIOCGPGRP
889	   static int _i_;
890#	   define tcgetpgrp(a) (ioctl(a, TIOCGPGRP, &_i_)>=0?_i_:-1)
891#  else
892#	   define tcgetpgrp(a) (-1)
893#  endif /* TIOCGPGRP */
894#endif /* _lib_tcgetpgrp */
895
896int
897b_stty(int argc, char** argv, void* context)
898{
899	struct termios		tty;
900	register int		n;
901	register int		flags = 0;
902	const Tty_t*		tp;
903	Optdisc_t		disc;
904
905	cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_INTERACTIVE);
906	if (tcgetattr(0, &tty) < 0)
907		error(ERROR_system(1),"not a tty");
908	memset(&disc, 0, sizeof(disc));
909	disc.version = OPT_VERSION;
910	disc.infof = infof;
911	opt_info.disc = &disc;
912	for (;;)
913	{
914		switch (n = optget(argv, usage))
915		{
916		case 'a':
917		case 'g':
918		case 't':
919			if (!opt_info.offset || !argv[opt_info.index][opt_info.offset])
920			{
921				switch (n)
922				{
923				case 'a':
924					flags |= A_FLAG;
925					break;
926				case 'g':
927					flags |= G_FLAG;
928					break;
929				case 't':
930					flags |= T_FLAG;
931					break;
932				}
933				continue;
934			}
935			/*FALLTHROUGH*/
936		case ':':
937			if (!opt_info.offset)
938				error(2, "%s", opt_info.arg);
939			else if (!(tp = lookup(argv[opt_info.index]+1)) || (tp->type != BIT && tp->type != TABS))
940				error(ERROR_exit(1), "%s: unknown mode", argv[opt_info.index]);
941			break;
942		case '?':
943			error(ERROR_usage(2), "%s", opt_info.arg);
944			break;
945		}
946		break;
947	}
948	argv += opt_info.index;
949	if (error_info.errors || (flags && *argv) || (flags&(flags-1)))
950		error(ERROR_usage(2), "%s", optusage(NiL));
951	if (flags & T_FLAG)
952		sfprintf(sfstdout, "%d\n", tcgetpgrp(0));
953	else if (*argv)
954	{
955		if (!argv[1] && **argv == ':')
956			gin(*argv, &tty);
957		else
958			set(argv, &tty);
959		if (tcsetattr(0, TCSANOW, &tty) < 0)
960			error(ERROR_system(1), "cannot set tty");
961	}
962	else
963		output(&tty, flags);
964	return error_info.errors;
965}
966