diff options
author | Eric Andersen <andersen@codepoet.org> | 2001-02-14 18:47:33 +0000 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 2001-02-14 18:47:33 +0000 |
commit | 98e599ca063bbba13e0806d54f8caa9b0d10ba56 (patch) | |
tree | 69f3fb0fcada0398c27cc3d6e26a25144876fff4 /coreutils | |
parent | ec455953008d08f588fe0c0bd9df4278cd188315 (diff) | |
download | busybox-w32-98e599ca063bbba13e0806d54f8caa9b0d10ba56.tar.gz busybox-w32-98e599ca063bbba13e0806d54f8caa9b0d10ba56.tar.bz2 busybox-w32-98e599ca063bbba13e0806d54f8caa9b0d10ba56.zip |
Commit Vladimir's latest version of stty.c. Nice work.
-Erik
Diffstat (limited to 'coreutils')
-rw-r--r-- | coreutils/stty.c | 1392 |
1 files changed, 1392 insertions, 0 deletions
diff --git a/coreutils/stty.c b/coreutils/stty.c new file mode 100644 index 000000000..0fc0bfbc2 --- /dev/null +++ b/coreutils/stty.c | |||
@@ -0,0 +1,1392 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* stty -- change and print terminal line settings | ||
3 | Copyright (C) 1990-1999 Free Software Foundation, Inc. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2, or (at your option) | ||
8 | any later version. | ||
9 | |||
10 | You should have received a copy of the GNU General Public License | ||
11 | along with this program; if not, write to the Free Software Foundation, | ||
12 | Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | ||
13 | |||
14 | /* Usage: stty [-ag] [-F device] [setting...] | ||
15 | |||
16 | Options: | ||
17 | -a Write all current settings to stdout in human-readable form. | ||
18 | -g Write all current settings to stdout in stty-readable form. | ||
19 | -F Open and use the specified device instead of stdin | ||
20 | |||
21 | If no args are given, write to stdout the baud rate and settings that | ||
22 | have been changed from their defaults. Mode reading and changes | ||
23 | are done on the specified device, or stdin if none was specified. | ||
24 | |||
25 | David MacKenzie <djm@gnu.ai.mit.edu> | ||
26 | |||
27 | Special for busybox ported by vodz@usa.net 2001 | ||
28 | |||
29 | */ | ||
30 | |||
31 | #include "busybox.h" | ||
32 | |||
33 | #include <termios.h> | ||
34 | #include <sys/ioctl.h> | ||
35 | #include <getopt.h> | ||
36 | |||
37 | #include <sys/param.h> | ||
38 | #include <unistd.h> | ||
39 | |||
40 | #ifndef STDIN_FILENO | ||
41 | # define STDIN_FILENO 0 | ||
42 | #endif | ||
43 | |||
44 | #ifndef STDOUT_FILENO | ||
45 | # define STDOUT_FILENO 1 | ||
46 | #endif | ||
47 | |||
48 | #include <stdlib.h> | ||
49 | #include <string.h> | ||
50 | #include <assert.h> | ||
51 | #include <ctype.h> | ||
52 | #include <errno.h> | ||
53 | #include <limits.h> | ||
54 | #include <memory.h> | ||
55 | #include <fcntl.h> | ||
56 | |||
57 | #define STREQ(a, b) (strcmp ((a), (b)) == 0) | ||
58 | |||
59 | |||
60 | #ifndef _POSIX_VDISABLE | ||
61 | # define _POSIX_VDISABLE ((unsigned char) 0) | ||
62 | #endif | ||
63 | |||
64 | #define Control(c) ((c) & 0x1f) | ||
65 | /* Canonical values for control characters. */ | ||
66 | #ifndef CINTR | ||
67 | # define CINTR Control ('c') | ||
68 | #endif | ||
69 | #ifndef CQUIT | ||
70 | # define CQUIT 28 | ||
71 | #endif | ||
72 | #ifndef CERASE | ||
73 | # define CERASE 127 | ||
74 | #endif | ||
75 | #ifndef CKILL | ||
76 | # define CKILL Control ('u') | ||
77 | #endif | ||
78 | #ifndef CEOF | ||
79 | # define CEOF Control ('d') | ||
80 | #endif | ||
81 | #ifndef CEOL | ||
82 | # define CEOL _POSIX_VDISABLE | ||
83 | #endif | ||
84 | #ifndef CSTART | ||
85 | # define CSTART Control ('q') | ||
86 | #endif | ||
87 | #ifndef CSTOP | ||
88 | # define CSTOP Control ('s') | ||
89 | #endif | ||
90 | #ifndef CSUSP | ||
91 | # define CSUSP Control ('z') | ||
92 | #endif | ||
93 | #if defined(VEOL2) && !defined(CEOL2) | ||
94 | # define CEOL2 _POSIX_VDISABLE | ||
95 | #endif | ||
96 | /* ISC renamed swtch to susp for termios, but we'll accept either name. */ | ||
97 | #if defined(VSUSP) && !defined(VSWTCH) | ||
98 | # define VSWTCH VSUSP | ||
99 | # define CSWTCH CSUSP | ||
100 | #endif | ||
101 | #if defined(VSWTCH) && !defined(CSWTCH) | ||
102 | # define CSWTCH _POSIX_VDISABLE | ||
103 | #endif | ||
104 | |||
105 | /* SunOS 5.3 loses (^Z doesn't work) if `swtch' is the same as `susp'. | ||
106 | So the default is to disable `swtch.' */ | ||
107 | #if defined (__sparc__) && defined (__svr4__) | ||
108 | # undef CSWTCH | ||
109 | # define CSWTCH _POSIX_VDISABLE | ||
110 | #endif | ||
111 | |||
112 | #if defined(VWERSE) && !defined (VWERASE) /* AIX-3.2.5 */ | ||
113 | # define VWERASE VWERSE | ||
114 | #endif | ||
115 | #if defined(VDSUSP) && !defined (CDSUSP) | ||
116 | # define CDSUSP Control ('y') | ||
117 | #endif | ||
118 | #if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */ | ||
119 | # define VREPRINT VRPRNT | ||
120 | #endif | ||
121 | #if defined(VREPRINT) && !defined(CRPRNT) | ||
122 | # define CRPRNT Control ('r') | ||
123 | #endif | ||
124 | #if defined(VWERASE) && !defined(CWERASE) | ||
125 | # define CWERASE Control ('w') | ||
126 | #endif | ||
127 | #if defined(VLNEXT) && !defined(CLNEXT) | ||
128 | # define CLNEXT Control ('v') | ||
129 | #endif | ||
130 | #if defined(VDISCARD) && !defined(VFLUSHO) | ||
131 | # define VFLUSHO VDISCARD | ||
132 | #endif | ||
133 | #if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */ | ||
134 | # define VFLUSHO VFLUSH | ||
135 | #endif | ||
136 | #if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */ | ||
137 | # define ECHOCTL CTLECH | ||
138 | #endif | ||
139 | #if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */ | ||
140 | # define ECHOCTL TCTLECH | ||
141 | #endif | ||
142 | #if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */ | ||
143 | # define ECHOKE CRTKIL | ||
144 | #endif | ||
145 | #if defined(VFLUSHO) && !defined(CFLUSHO) | ||
146 | # define CFLUSHO Control ('o') | ||
147 | #endif | ||
148 | #if defined(VSTATUS) && !defined(CSTATUS) | ||
149 | # define CSTATUS Control ('t') | ||
150 | #endif | ||
151 | |||
152 | /* Which speeds to set. */ | ||
153 | enum speed_setting { | ||
154 | input_speed, output_speed, both_speeds | ||
155 | }; | ||
156 | |||
157 | /* What to output and how. */ | ||
158 | enum output_type { | ||
159 | changed, all, recoverable /* Default, -a, -g. */ | ||
160 | }; | ||
161 | |||
162 | /* Which member(s) of `struct termios' a mode uses. */ | ||
163 | enum mode_type { | ||
164 | control, input, output, local, combination | ||
165 | }; | ||
166 | |||
167 | |||
168 | static const char evenp[] = "evenp"; | ||
169 | static const char raw[] = "raw"; | ||
170 | static const char stty_min[] = "min"; | ||
171 | static const char stty_time[] = "time"; | ||
172 | static const char stty_swtch[] = "swtch"; | ||
173 | static const char stty_eol[] = "eol"; | ||
174 | static const char stty_eof[] = "eof"; | ||
175 | static const char parity[] = "parity"; | ||
176 | static const char stty_oddp[] = "oddp"; | ||
177 | static const char stty_nl[] = "nl"; | ||
178 | static const char stty_ek[] = "ek"; | ||
179 | static const char stty_sane[] = "sane"; | ||
180 | static const char cbreak[] = "cbreak"; | ||
181 | static const char stty_pass8[] = "pass8"; | ||
182 | static const char litout[] = "litout"; | ||
183 | static const char cooked[] = "cooked"; | ||
184 | static const char decctlq[] = "decctlq"; | ||
185 | static const char stty_tabs[] = "tabs"; | ||
186 | static const char stty_lcase[] = "lcase"; | ||
187 | static const char stty_LCASE[] = "LCASE"; | ||
188 | static const char stty_crt[] = "crt"; | ||
189 | static const char stty_dec[] = "dec"; | ||
190 | |||
191 | |||
192 | /* Flags for `struct mode_info'. */ | ||
193 | #define SANE_SET 1 /* Set in `sane' mode. */ | ||
194 | #define SANE_UNSET 2 /* Unset in `sane' mode. */ | ||
195 | #define REV 4 /* Can be turned off by prepending `-'. */ | ||
196 | #define OMIT 8 /* Don't display value. */ | ||
197 | |||
198 | /* Each mode. */ | ||
199 | struct mode_info { | ||
200 | const char *name; /* Name given on command line. */ | ||
201 | enum mode_type type; /* Which structure element to change. */ | ||
202 | char flags; /* Setting and display options. */ | ||
203 | unsigned long bits; /* Bits to set for this mode. */ | ||
204 | unsigned long mask; /* Other bits to turn off for this mode. */ | ||
205 | }; | ||
206 | |||
207 | static const struct mode_info mode_info[] = { | ||
208 | {"parenb", control, REV, PARENB, 0}, | ||
209 | {"parodd", control, REV, PARODD, 0}, | ||
210 | {"cs5", control, 0, CS5, CSIZE}, | ||
211 | {"cs6", control, 0, CS6, CSIZE}, | ||
212 | {"cs7", control, 0, CS7, CSIZE}, | ||
213 | {"cs8", control, 0, CS8, CSIZE}, | ||
214 | {"hupcl", control, REV, HUPCL, 0}, | ||
215 | {"hup", control, REV | OMIT, HUPCL, 0}, | ||
216 | {"cstopb", control, REV, CSTOPB, 0}, | ||
217 | {"cread", control, SANE_SET | REV, CREAD, 0}, | ||
218 | {"clocal", control, REV, CLOCAL, 0}, | ||
219 | #ifdef CRTSCTS | ||
220 | {"crtscts", control, REV, CRTSCTS, 0}, | ||
221 | #endif | ||
222 | |||
223 | {"ignbrk", input, SANE_UNSET | REV, IGNBRK, 0}, | ||
224 | {"brkint", input, SANE_SET | REV, BRKINT, 0}, | ||
225 | {"ignpar", input, REV, IGNPAR, 0}, | ||
226 | {"parmrk", input, REV, PARMRK, 0}, | ||
227 | {"inpck", input, REV, INPCK, 0}, | ||
228 | {"istrip", input, REV, ISTRIP, 0}, | ||
229 | {"inlcr", input, SANE_UNSET | REV, INLCR, 0}, | ||
230 | {"igncr", input, SANE_UNSET | REV, IGNCR, 0}, | ||
231 | {"icrnl", input, SANE_SET | REV, ICRNL, 0}, | ||
232 | {"ixon", input, REV, IXON, 0}, | ||
233 | {"ixoff", input, SANE_UNSET | REV, IXOFF, 0}, | ||
234 | {"tandem", input, REV | OMIT, IXOFF, 0}, | ||
235 | #ifdef IUCLC | ||
236 | {"iuclc", input, SANE_UNSET | REV, IUCLC, 0}, | ||
237 | #endif | ||
238 | #ifdef IXANY | ||
239 | {"ixany", input, SANE_UNSET | REV, IXANY, 0}, | ||
240 | #endif | ||
241 | #ifdef IMAXBEL | ||
242 | {"imaxbel", input, SANE_SET | REV, IMAXBEL, 0}, | ||
243 | #endif | ||
244 | |||
245 | {"opost", output, SANE_SET | REV, OPOST, 0}, | ||
246 | #ifdef OLCUC | ||
247 | {"olcuc", output, SANE_UNSET | REV, OLCUC, 0}, | ||
248 | #endif | ||
249 | #ifdef OCRNL | ||
250 | {"ocrnl", output, SANE_UNSET | REV, OCRNL, 0}, | ||
251 | #endif | ||
252 | #ifdef ONLCR | ||
253 | {"onlcr", output, SANE_SET | REV, ONLCR, 0}, | ||
254 | #endif | ||
255 | #ifdef ONOCR | ||
256 | {"onocr", output, SANE_UNSET | REV, ONOCR, 0}, | ||
257 | #endif | ||
258 | #ifdef ONLRET | ||
259 | {"onlret", output, SANE_UNSET | REV, ONLRET, 0}, | ||
260 | #endif | ||
261 | #ifdef OFILL | ||
262 | {"ofill", output, SANE_UNSET | REV, OFILL, 0}, | ||
263 | #endif | ||
264 | #ifdef OFDEL | ||
265 | {"ofdel", output, SANE_UNSET | REV, OFDEL, 0}, | ||
266 | #endif | ||
267 | #ifdef NLDLY | ||
268 | {"nl1", output, SANE_UNSET, NL1, NLDLY}, | ||
269 | {"nl0", output, SANE_SET, NL0, NLDLY}, | ||
270 | #endif | ||
271 | #ifdef CRDLY | ||
272 | {"cr3", output, SANE_UNSET, CR3, CRDLY}, | ||
273 | {"cr2", output, SANE_UNSET, CR2, CRDLY}, | ||
274 | {"cr1", output, SANE_UNSET, CR1, CRDLY}, | ||
275 | {"cr0", output, SANE_SET, CR0, CRDLY}, | ||
276 | #endif | ||
277 | #ifdef TABDLY | ||
278 | {"tab3", output, SANE_UNSET, TAB3, TABDLY}, | ||
279 | {"tab2", output, SANE_UNSET, TAB2, TABDLY}, | ||
280 | {"tab1", output, SANE_UNSET, TAB1, TABDLY}, | ||
281 | {"tab0", output, SANE_SET, TAB0, TABDLY}, | ||
282 | #else | ||
283 | # ifdef OXTABS | ||
284 | {"tab3", output, SANE_UNSET, OXTABS, 0}, | ||
285 | # endif | ||
286 | #endif | ||
287 | #ifdef BSDLY | ||
288 | {"bs1", output, SANE_UNSET, BS1, BSDLY}, | ||
289 | {"bs0", output, SANE_SET, BS0, BSDLY}, | ||
290 | #endif | ||
291 | #ifdef VTDLY | ||
292 | {"vt1", output, SANE_UNSET, VT1, VTDLY}, | ||
293 | {"vt0", output, SANE_SET, VT0, VTDLY}, | ||
294 | #endif | ||
295 | #ifdef FFDLY | ||
296 | {"ff1", output, SANE_UNSET, FF1, FFDLY}, | ||
297 | {"ff0", output, SANE_SET, FF0, FFDLY}, | ||
298 | #endif | ||
299 | |||
300 | {"isig", local, SANE_SET | REV, ISIG, 0}, | ||
301 | {"icanon", local, SANE_SET | REV, ICANON, 0}, | ||
302 | #ifdef IEXTEN | ||
303 | {"iexten", local, SANE_SET | REV, IEXTEN, 0}, | ||
304 | #endif | ||
305 | {"echo", local, SANE_SET | REV, ECHO, 0}, | ||
306 | {"echoe", local, SANE_SET | REV, ECHOE, 0}, | ||
307 | {"crterase", local, REV | OMIT, ECHOE, 0}, | ||
308 | {"echok", local, SANE_SET | REV, ECHOK, 0}, | ||
309 | {"echonl", local, SANE_UNSET | REV, ECHONL, 0}, | ||
310 | {"noflsh", local, SANE_UNSET | REV, NOFLSH, 0}, | ||
311 | #ifdef XCASE | ||
312 | {"xcase", local, SANE_UNSET | REV, XCASE, 0}, | ||
313 | #endif | ||
314 | #ifdef TOSTOP | ||
315 | {"tostop", local, SANE_UNSET | REV, TOSTOP, 0}, | ||
316 | #endif | ||
317 | #ifdef ECHOPRT | ||
318 | {"echoprt", local, SANE_UNSET | REV, ECHOPRT, 0}, | ||
319 | {"prterase", local, REV | OMIT, ECHOPRT, 0}, | ||
320 | #endif | ||
321 | #ifdef ECHOCTL | ||
322 | {"echoctl", local, SANE_SET | REV, ECHOCTL, 0}, | ||
323 | {"ctlecho", local, REV | OMIT, ECHOCTL, 0}, | ||
324 | #endif | ||
325 | #ifdef ECHOKE | ||
326 | {"echoke", local, SANE_SET | REV, ECHOKE, 0}, | ||
327 | {"crtkill", local, REV | OMIT, ECHOKE, 0}, | ||
328 | #endif | ||
329 | |||
330 | {evenp, combination, REV | OMIT, 0, 0}, | ||
331 | {parity, combination, REV | OMIT, 0, 0}, | ||
332 | {stty_oddp, combination, REV | OMIT, 0, 0}, | ||
333 | {stty_nl, combination, REV | OMIT, 0, 0}, | ||
334 | {stty_ek, combination, OMIT, 0, 0}, | ||
335 | {stty_sane, combination, OMIT, 0, 0}, | ||
336 | {cooked, combination, REV | OMIT, 0, 0}, | ||
337 | {raw, combination, REV | OMIT, 0, 0}, | ||
338 | {stty_pass8, combination, REV | OMIT, 0, 0}, | ||
339 | {litout, combination, REV | OMIT, 0, 0}, | ||
340 | {cbreak, combination, REV | OMIT, 0, 0}, | ||
341 | #ifdef IXANY | ||
342 | {decctlq, combination, REV | OMIT, 0, 0}, | ||
343 | #endif | ||
344 | #if defined (TABDLY) || defined (OXTABS) | ||
345 | {stty_tabs, combination, REV | OMIT, 0, 0}, | ||
346 | #endif | ||
347 | #if defined(XCASE) && defined(IUCLC) && defined(OLCUC) | ||
348 | {stty_lcase, combination, REV | OMIT, 0, 0}, | ||
349 | {stty_LCASE, combination, REV | OMIT, 0, 0}, | ||
350 | #endif | ||
351 | {stty_crt, combination, OMIT, 0, 0}, | ||
352 | {stty_dec, combination, OMIT, 0, 0}, | ||
353 | }; | ||
354 | |||
355 | static const int NUM_mode_info = | ||
356 | |||
357 | (sizeof(mode_info) / sizeof(struct mode_info)); | ||
358 | |||
359 | /* Control character settings. */ | ||
360 | struct control_info { | ||
361 | const char *name; /* Name given on command line. */ | ||
362 | unsigned char saneval; /* Value to set for `stty sane'. */ | ||
363 | int offset; /* Offset in c_cc. */ | ||
364 | }; | ||
365 | |||
366 | /* Control characters. */ | ||
367 | |||
368 | static const struct control_info control_info[] = { | ||
369 | {"intr", CINTR, VINTR}, | ||
370 | {"quit", CQUIT, VQUIT}, | ||
371 | {"erase", CERASE, VERASE}, | ||
372 | {"kill", CKILL, VKILL}, | ||
373 | {stty_eof, CEOF, VEOF}, | ||
374 | {stty_eol, CEOL, VEOL}, | ||
375 | #ifdef VEOL2 | ||
376 | {"eol2", CEOL2, VEOL2}, | ||
377 | #endif | ||
378 | #ifdef VSWTCH | ||
379 | {stty_swtch, CSWTCH, VSWTCH}, | ||
380 | #endif | ||
381 | {"start", CSTART, VSTART}, | ||
382 | {"stop", CSTOP, VSTOP}, | ||
383 | {"susp", CSUSP, VSUSP}, | ||
384 | #ifdef VDSUSP | ||
385 | {"dsusp", CDSUSP, VDSUSP}, | ||
386 | #endif | ||
387 | #ifdef VREPRINT | ||
388 | {"rprnt", CRPRNT, VREPRINT}, | ||
389 | #endif | ||
390 | #ifdef VWERASE | ||
391 | {"werase", CWERASE, VWERASE}, | ||
392 | #endif | ||
393 | #ifdef VLNEXT | ||
394 | {"lnext", CLNEXT, VLNEXT}, | ||
395 | #endif | ||
396 | #ifdef VFLUSHO | ||
397 | {"flush", CFLUSHO, VFLUSHO}, | ||
398 | #endif | ||
399 | #ifdef VSTATUS | ||
400 | {"status", CSTATUS, VSTATUS}, | ||
401 | #endif | ||
402 | |||
403 | /* These must be last because of the display routines. */ | ||
404 | {stty_min, 1, VMIN}, | ||
405 | {stty_time, 0, VTIME}, | ||
406 | }; | ||
407 | |||
408 | static const int NUM_control_info = | ||
409 | (sizeof(control_info) / sizeof(struct control_info)); | ||
410 | |||
411 | |||
412 | static const char *visible(unsigned int ch); | ||
413 | static unsigned long baud_to_value(speed_t speed); | ||
414 | static int recover_mode(char *arg, struct termios *mode); | ||
415 | static int screen_columns(void); | ||
416 | static int set_mode(const struct mode_info *info, | ||
417 | int reversed, struct termios *mode); | ||
418 | static speed_t string_to_baud(const char *arg); | ||
419 | static tcflag_t *mode_type_flag(enum mode_type type, struct termios *mode); | ||
420 | static void display_all(struct termios *mode, int fd, | ||
421 | const char *device_name); | ||
422 | static void display_changed(struct termios *mode); | ||
423 | static void display_recoverable(struct termios *mode); | ||
424 | static void display_settings(enum output_type output_type, | ||
425 | struct termios *mode, int fd, | ||
426 | const char *device_name); | ||
427 | static void display_speed(struct termios *mode, int fancy); | ||
428 | static void display_window_size(int fancy, int fd, | ||
429 | const char *device_name); | ||
430 | static void sane_mode(struct termios *mode); | ||
431 | static void set_control_char(const struct control_info *info, | ||
432 | const char *arg, struct termios *mode); | ||
433 | static void set_speed(enum speed_setting type, | ||
434 | const char *arg, struct termios *mode); | ||
435 | static void set_window_size(int rows, int cols, int fd, | ||
436 | |||
437 | const char *device_name); | ||
438 | |||
439 | /* The width of the screen, for output wrapping. */ | ||
440 | static int max_col; | ||
441 | |||
442 | /* Current position, to know when to wrap. */ | ||
443 | static int current_col; | ||
444 | |||
445 | /* Print format string MESSAGE and optional args. | ||
446 | Wrap to next line first if it won't fit. | ||
447 | Print a space first unless MESSAGE will start a new line. */ | ||
448 | |||
449 | static void wrapf(const char *message, ...) | ||
450 | { | ||
451 | va_list args; | ||
452 | char buf[1024]; /* Plenty long for our needs. */ | ||
453 | int buflen; | ||
454 | |||
455 | va_start(args, message); | ||
456 | vsprintf(buf, message, args); | ||
457 | va_end(args); | ||
458 | buflen = strlen(buf); | ||
459 | if (current_col + (current_col > 0) + buflen >= max_col) { | ||
460 | putchar('\n'); | ||
461 | current_col = 0; | ||
462 | } | ||
463 | if (current_col > 0) { | ||
464 | putchar(' '); | ||
465 | current_col++; | ||
466 | } | ||
467 | fputs(buf, stdout); | ||
468 | current_col += buflen; | ||
469 | } | ||
470 | |||
471 | static const struct suffix_mult stty_suffixes[] = { | ||
472 | {"b", 512}, | ||
473 | {"k", 1024}, | ||
474 | {"B", 1024}, | ||
475 | {NULL, 0} | ||
476 | }; | ||
477 | |||
478 | extern int stty_main(int argc, char **argv) | ||
479 | { | ||
480 | struct termios mode; | ||
481 | enum output_type output_type; | ||
482 | int optc; | ||
483 | int require_set_attr; | ||
484 | int speed_was_set; | ||
485 | int verbose_output; | ||
486 | int recoverable_output; | ||
487 | int k; | ||
488 | int noargs = 1; | ||
489 | char *file_name = NULL; | ||
490 | int fd; | ||
491 | const char *device_name; | ||
492 | |||
493 | output_type = changed; | ||
494 | verbose_output = 0; | ||
495 | recoverable_output = 0; | ||
496 | |||
497 | /* Don't print error messages for unrecognized options. */ | ||
498 | opterr = 0; | ||
499 | |||
500 | while ((optc = getopt(argc, argv, "agF:")) != -1) { | ||
501 | switch (optc) { | ||
502 | case 'a': | ||
503 | verbose_output = 1; | ||
504 | output_type = all; | ||
505 | break; | ||
506 | |||
507 | case 'g': | ||
508 | recoverable_output = 1; | ||
509 | output_type = recoverable; | ||
510 | break; | ||
511 | |||
512 | case 'F': | ||
513 | if (file_name) | ||
514 | error_msg_and_die("only one device may be specified"); | ||
515 | file_name = optarg; | ||
516 | break; | ||
517 | |||
518 | default: /* unrecognized option */ | ||
519 | noargs = 0; | ||
520 | break; | ||
521 | } | ||
522 | |||
523 | if (noargs == 0) | ||
524 | break; | ||
525 | } | ||
526 | |||
527 | if (optind < argc) | ||
528 | noargs = 0; | ||
529 | |||
530 | /* Specifying both -a and -g gets an error. */ | ||
531 | if (verbose_output && recoverable_output) | ||
532 | error_msg_and_die ("verbose and stty-readable output styles are mutually exclusive"); | ||
533 | |||
534 | /* Specifying any other arguments with -a or -g gets an error. */ | ||
535 | if (!noargs && (verbose_output || recoverable_output)) | ||
536 | error_msg_and_die ("modes may not be set when specifying an output style"); | ||
537 | |||
538 | /* FIXME: it'd be better not to open the file until we've verified | ||
539 | that all arguments are valid. Otherwise, we could end up doing | ||
540 | only some of the requested operations and then failing, probably | ||
541 | leaving things in an undesirable state. */ | ||
542 | |||
543 | if (file_name) { | ||
544 | int fdflags; | ||
545 | |||
546 | device_name = file_name; | ||
547 | fd = open(device_name, O_RDONLY | O_NONBLOCK); | ||
548 | if (fd < 0) | ||
549 | perror_msg_and_die("%s", device_name); | ||
550 | if ((fdflags = fcntl(fd, F_GETFL)) == -1 | ||
551 | || fcntl(fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) | ||
552 | perror_msg_and_die("%s: couldn't reset non-blocking mode", | ||
553 | device_name); | ||
554 | } else { | ||
555 | fd = 0; | ||
556 | device_name = "standard input"; | ||
557 | } | ||
558 | |||
559 | /* Initialize to all zeroes so there is no risk memcmp will report a | ||
560 | spurious difference in an uninitialized portion of the structure. */ | ||
561 | memset(&mode, 0, sizeof(mode)); | ||
562 | if (tcgetattr(fd, &mode)) | ||
563 | perror_msg_and_die("%s", device_name); | ||
564 | |||
565 | if (verbose_output || recoverable_output || noargs) { | ||
566 | max_col = screen_columns(); | ||
567 | current_col = 0; | ||
568 | display_settings(output_type, &mode, fd, device_name); | ||
569 | return EXIT_SUCCESS; | ||
570 | } | ||
571 | |||
572 | speed_was_set = 0; | ||
573 | require_set_attr = 0; | ||
574 | k = optind; | ||
575 | while (k < argc) { | ||
576 | int match_found = 0; | ||
577 | int reversed = 0; | ||
578 | int i; | ||
579 | |||
580 | if (argv[k][0] == '-') { | ||
581 | ++argv[k]; | ||
582 | reversed = 1; | ||
583 | } | ||
584 | for (i = 0; i < NUM_mode_info; ++i) { | ||
585 | if (STREQ(argv[k], mode_info[i].name)) { | ||
586 | match_found = set_mode(&mode_info[i], reversed, &mode); | ||
587 | require_set_attr = 1; | ||
588 | break; | ||
589 | } | ||
590 | } | ||
591 | if (match_found == 0 && reversed) { | ||
592 | error_msg_and_die("invalid argument `%s'", --argv[k]); | ||
593 | } | ||
594 | if (match_found == 0) { | ||
595 | for (i = 0; i < NUM_control_info; ++i) { | ||
596 | if (STREQ(argv[k], control_info[i].name)) { | ||
597 | if (k == argc - 1) { | ||
598 | error_msg_and_die("missing argument to `%s'", argv[k]); | ||
599 | } | ||
600 | match_found = 1; | ||
601 | ++k; | ||
602 | set_control_char(&control_info[i], argv[k], &mode); | ||
603 | require_set_attr = 1; | ||
604 | break; | ||
605 | } | ||
606 | } | ||
607 | } | ||
608 | if (match_found == 0) { | ||
609 | if (STREQ(argv[k], "ispeed")) { | ||
610 | if (k == argc - 1) { | ||
611 | error_msg_and_die("missing argument to `%s'", argv[k]); | ||
612 | } | ||
613 | ++k; | ||
614 | set_speed(input_speed, argv[k], &mode); | ||
615 | speed_was_set = 1; | ||
616 | require_set_attr = 1; | ||
617 | } else if (STREQ(argv[k], "ospeed")) { | ||
618 | if (k == argc - 1) { | ||
619 | error_msg_and_die("missing argument to `%s'", argv[k]); | ||
620 | } | ||
621 | ++k; | ||
622 | set_speed(output_speed, argv[k], &mode); | ||
623 | speed_was_set = 1; | ||
624 | require_set_attr = 1; | ||
625 | } | ||
626 | #ifdef TIOCGWINSZ | ||
627 | else if (STREQ(argv[k], "rows")) { | ||
628 | if (k == argc - 1) { | ||
629 | error_msg_and_die("missing argument to `%s'", argv[k]); | ||
630 | } | ||
631 | ++k; | ||
632 | set_window_size((int) parse_number(argv[k], stty_suffixes), | ||
633 | -1, fd, device_name); | ||
634 | } else if (STREQ(argv[k], "cols") || STREQ(argv[k], "columns")) { | ||
635 | if (k == argc - 1) { | ||
636 | error_msg_and_die("missing argument to `%s'", argv[k]); | ||
637 | } | ||
638 | ++k; | ||
639 | set_window_size(-1, | ||
640 | (int) parse_number(argv[k], stty_suffixes), | ||
641 | fd, device_name); | ||
642 | } else if (STREQ(argv[k], "size")) { | ||
643 | max_col = screen_columns(); | ||
644 | current_col = 0; | ||
645 | display_window_size(0, fd, device_name); | ||
646 | } | ||
647 | #endif | ||
648 | #ifdef HAVE_C_LINE | ||
649 | else if (STREQ(argv[k], "line")) { | ||
650 | if (k == argc - 1) { | ||
651 | error_msg_and_die("missing argument to `%s'", argv[k]); | ||
652 | } | ||
653 | ++k; | ||
654 | mode.c_line = parse_number(argv[k], stty_suffixes); | ||
655 | require_set_attr = 1; | ||
656 | } | ||
657 | #endif | ||
658 | else if (STREQ(argv[k], "speed")) { | ||
659 | max_col = screen_columns(); | ||
660 | display_speed(&mode, 0); | ||
661 | } else if (string_to_baud(argv[k]) != (speed_t) - 1) { | ||
662 | set_speed(both_speeds, argv[k], &mode); | ||
663 | speed_was_set = 1; | ||
664 | require_set_attr = 1; | ||
665 | } else { | ||
666 | if (recover_mode(argv[k], &mode) == 0) { | ||
667 | error_msg_and_die("invalid argument `%s'", argv[k]); | ||
668 | } | ||
669 | require_set_attr = 1; | ||
670 | } | ||
671 | } | ||
672 | k++; | ||
673 | } | ||
674 | |||
675 | if (require_set_attr) { | ||
676 | struct termios new_mode; | ||
677 | |||
678 | if (tcsetattr(fd, TCSADRAIN, &mode)) | ||
679 | perror_msg_and_die("%s", device_name); | ||
680 | |||
681 | /* POSIX (according to Zlotnick's book) tcsetattr returns zero if | ||
682 | it performs *any* of the requested operations. This means it | ||
683 | can report `success' when it has actually failed to perform | ||
684 | some proper subset of the requested operations. To detect | ||
685 | this partial failure, get the current terminal attributes and | ||
686 | compare them to the requested ones. */ | ||
687 | |||
688 | /* Initialize to all zeroes so there is no risk memcmp will report a | ||
689 | spurious difference in an uninitialized portion of the structure. */ | ||
690 | memset(&new_mode, 0, sizeof(new_mode)); | ||
691 | if (tcgetattr(fd, &new_mode)) | ||
692 | perror_msg_and_die("%s", device_name); | ||
693 | |||
694 | /* Normally, one shouldn't use memcmp to compare structures that | ||
695 | may have `holes' containing uninitialized data, but we have been | ||
696 | careful to initialize the storage of these two variables to all | ||
697 | zeroes. One might think it more efficient simply to compare the | ||
698 | modified fields, but that would require enumerating those fields -- | ||
699 | and not all systems have the same fields in this structure. */ | ||
700 | |||
701 | if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) { | ||
702 | #ifdef CIBAUD | ||
703 | /* SunOS 4.1.3 (at least) has the problem that after this sequence, | ||
704 | tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2); | ||
705 | sometimes (m1 != m2). The only difference is in the four bits | ||
706 | of the c_cflag field corresponding to the baud rate. To save | ||
707 | Sun users a little confusion, don't report an error if this | ||
708 | happens. But suppress the error only if we haven't tried to | ||
709 | set the baud rate explicitly -- otherwise we'd never give an | ||
710 | error for a true failure to set the baud rate. */ | ||
711 | |||
712 | new_mode.c_cflag &= (~CIBAUD); | ||
713 | if (speed_was_set || memcmp(&mode, &new_mode, sizeof(mode)) != 0) | ||
714 | #endif | ||
715 | { | ||
716 | error_msg_and_die ("%s: unable to perform all requested operations", | ||
717 | device_name); | ||
718 | #ifdef TESTING | ||
719 | { | ||
720 | size_t i; | ||
721 | |||
722 | printf("new_mode: mode\n"); | ||
723 | for (i = 0; i < sizeof(new_mode); i++) | ||
724 | printf("0x%02x: 0x%02x\n", | ||
725 | *(((unsigned char *) &new_mode) + i), | ||
726 | *(((unsigned char *) &mode) + i)); | ||
727 | } | ||
728 | #endif | ||
729 | } | ||
730 | } | ||
731 | } | ||
732 | |||
733 | return EXIT_SUCCESS; | ||
734 | } | ||
735 | |||
736 | /* Return 0 if not applied because not reversible; otherwise return 1. */ | ||
737 | |||
738 | static int | ||
739 | set_mode(const struct mode_info *info, int reversed, struct termios *mode) | ||
740 | { | ||
741 | tcflag_t *bitsp; | ||
742 | |||
743 | if (reversed && (info->flags & REV) == 0) | ||
744 | return 0; | ||
745 | |||
746 | bitsp = mode_type_flag(info->type, mode); | ||
747 | |||
748 | if (bitsp == NULL) { | ||
749 | /* Combination mode. */ | ||
750 | if (info->name == evenp || info->name == parity) { | ||
751 | if (reversed) | ||
752 | mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; | ||
753 | else | ||
754 | mode->c_cflag = | ||
755 | (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7; | ||
756 | } else if (info->name == stty_oddp) { | ||
757 | if (reversed) | ||
758 | mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; | ||
759 | else | ||
760 | mode->c_cflag = | ||
761 | (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB; | ||
762 | } else if (info->name == stty_nl) { | ||
763 | if (reversed) { | ||
764 | mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR; | ||
765 | mode->c_oflag = (mode->c_oflag | ||
766 | #ifdef ONLCR | ||
767 | | ONLCR | ||
768 | #endif | ||
769 | ) | ||
770 | #ifdef OCRNL | ||
771 | & ~OCRNL | ||
772 | #endif | ||
773 | #ifdef ONLRET | ||
774 | & ~ONLRET | ||
775 | #endif | ||
776 | ; | ||
777 | } else { | ||
778 | mode->c_iflag = mode->c_iflag & ~ICRNL; | ||
779 | #ifdef ONLCR | ||
780 | mode->c_oflag = mode->c_oflag & ~ONLCR; | ||
781 | #endif | ||
782 | } | ||
783 | } else if (info->name == stty_ek) { | ||
784 | mode->c_cc[VERASE] = CERASE; | ||
785 | mode->c_cc[VKILL] = CKILL; | ||
786 | } else if (info->name == stty_sane) | ||
787 | sane_mode(mode); | ||
788 | else if (info->name == cbreak) { | ||
789 | if (reversed) | ||
790 | mode->c_lflag |= ICANON; | ||
791 | else | ||
792 | mode->c_lflag &= ~ICANON; | ||
793 | } else if (info->name == stty_pass8) { | ||
794 | if (reversed) { | ||
795 | mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB; | ||
796 | mode->c_iflag |= ISTRIP; | ||
797 | } else { | ||
798 | mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; | ||
799 | mode->c_iflag &= ~ISTRIP; | ||
800 | } | ||
801 | } else if (info->name == litout) { | ||
802 | if (reversed) { | ||
803 | mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB; | ||
804 | mode->c_iflag |= ISTRIP; | ||
805 | mode->c_oflag |= OPOST; | ||
806 | } else { | ||
807 | mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; | ||
808 | mode->c_iflag &= ~ISTRIP; | ||
809 | mode->c_oflag &= ~OPOST; | ||
810 | } | ||
811 | } else if (info->name == raw || info->name == cooked) { | ||
812 | if ((info->name[0] == 'r' && reversed) | ||
813 | || (info->name[0] == 'c' && !reversed)) { | ||
814 | /* Cooked mode. */ | ||
815 | mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON; | ||
816 | mode->c_oflag |= OPOST; | ||
817 | mode->c_lflag |= ISIG | ICANON; | ||
818 | #if VMIN == VEOF | ||
819 | mode->c_cc[VEOF] = CEOF; | ||
820 | #endif | ||
821 | #if VTIME == VEOL | ||
822 | mode->c_cc[VEOL] = CEOL; | ||
823 | #endif | ||
824 | } else { | ||
825 | /* Raw mode. */ | ||
826 | mode->c_iflag = 0; | ||
827 | mode->c_oflag &= ~OPOST; | ||
828 | mode->c_lflag &= ~(ISIG | ICANON | ||
829 | #ifdef XCASE | ||
830 | | XCASE | ||
831 | #endif | ||
832 | ); | ||
833 | mode->c_cc[VMIN] = 1; | ||
834 | mode->c_cc[VTIME] = 0; | ||
835 | } | ||
836 | } | ||
837 | #ifdef IXANY | ||
838 | else if (info->name == decctlq) { | ||
839 | if (reversed) | ||
840 | mode->c_iflag |= IXANY; | ||
841 | else | ||
842 | mode->c_iflag &= ~IXANY; | ||
843 | } | ||
844 | #endif | ||
845 | #ifdef TABDLY | ||
846 | else if (info->name == stty_tabs) { | ||
847 | if (reversed) | ||
848 | mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3; | ||
849 | else | ||
850 | mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0; | ||
851 | } | ||
852 | #else | ||
853 | # ifdef OXTABS | ||
854 | else if (info->name == stty_tabs) { | ||
855 | if (reversed) | ||
856 | mode->c_oflag = mode->c_oflag | OXTABS; | ||
857 | else | ||
858 | mode->c_oflag = mode->c_oflag & ~OXTABS; | ||
859 | } | ||
860 | # endif | ||
861 | #endif | ||
862 | #if defined(XCASE) && defined(IUCLC) && defined(OLCUC) | ||
863 | else if (info->name == stty_lcase || info->name == stty_LCASE) { | ||
864 | if (reversed) { | ||
865 | mode->c_lflag &= ~XCASE; | ||
866 | mode->c_iflag &= ~IUCLC; | ||
867 | mode->c_oflag &= ~OLCUC; | ||
868 | } else { | ||
869 | mode->c_lflag |= XCASE; | ||
870 | mode->c_iflag |= IUCLC; | ||
871 | mode->c_oflag |= OLCUC; | ||
872 | } | ||
873 | } | ||
874 | #endif | ||
875 | else if (info->name == stty_crt) | ||
876 | mode->c_lflag |= ECHOE | ||
877 | #ifdef ECHOCTL | ||
878 | | ECHOCTL | ||
879 | #endif | ||
880 | #ifdef ECHOKE | ||
881 | | ECHOKE | ||
882 | #endif | ||
883 | ; | ||
884 | else if (info->name == stty_dec) { | ||
885 | mode->c_cc[VINTR] = 3; /* ^C */ | ||
886 | mode->c_cc[VERASE] = 127; /* DEL */ | ||
887 | mode->c_cc[VKILL] = 21; /* ^U */ | ||
888 | mode->c_lflag |= ECHOE | ||
889 | #ifdef ECHOCTL | ||
890 | | ECHOCTL | ||
891 | #endif | ||
892 | #ifdef ECHOKE | ||
893 | | ECHOKE | ||
894 | #endif | ||
895 | ; | ||
896 | #ifdef IXANY | ||
897 | mode->c_iflag &= ~IXANY; | ||
898 | #endif | ||
899 | } | ||
900 | } else if (reversed) | ||
901 | *bitsp = *bitsp & ~info->mask & ~info->bits; | ||
902 | else | ||
903 | *bitsp = (*bitsp & ~info->mask) | info->bits; | ||
904 | |||
905 | return 1; | ||
906 | } | ||
907 | |||
908 | static void | ||
909 | set_control_char(const struct control_info *info, const char *arg, | ||
910 | struct termios *mode) | ||
911 | { | ||
912 | unsigned char value; | ||
913 | |||
914 | if (info->name == stty_min || info->name == stty_time) | ||
915 | value = parse_number(arg, stty_suffixes); | ||
916 | else if (arg[0] == '\0' || arg[1] == '\0') | ||
917 | value = arg[0]; | ||
918 | else if (STREQ(arg, "^-") || STREQ(arg, "undef")) | ||
919 | value = _POSIX_VDISABLE; | ||
920 | else if (arg[0] == '^' && arg[1] != '\0') { /* Ignore any trailing junk. */ | ||
921 | if (arg[1] == '?') | ||
922 | value = 127; | ||
923 | else | ||
924 | value = arg[1] & ~0140; /* Non-letters get weird results. */ | ||
925 | } else | ||
926 | value = parse_number(arg, stty_suffixes); | ||
927 | mode->c_cc[info->offset] = value; | ||
928 | } | ||
929 | |||
930 | static void | ||
931 | set_speed(enum speed_setting type, const char *arg, struct termios *mode) | ||
932 | { | ||
933 | speed_t baud; | ||
934 | |||
935 | baud = string_to_baud(arg); | ||
936 | if (type == input_speed || type == both_speeds) | ||
937 | cfsetispeed(mode, baud); | ||
938 | if (type == output_speed || type == both_speeds) | ||
939 | cfsetospeed(mode, baud); | ||
940 | } | ||
941 | |||
942 | #ifdef TIOCGWINSZ | ||
943 | |||
944 | static int get_win_size(int fd, struct winsize *win) | ||
945 | { | ||
946 | int err = ioctl(fd, TIOCGWINSZ, (char *) win); | ||
947 | |||
948 | return err; | ||
949 | } | ||
950 | |||
951 | static void | ||
952 | set_window_size(int rows, int cols, int fd, const char *device_name) | ||
953 | { | ||
954 | struct winsize win; | ||
955 | |||
956 | if (get_win_size(fd, &win)) { | ||
957 | if (errno != EINVAL) | ||
958 | perror_msg_and_die("%s", device_name); | ||
959 | memset(&win, 0, sizeof(win)); | ||
960 | } | ||
961 | |||
962 | if (rows >= 0) | ||
963 | win.ws_row = rows; | ||
964 | if (cols >= 0) | ||
965 | win.ws_col = cols; | ||
966 | |||
967 | # ifdef TIOCSSIZE | ||
968 | /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote: | ||
969 | The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel. | ||
970 | This comment from sys/ttold.h describes Sun's twisted logic - a better | ||
971 | test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0). | ||
972 | At any rate, the problem is gone in Solaris 2.x. */ | ||
973 | |||
974 | if (win.ws_row == 0 || win.ws_col == 0) { | ||
975 | struct ttysize ttysz; | ||
976 | |||
977 | ttysz.ts_lines = win.ws_row; | ||
978 | ttysz.ts_cols = win.ws_col; | ||
979 | |||
980 | win.ws_row = 1; | ||
981 | win.ws_col = 1; | ||
982 | |||
983 | if (ioctl(fd, TIOCSWINSZ, (char *) &win)) | ||
984 | perror_msg_and_die("%s", device_name); | ||
985 | |||
986 | if (ioctl(fd, TIOCSSIZE, (char *) &ttysz)) | ||
987 | perror_msg_and_die("%s", device_name); | ||
988 | return; | ||
989 | } | ||
990 | # endif | ||
991 | |||
992 | if (ioctl(fd, TIOCSWINSZ, (char *) &win)) | ||
993 | perror_msg_and_die("%s", device_name); | ||
994 | } | ||
995 | |||
996 | static void display_window_size(int fancy, int fd, const char *device_name) | ||
997 | { | ||
998 | struct winsize win; | ||
999 | |||
1000 | if (get_win_size(fd, &win)) { | ||
1001 | if (errno != EINVAL) | ||
1002 | perror_msg_and_die("%s", device_name); | ||
1003 | if (!fancy) | ||
1004 | perror_msg_and_die("%s: no size information for this device", | ||
1005 | device_name); | ||
1006 | } else { | ||
1007 | wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n", | ||
1008 | win.ws_row, win.ws_col); | ||
1009 | if (!fancy) | ||
1010 | current_col = 0; | ||
1011 | } | ||
1012 | } | ||
1013 | #endif | ||
1014 | |||
1015 | static int screen_columns(void) | ||
1016 | { | ||
1017 | #ifdef TIOCGWINSZ | ||
1018 | struct winsize win; | ||
1019 | |||
1020 | /* With Solaris 2.[123], this ioctl fails and errno is set to | ||
1021 | EINVAL for telnet (but not rlogin) sessions. | ||
1022 | On ISC 3.0, it fails for the console and the serial port | ||
1023 | (but it works for ptys). | ||
1024 | It can also fail on any system when stdout isn't a tty. | ||
1025 | In case of any failure, just use the default. */ | ||
1026 | if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0) | ||
1027 | return win.ws_col; | ||
1028 | #endif | ||
1029 | |||
1030 | if (getenv("COLUMNS")) | ||
1031 | return atoi(getenv("COLUMNS")); | ||
1032 | return 80; | ||
1033 | } | ||
1034 | |||
1035 | static tcflag_t *mode_type_flag(enum mode_type type, struct termios *mode) | ||
1036 | { | ||
1037 | switch (type) { | ||
1038 | case control: | ||
1039 | return &mode->c_cflag; | ||
1040 | |||
1041 | case input: | ||
1042 | return &mode->c_iflag; | ||
1043 | |||
1044 | case output: | ||
1045 | return &mode->c_oflag; | ||
1046 | |||
1047 | case local: | ||
1048 | return &mode->c_lflag; | ||
1049 | |||
1050 | default: /* combination: */ | ||
1051 | return NULL; | ||
1052 | } | ||
1053 | } | ||
1054 | |||
1055 | static void | ||
1056 | display_settings(enum output_type output_type, struct termios *mode, | ||
1057 | int fd, const char *device_name) | ||
1058 | { | ||
1059 | switch (output_type) { | ||
1060 | case changed: | ||
1061 | display_changed(mode); | ||
1062 | break; | ||
1063 | |||
1064 | case all: | ||
1065 | display_all(mode, fd, device_name); | ||
1066 | break; | ||
1067 | |||
1068 | case recoverable: | ||
1069 | display_recoverable(mode); | ||
1070 | break; | ||
1071 | } | ||
1072 | } | ||
1073 | |||
1074 | static void display_changed(struct termios *mode) | ||
1075 | { | ||
1076 | int i; | ||
1077 | int empty_line; | ||
1078 | tcflag_t *bitsp; | ||
1079 | unsigned long mask; | ||
1080 | enum mode_type prev_type = control; | ||
1081 | |||
1082 | display_speed(mode, 1); | ||
1083 | #ifdef HAVE_C_LINE | ||
1084 | wrapf("line = %d;", mode->c_line); | ||
1085 | #endif | ||
1086 | putchar('\n'); | ||
1087 | current_col = 0; | ||
1088 | |||
1089 | empty_line = 1; | ||
1090 | for (i = 0; control_info[i].name != stty_min; ++i) { | ||
1091 | if (mode->c_cc[control_info[i].offset] == control_info[i].saneval) | ||
1092 | continue; | ||
1093 | /* If swtch is the same as susp, don't print both. */ | ||
1094 | #if VSWTCH == VSUSP | ||
1095 | if (control_info[i].name == stty_swtch) | ||
1096 | continue; | ||
1097 | #endif | ||
1098 | /* If eof uses the same slot as min, only print whichever applies. */ | ||
1099 | #if VEOF == VMIN | ||
1100 | if ((mode->c_lflag & ICANON) == 0 | ||
1101 | && (control_info[i].name == stty_eof | ||
1102 | || control_info[i].name == stty_eol)) continue; | ||
1103 | #endif | ||
1104 | |||
1105 | empty_line = 0; | ||
1106 | wrapf("%s = %s;", control_info[i].name, | ||
1107 | visible(mode->c_cc[control_info[i].offset])); | ||
1108 | } | ||
1109 | if ((mode->c_lflag & ICANON) == 0) { | ||
1110 | wrapf("min = %d; time = %d;\n", (int) mode->c_cc[VMIN], | ||
1111 | (int) mode->c_cc[VTIME]); | ||
1112 | } else if (empty_line == 0) | ||
1113 | putchar('\n'); | ||
1114 | current_col = 0; | ||
1115 | |||
1116 | empty_line = 1; | ||
1117 | for (i = 0; i < NUM_mode_info; ++i) { | ||
1118 | if (mode_info[i].flags & OMIT) | ||
1119 | continue; | ||
1120 | if (mode_info[i].type != prev_type) { | ||
1121 | if (empty_line == 0) { | ||
1122 | putchar('\n'); | ||
1123 | current_col = 0; | ||
1124 | empty_line = 1; | ||
1125 | } | ||
1126 | prev_type = mode_info[i].type; | ||
1127 | } | ||
1128 | |||
1129 | bitsp = mode_type_flag(mode_info[i].type, mode); | ||
1130 | mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits; | ||
1131 | if ((*bitsp & mask) == mode_info[i].bits) { | ||
1132 | if (mode_info[i].flags & SANE_UNSET) { | ||
1133 | wrapf("%s", mode_info[i].name); | ||
1134 | empty_line = 0; | ||
1135 | } | ||
1136 | } | ||
1137 | else if ((mode_info[i].flags & (SANE_SET | REV)) == | ||
1138 | (SANE_SET | REV)) { | ||
1139 | wrapf("-%s", mode_info[i].name); | ||
1140 | empty_line = 0; | ||
1141 | } | ||
1142 | } | ||
1143 | if (empty_line == 0) | ||
1144 | putchar('\n'); | ||
1145 | current_col = 0; | ||
1146 | } | ||
1147 | |||
1148 | static void | ||
1149 | display_all(struct termios *mode, int fd, const char *device_name) | ||
1150 | { | ||
1151 | int i; | ||
1152 | tcflag_t *bitsp; | ||
1153 | unsigned long mask; | ||
1154 | enum mode_type prev_type = control; | ||
1155 | |||
1156 | display_speed(mode, 1); | ||
1157 | #ifdef TIOCGWINSZ | ||
1158 | display_window_size(1, fd, device_name); | ||
1159 | #endif | ||
1160 | #ifdef HAVE_C_LINE | ||
1161 | wrapf("line = %d;", mode->c_line); | ||
1162 | #endif | ||
1163 | putchar('\n'); | ||
1164 | current_col = 0; | ||
1165 | |||
1166 | for (i = 0; control_info[i].name != stty_min; ++i) { | ||
1167 | /* If swtch is the same as susp, don't print both. */ | ||
1168 | #if VSWTCH == VSUSP | ||
1169 | if (control_info[i].name == stty_swtch) | ||
1170 | continue; | ||
1171 | #endif | ||
1172 | /* If eof uses the same slot as min, only print whichever applies. */ | ||
1173 | #if VEOF == VMIN | ||
1174 | if ((mode->c_lflag & ICANON) == 0 | ||
1175 | && (control_info[i].name == stty_eof | ||
1176 | || control_info[i].name == stty_eol)) continue; | ||
1177 | #endif | ||
1178 | wrapf("%s = %s;", control_info[i].name, | ||
1179 | visible(mode->c_cc[control_info[i].offset])); | ||
1180 | } | ||
1181 | #if VEOF == VMIN | ||
1182 | if ((mode->c_lflag & ICANON) == 0) | ||
1183 | #endif | ||
1184 | wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]); | ||
1185 | if (current_col != 0) | ||
1186 | putchar('\n'); | ||
1187 | current_col = 0; | ||
1188 | |||
1189 | for (i = 0; i < NUM_mode_info; ++i) { | ||
1190 | if (mode_info[i].flags & OMIT) | ||
1191 | continue; | ||
1192 | if (mode_info[i].type != prev_type) { | ||
1193 | putchar('\n'); | ||
1194 | current_col = 0; | ||
1195 | prev_type = mode_info[i].type; | ||
1196 | } | ||
1197 | |||
1198 | bitsp = mode_type_flag(mode_info[i].type, mode); | ||
1199 | mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits; | ||
1200 | if ((*bitsp & mask) == mode_info[i].bits) | ||
1201 | wrapf("%s", mode_info[i].name); | ||
1202 | else if (mode_info[i].flags & REV) | ||
1203 | wrapf("-%s", mode_info[i].name); | ||
1204 | } | ||
1205 | putchar('\n'); | ||
1206 | current_col = 0; | ||
1207 | } | ||
1208 | |||
1209 | static void display_speed(struct termios *mode, int fancy) | ||
1210 | { | ||
1211 | if (cfgetispeed(mode) == 0 || cfgetispeed(mode) == cfgetospeed(mode)) | ||
1212 | wrapf(fancy ? "speed %lu baud;" : "%lu\n", | ||
1213 | baud_to_value(cfgetospeed(mode))); | ||
1214 | else | ||
1215 | wrapf(fancy ? "ispeed %lu baud; ospeed %lu baud;" : "%lu %lu\n", | ||
1216 | baud_to_value(cfgetispeed(mode)), | ||
1217 | baud_to_value(cfgetospeed(mode))); | ||
1218 | if (!fancy) | ||
1219 | current_col = 0; | ||
1220 | } | ||
1221 | |||
1222 | static void display_recoverable(struct termios *mode) | ||
1223 | { | ||
1224 | int i; | ||
1225 | |||
1226 | printf("%lx:%lx:%lx:%lx", | ||
1227 | (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag, | ||
1228 | (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag); | ||
1229 | for (i = 0; i < NCCS; ++i) | ||
1230 | printf(":%x", (unsigned int) mode->c_cc[i]); | ||
1231 | putchar('\n'); | ||
1232 | } | ||
1233 | |||
1234 | static int recover_mode(char *arg, struct termios *mode) | ||
1235 | { | ||
1236 | int i, n; | ||
1237 | unsigned int chr; | ||
1238 | unsigned long iflag, oflag, cflag, lflag; | ||
1239 | |||
1240 | /* Scan into temporaries since it is too much trouble to figure out | ||
1241 | the right format for `tcflag_t'. */ | ||
1242 | if (sscanf(arg, "%lx:%lx:%lx:%lx%n", | ||
1243 | &iflag, &oflag, &cflag, &lflag, &n) != 4) | ||
1244 | return 0; | ||
1245 | mode->c_iflag = iflag; | ||
1246 | mode->c_oflag = oflag; | ||
1247 | mode->c_cflag = cflag; | ||
1248 | mode->c_lflag = lflag; | ||
1249 | arg += n; | ||
1250 | for (i = 0; i < NCCS; ++i) { | ||
1251 | if (sscanf(arg, ":%x%n", &chr, &n) != 1) | ||
1252 | return 0; | ||
1253 | mode->c_cc[i] = chr; | ||
1254 | arg += n; | ||
1255 | } | ||
1256 | |||
1257 | /* Fail if there are too many fields. */ | ||
1258 | if (*arg != '\0') | ||
1259 | return 0; | ||
1260 | |||
1261 | return 1; | ||
1262 | } | ||
1263 | |||
1264 | struct speed_map { | ||
1265 | speed_t speed; /* Internal form. */ | ||
1266 | unsigned long value; /* Numeric value. */ | ||
1267 | }; | ||
1268 | |||
1269 | static const struct speed_map speeds[] = { | ||
1270 | {B0, 0}, | ||
1271 | {B50, 50}, | ||
1272 | {B75, 75}, | ||
1273 | {B110, 110}, | ||
1274 | {B134, 134}, | ||
1275 | {B150, 150}, | ||
1276 | {B200, 200}, | ||
1277 | {B300, 300}, | ||
1278 | {B600, 600}, | ||
1279 | {B1200, 1200}, | ||
1280 | {B1800, 1800}, | ||
1281 | {B2400, 2400}, | ||
1282 | {B4800, 4800}, | ||
1283 | {B9600, 9600}, | ||
1284 | {B19200, 19200}, | ||
1285 | {B38400, 38400}, | ||
1286 | #ifdef B57600 | ||
1287 | {B57600, 57600}, | ||
1288 | #endif | ||
1289 | #ifdef B115200 | ||
1290 | {B115200, 115200}, | ||
1291 | #endif | ||
1292 | #ifdef B230400 | ||
1293 | {B230400, 230400}, | ||
1294 | #endif | ||
1295 | #ifdef B460800 | ||
1296 | {B460800, 460800}, | ||
1297 | #endif | ||
1298 | }; | ||
1299 | |||
1300 | static const int NUM_SPEEDS = (sizeof(speeds) / sizeof(struct speed_map)); | ||
1301 | |||
1302 | static speed_t string_to_baud(const char *arg) | ||
1303 | { | ||
1304 | int i; | ||
1305 | static const struct suffix_mult stty_zerosuff = { NULL, 0 }; | ||
1306 | |||
1307 | for (i = 0; i < NUM_SPEEDS; ++i) | ||
1308 | if (parse_number(arg, &stty_zerosuff) == speeds[i].value) | ||
1309 | return speeds[i].speed; | ||
1310 | return (speed_t) - 1; | ||
1311 | } | ||
1312 | |||
1313 | static unsigned long baud_to_value(speed_t speed) | ||
1314 | { | ||
1315 | int i; | ||
1316 | |||
1317 | for (i = 0; i < NUM_SPEEDS; ++i) | ||
1318 | if (speed == speeds[i].speed) | ||
1319 | return speeds[i].value; | ||
1320 | return 0; | ||
1321 | } | ||
1322 | |||
1323 | static void sane_mode(struct termios *mode) | ||
1324 | { | ||
1325 | int i; | ||
1326 | tcflag_t *bitsp; | ||
1327 | |||
1328 | for (i = 0; i < NUM_control_info; ++i) { | ||
1329 | #if VMIN == VEOF | ||
1330 | if (control_info[i].name == stty_min) | ||
1331 | break; | ||
1332 | #endif | ||
1333 | mode->c_cc[control_info[i].offset] = control_info[i].saneval; | ||
1334 | } | ||
1335 | |||
1336 | for (i = 0; i < NUM_mode_info; ++i) { | ||
1337 | if (mode_info[i].flags & SANE_SET) { | ||
1338 | bitsp = mode_type_flag(mode_info[i].type, mode); | ||
1339 | *bitsp = (*bitsp & ~mode_info[i].mask) | mode_info[i].bits; | ||
1340 | } else if (mode_info[i].flags & SANE_UNSET) { | ||
1341 | bitsp = mode_type_flag(mode_info[i].type, mode); | ||
1342 | *bitsp = *bitsp & ~mode_info[i].mask & ~mode_info[i].bits; | ||
1343 | } | ||
1344 | } | ||
1345 | } | ||
1346 | |||
1347 | /* Return a string that is the printable representation of character CH. */ | ||
1348 | /* Adapted from `cat' by Torbjorn Granlund. */ | ||
1349 | |||
1350 | static const char *visible(unsigned int ch) | ||
1351 | { | ||
1352 | static char buf[10]; | ||
1353 | char *bpout = buf; | ||
1354 | |||
1355 | if (ch == _POSIX_VDISABLE) | ||
1356 | return "<undef>"; | ||
1357 | |||
1358 | if (ch >= 32) { | ||
1359 | if (ch < 127) | ||
1360 | *bpout++ = ch; | ||
1361 | else if (ch == 127) { | ||
1362 | *bpout++ = '^'; | ||
1363 | *bpout++ = '?'; | ||
1364 | } else { | ||
1365 | *bpout++ = 'M', *bpout++ = '-'; | ||
1366 | if (ch >= 128 + 32) { | ||
1367 | if (ch < 128 + 127) | ||
1368 | *bpout++ = ch - 128; | ||
1369 | else { | ||
1370 | *bpout++ = '^'; | ||
1371 | *bpout++ = '?'; | ||
1372 | } | ||
1373 | } else { | ||
1374 | *bpout++ = '^'; | ||
1375 | *bpout++ = ch - 128 + 64; | ||
1376 | } | ||
1377 | } | ||
1378 | } else { | ||
1379 | *bpout++ = '^'; | ||
1380 | *bpout++ = ch + 64; | ||
1381 | } | ||
1382 | *bpout = '\0'; | ||
1383 | return (const char *) buf; | ||
1384 | } | ||
1385 | |||
1386 | /* | ||
1387 | Local Variables: | ||
1388 | c-file-style: "linux" | ||
1389 | c-basic-offset: 4 | ||
1390 | tab-width: 4 | ||
1391 | End: | ||
1392 | */ | ||