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