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