It turns out that DODMALLOC was broken when I reorganized busybox.h
[oweals/busybox.git] / stty.c
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
32 #include <termios.h>
33 #include <sys/ioctl.h>
34 #include <getopt.h>
35
36 #include <sys/param.h>
37 #include <unistd.h>
38
39 #ifndef STDIN_FILENO
40 # define STDIN_FILENO 0
41 #endif
42
43 #ifndef STDOUT_FILENO
44 # define STDOUT_FILENO 1
45 #endif
46
47 #include <stdlib.h>
48 #include <string.h>
49 #include <assert.h>
50 #include <ctype.h>
51 #include <errno.h>
52 #include <limits.h>
53 #include <memory.h>
54 #include <fcntl.h>
55 #include "busybox.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
1306         for (i = 0; i < NUM_SPEEDS; ++i)
1307                 if (parse_number(arg, 0) == speeds[i].value)
1308                         return speeds[i].speed;
1309         return (speed_t) - 1;
1310 }
1311
1312 static unsigned long baud_to_value(speed_t speed)
1313 {
1314         int i;
1315
1316         for (i = 0; i < NUM_SPEEDS; ++i)
1317                 if (speed == speeds[i].speed)
1318                         return speeds[i].value;
1319         return 0;
1320 }
1321
1322 static void sane_mode(struct termios *mode)
1323 {
1324         int i;
1325         tcflag_t *bitsp;
1326
1327         for (i = 0; i < NUM_control_info; ++i) {
1328 #if VMIN == VEOF
1329                 if (control_info[i].name == stty_min)
1330                         break;
1331 #endif
1332                 mode->c_cc[control_info[i].offset] = control_info[i].saneval;
1333         }
1334
1335         for (i = 0; i < NUM_mode_info; ++i) {
1336                 if (mode_info[i].flags & SANE_SET) {
1337                         bitsp = mode_type_flag(mode_info[i].type, mode);
1338                         *bitsp = (*bitsp & ~mode_info[i].mask) | mode_info[i].bits;
1339                 } else if (mode_info[i].flags & SANE_UNSET) {
1340                         bitsp = mode_type_flag(mode_info[i].type, mode);
1341                         *bitsp = *bitsp & ~mode_info[i].mask & ~mode_info[i].bits;
1342                 }
1343         }
1344 }
1345
1346 /* Return a string that is the printable representation of character CH.  */
1347 /* Adapted from `cat' by Torbjorn Granlund.  */
1348
1349 static const char *visible(unsigned int ch)
1350 {
1351         static char buf[10];
1352         char *bpout = buf;
1353
1354         if (ch == _POSIX_VDISABLE)
1355                 return "<undef>";
1356
1357         if (ch >= 32) {
1358                 if (ch < 127)
1359                         *bpout++ = ch;
1360                 else if (ch == 127) {
1361                         *bpout++ = '^';
1362                         *bpout++ = '?';
1363                 } else {
1364                         *bpout++ = 'M', *bpout++ = '-';
1365                         if (ch >= 128 + 32) {
1366                                 if (ch < 128 + 127)
1367                                         *bpout++ = ch - 128;
1368                                 else {
1369                                         *bpout++ = '^';
1370                                         *bpout++ = '?';
1371                                 }
1372                         } else {
1373                                 *bpout++ = '^';
1374                                 *bpout++ = ch - 128 + 64;
1375                         }
1376                 }
1377         } else {
1378                 *bpout++ = '^';
1379                 *bpout++ = ch + 64;
1380         }
1381         *bpout = '\0';
1382         return (const char *) buf;
1383 }
1384
1385 /*
1386 Local Variables:
1387 c-file-style: "linux"
1388 c-basic-offset: 4
1389 tab-width: 4
1390 End:
1391 */