1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * Copyright (c) 2014, Joyent, Inc. All rights reserved. 26 */ 27 28 /* 29 * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T 30 * All Rights Reserved 31 * 32 */ 33 34 #include <stdio.h> 35 #include <unistd.h> 36 #include <stdlib.h> 37 #include <libintl.h> 38 #include <sys/types.h> 39 #include <ctype.h> 40 #include <termio.h> 41 #include <sys/stermio.h> 42 #include <sys/termiox.h> 43 #ifdef EUC 44 #include <sys/param.h> 45 #include <sys/stropts.h> 46 #include <sys/eucioctl.h> 47 #include <sys/csiioctl.h> 48 #include <sys/stream.h> 49 #include <sys/termios.h> 50 #include <sys/ldterm.h> 51 #include <getwidth.h> 52 #endif /* EUC */ 53 #include "stty.h" 54 #include <locale.h> 55 #include <string.h> 56 57 static char *s_arg; /* s_arg: ptr to mode to be set */ 58 static int match; 59 #ifdef EUC 60 static int parse_encoded(struct termios *, ldterm_cs_data_user_t *, int); 61 #else 62 static int parse_encoded(struct termios *); 63 #endif /* EUC */ 64 static int eq(const char *string); 65 static int gct(char *cp, int term); 66 67 /* set terminal modes for supplied options */ 68 char * 69 sttyparse(int argc, char *argv[], int term, struct termio *ocb, 70 struct termios *cb, struct termiox *termiox, struct winsize *winsize 71 #ifdef EUC 72 /* */, eucwidth_t *wp, struct eucioc *kwp, ldterm_cs_data_user_t *cswp, 73 ldterm_cs_data_user_t *kcswp 74 #endif /* EUC */ 75 /* */) 76 { 77 int i; 78 79 while (--argc > 0) { 80 s_arg = *++argv; 81 match = 0; 82 if (term & ASYNC) { 83 if (eq("erase") && --argc) 84 cb->c_cc[VERASE] = gct(*++argv, term); 85 else if (eq("intr") && --argc) 86 cb->c_cc[VINTR] = gct(*++argv, term); 87 else if (eq("quit") && --argc) 88 cb->c_cc[VQUIT] = gct(*++argv, term); 89 else if (eq("eof") && --argc) 90 cb->c_cc[VEOF] = gct(*++argv, term); 91 else if (eq("min") && --argc) { 92 if (isdigit((unsigned char)argv[1][0])) 93 cb->c_cc[VMIN] = atoi(*++argv); 94 else 95 cb->c_cc[VMIN] = gct(*++argv, term); 96 } else if (eq("eol") && --argc) 97 cb->c_cc[VEOL] = gct(*++argv, term); 98 else if (eq("eol2") && --argc) 99 cb->c_cc[VEOL2] = gct(*++argv, term); 100 else if (eq("time") && --argc) { 101 if (isdigit((unsigned char)argv[1][0])) 102 cb->c_cc[VTIME] = atoi(*++argv); 103 else 104 cb->c_cc[VTIME] = gct(*++argv, term); 105 } else if (eq("kill") && --argc) 106 cb->c_cc[VKILL] = gct(*++argv, term); 107 else if (eq("swtch") && --argc) 108 cb->c_cc[VSWTCH] = gct(*++argv, term); 109 if (match) 110 continue; 111 if (term & TERMIOS) { 112 if (eq("start") && --argc) 113 cb->c_cc[VSTART] = gct(*++argv, term); 114 else if (eq("stop") && --argc) 115 cb->c_cc[VSTOP] = gct(*++argv, term); 116 else if (eq("susp") && --argc) 117 cb->c_cc[VSUSP] = gct(*++argv, term); 118 else if (eq("dsusp") && --argc) 119 cb->c_cc[VDSUSP] = gct(*++argv, term); 120 else if (eq("rprnt") && --argc) 121 cb->c_cc[VREPRINT] = gct(*++argv, term); 122 else if (eq("reprint") && --argc) 123 cb->c_cc[VREPRINT] = gct(*++argv, term); 124 else if (eq("discard") && --argc) 125 cb->c_cc[VDISCARD] = gct(*++argv, term); 126 else if (eq("flush") && --argc) 127 cb->c_cc[VDISCARD] = gct(*++argv, term); 128 else if (eq("werase") && --argc) 129 cb->c_cc[VWERASE] = gct(*++argv, term); 130 else if (eq("lnext") && --argc) 131 cb->c_cc[VLNEXT] = gct(*++argv, term); 132 else if (eq("status") && --argc) 133 cb->c_cc[VSTATUS] = gct(*++argv, term); 134 } 135 if (match) 136 continue; 137 if (eq("ek")) { 138 cb->c_cc[VERASE] = CERASE; 139 cb->c_cc[VKILL] = CKILL; 140 } else if (eq("line") && 141 !(term & TERMIOS) && --argc) { 142 ocb->c_line = atoi(*++argv); 143 continue; 144 } else if (eq("raw")) { 145 cb->c_cc[VMIN] = 1; 146 cb->c_cc[VTIME] = 0; 147 } else if (eq("-raw") | eq("cooked")) { 148 cb->c_cc[VEOF] = CEOF; 149 cb->c_cc[VEOL] = CNUL; 150 } else if (eq("sane")) { 151 cb->c_cc[VERASE] = CERASE; 152 cb->c_cc[VKILL] = CKILL; 153 cb->c_cc[VQUIT] = CQUIT; 154 cb->c_cc[VINTR] = CINTR; 155 cb->c_cc[VEOF] = CEOF; 156 cb->c_cc[VEOL] = CNUL; 157 cb->c_cc[VSTATUS] = CSTATUS; 158 /* SWTCH purposely not set */ 159 #ifdef EUC 160 } else if (eq("defeucw")) { 161 kwp->eucw[0] = '\001'; 162 kwp->eucw[1] = 163 (unsigned char)(wp->_eucw1 & 0177); 164 kwp->eucw[2] = 165 (unsigned char)(wp->_eucw2 & 0177); 166 kwp->eucw[3] = 167 (unsigned char)(wp->_eucw3 & 0177); 168 169 kwp->scrw[0] = '\001'; 170 kwp->scrw[1] = 171 (unsigned char)(wp->_scrw1 & 0177); 172 kwp->scrw[2] = 173 (unsigned char)(wp->_scrw2 & 0177); 174 kwp->scrw[3] = 175 (unsigned char)(wp->_scrw3 & 0177); 176 177 (void) memcpy((void *)kcswp, (const void *)cswp, 178 sizeof (ldterm_cs_data_user_t)); 179 #endif /* EUC */ 180 } else if ((term & TERMIOS) && eq("ospeed") && --argc) { 181 s_arg = *++argv; 182 for (match = 0, i = 0; speeds[i].string; i++) { 183 if (eq(speeds[i].string)) { 184 (void) cfsetospeed(cb, 185 speeds[i].code); 186 break; 187 } 188 } 189 if (!match) 190 return (s_arg); 191 continue; 192 193 } else if ((term & TERMIOS) && eq("ispeed") && --argc) { 194 s_arg = *++argv; 195 for (match = 0, i = 0; speeds[i].string; i++) { 196 if (eq(speeds[i].string)) { 197 (void) cfsetispeed(cb, 198 speeds[i].code); 199 break; 200 } 201 } 202 if (!match) 203 return (s_arg); 204 continue; 205 206 } else { 207 for (match = 0, i = 0; speeds[i].string; i++) { 208 if (eq(speeds[i].string)) { 209 (void) cfsetospeed(cb, 210 speeds[i].code); 211 (void) cfsetispeed(cb, 212 speeds[i].code); 213 break; 214 } 215 } 216 } 217 } 218 if (!(term & ASYNC) && eq("ctab") && --argc) { 219 cb->c_cc[7] = gct(*++argv, term); 220 continue; 221 } 222 223 for (i = 0; imodes[i].string; i++) 224 if (eq(imodes[i].string)) { 225 cb->c_iflag &= ~imodes[i].reset; 226 cb->c_iflag |= imodes[i].set; 227 #ifdef EUC 228 if (wp->_multibyte && 229 (eq("-raw") || eq("cooked") || eq("sane"))) 230 cb->c_iflag &= ~ISTRIP; 231 #endif /* EUC */ 232 } 233 if (term & TERMIOS) { 234 for (i = 0; nimodes[i].string; i++) 235 if (eq(nimodes[i].string)) { 236 cb->c_iflag &= ~nimodes[i].reset; 237 cb->c_iflag |= nimodes[i].set; 238 } 239 } 240 241 for (i = 0; omodes[i].string; i++) 242 if (eq(omodes[i].string)) { 243 cb->c_oflag &= ~omodes[i].reset; 244 cb->c_oflag |= omodes[i].set; 245 } 246 if (!(term & ASYNC) && eq("sane")) { 247 cb->c_oflag |= TAB3; 248 continue; 249 } 250 for (i = 0; cmodes[i].string; i++) 251 if (eq(cmodes[i].string)) { 252 cb->c_cflag &= ~cmodes[i].reset; 253 cb->c_cflag |= cmodes[i].set; 254 #ifdef EUC 255 if (wp->_multibyte && 256 (eq("-raw") || eq("cooked") || 257 eq("sane"))) { 258 cb->c_cflag &= ~(CS7|PARENB); 259 cb->c_cflag |= CS8; 260 } 261 #endif /* EUC */ 262 } 263 if (term & TERMIOS) 264 for (i = 0; ncmodes[i].string; i++) 265 if (eq(ncmodes[i].string)) { 266 cb->c_cflag &= ~ncmodes[i].reset; 267 cb->c_cflag |= ncmodes[i].set; 268 } 269 for (i = 0; lmodes[i].string; i++) 270 if (eq(lmodes[i].string)) { 271 cb->c_lflag &= ~lmodes[i].reset; 272 cb->c_lflag |= lmodes[i].set; 273 } 274 if (term & TERMIOS) 275 for (i = 0; nlmodes[i].string; i++) 276 if (eq(nlmodes[i].string)) { 277 cb->c_lflag &= ~nlmodes[i].reset; 278 cb->c_lflag |= nlmodes[i].set; 279 } 280 if (term & FLOW) { 281 for (i = 0; hmodes[i].string; i++) 282 if (eq(hmodes[i].string)) { 283 termiox->x_hflag &= ~hmodes[i].reset; 284 termiox->x_hflag |= hmodes[i].set; 285 } 286 for (i = 0; clkmodes[i].string; i++) 287 if (eq(clkmodes[i].string)) { 288 termiox->x_cflag &= ~clkmodes[i].reset; 289 termiox->x_cflag |= clkmodes[i].set; 290 } 291 292 } 293 294 if (eq("rows") && --argc) 295 winsize->ws_row = atoi(*++argv); 296 else if ((eq("columns") || eq("cols")) && --argc) 297 winsize->ws_col = atoi(*++argv); 298 else if (eq("xpixels") && --argc) 299 winsize->ws_xpixel = atoi(*++argv); 300 else if (eq("ypixels") && --argc) 301 winsize->ws_ypixel = atoi(*++argv); 302 303 if (!match) { 304 #ifdef EUC 305 if (!parse_encoded(cb, kcswp, term)) { 306 #else 307 if (!parse_encoded(cb)) { 308 #endif /* EUC */ 309 return (s_arg); /* parsing failed */ 310 } 311 } 312 } 313 return ((char *)0); 314 } 315 316 static int 317 eq(const char *string) 318 { 319 int i; 320 321 if (!s_arg) 322 return (0); 323 i = 0; 324 loop: 325 if (s_arg[i] != string[i]) 326 return (0); 327 if (s_arg[i++] != '\0') 328 goto loop; 329 match++; 330 return (1); 331 } 332 333 /* get pseudo control characters from terminal */ 334 /* and convert to internal representation */ 335 static int 336 gct(char *cp, int term) 337 { 338 int c; 339 340 c = *cp; 341 if (c == '^') { 342 c = *++cp; 343 if (c == '?') 344 c = 0177; /* map '^?' to 0177 */ 345 else if (c == '-') { 346 /* map '^-' to undefined */ 347 c = (term & TERMIOS) ? _POSIX_VDISABLE : 0200; 348 } else 349 c &= 037; 350 } else if (strcmp(cp, "undef") == 0) { 351 /* map "undef" to undefined */ 352 c = (term & TERMIOS) ? _POSIX_VDISABLE : 0200; 353 } 354 return (c); 355 } 356 357 /* get modes of tty device and fill in applicable structures */ 358 int 359 get_ttymode(int fd, struct termio *termio, struct termios *termios, 360 struct stio *stermio, struct termiox *termiox, struct winsize *winsize 361 #ifdef EUC 362 /* */, struct eucioc *kwp, ldterm_cs_data_user_t *kcswp 363 #endif /* EUC */ 364 /* */) 365 { 366 int i; 367 int term = 0; 368 #ifdef EUC 369 struct strioctl cmd; 370 #endif /* EUC */ 371 if (ioctl(fd, STGET, stermio) == -1) { 372 term |= ASYNC; 373 if (ioctl(fd, TCGETS, termios) == -1) { 374 if (ioctl(fd, TCGETA, termio) == -1) 375 return (-1); 376 termios->c_lflag = termio->c_lflag; 377 termios->c_oflag = termio->c_oflag; 378 termios->c_iflag = termio->c_iflag; 379 termios->c_cflag = termio->c_cflag; 380 for (i = 0; i < NCC; i++) 381 termios->c_cc[i] = termio->c_cc[i]; 382 } else 383 term |= TERMIOS; 384 } else { 385 termios->c_cc[7] = (unsigned)stermio->tab; 386 termios->c_lflag = stermio->lmode; 387 termios->c_oflag = stermio->omode; 388 termios->c_iflag = stermio->imode; 389 } 390 391 if (ioctl(fd, TCGETX, termiox) == 0) 392 term |= FLOW; 393 394 if (ioctl(fd, TIOCGWINSZ, winsize) == 0) 395 term |= WINDOW; 396 #ifdef EUC 397 cmd.ic_cmd = EUC_WGET; 398 cmd.ic_timout = 0; 399 cmd.ic_len = sizeof (struct eucioc); 400 cmd.ic_dp = (char *)kwp; 401 402 if (ioctl(fd, I_STR, &cmd) == 0) 403 term |= EUCW; 404 405 cmd.ic_cmd = CSDATA_GET; 406 cmd.ic_timout = 0; 407 cmd.ic_len = sizeof (ldterm_cs_data_user_t); 408 cmd.ic_dp = (char *)kcswp; 409 410 if (ioctl(fd, I_STR, &cmd) == 0) 411 term |= CSIW; 412 else 413 (void) memset((void *)kcswp, 0, sizeof (ldterm_cs_data_user_t)); 414 #endif /* EUC */ 415 return (term); 416 } 417 418 /* set tty modes */ 419 int 420 set_ttymode(int fd, int term, struct termio *termio, struct termios *termios, 421 struct stio *stermio, struct termiox *termiox, struct winsize *winsize, 422 struct winsize *owinsize 423 #ifdef EUC 424 /* */, struct eucioc *kwp, ldterm_cs_data_user_t *kcswp, 425 int invalid_ldterm_dat_file 426 #endif /* EUC */ 427 /* */) 428 { 429 int i; 430 #ifdef EUC 431 struct strioctl cmd; 432 #endif /* EUC */ 433 434 if (term & ASYNC) { 435 if (term & TERMIOS) { 436 if (ioctl(fd, TCSETSW, termios) == -1) 437 return (-1); 438 } else { 439 termio->c_lflag = termios->c_lflag; 440 termio->c_oflag = termios->c_oflag; 441 termio->c_iflag = termios->c_iflag; 442 termio->c_cflag = termios->c_cflag; 443 for (i = 0; i < NCC; i++) 444 termio->c_cc[i] = termios->c_cc[i]; 445 if (ioctl(fd, TCSETAW, termio) == -1) 446 return (-1); 447 } 448 449 } else { 450 stermio->imode = termios->c_iflag; 451 stermio->omode = termios->c_oflag; 452 stermio->lmode = termios->c_lflag; 453 stermio->tab = termios->c_cc[7]; 454 if (ioctl(fd, STSET, stermio) == -1) 455 return (-1); 456 } 457 if (term & FLOW) { 458 if (ioctl(fd, TCSETXW, termiox) == -1) 459 return (-1); 460 } 461 if ((owinsize->ws_col != winsize->ws_col || 462 owinsize->ws_row != winsize->ws_row || 463 owinsize->ws_xpixel != winsize->ws_xpixel || 464 owinsize->ws_ypixel != winsize->ws_ypixel) && 465 ioctl(0, TIOCSWINSZ, winsize) != 0) 466 return (-1); 467 #ifdef EUC 468 /* 469 * If the ldterm.dat file contains valid, non-EUC codeset info, 470 * send downstream CSDATA_SET. Otherwise, try EUC_WSET. 471 */ 472 if (invalid_ldterm_dat_file) { 473 (void) fprintf(stderr, gettext( 474 "stty: can't set codeset width due to invalid ldterm.dat.\n")); 475 return (-1); 476 } else if ((term & CSIW) && kcswp->version) { 477 cmd.ic_cmd = CSDATA_SET; 478 cmd.ic_timout = 0; 479 cmd.ic_len = sizeof (ldterm_cs_data_user_t); 480 cmd.ic_dp = (char *)kcswp; 481 if (ioctl(fd, I_STR, &cmd) != 0) { 482 (void) fprintf(stderr, gettext( 483 "stty: can't set codeset width.\n")); 484 return (-1); 485 } 486 } else if (term & EUCW) { 487 cmd.ic_cmd = EUC_WSET; 488 cmd.ic_timout = 0; 489 cmd.ic_len = sizeof (struct eucioc); 490 cmd.ic_dp = (char *)kwp; 491 if (ioctl(fd, I_STR, &cmd) != 0) { 492 (void) fprintf(stderr, gettext( 493 "stty: can't set EUC codeset width.\n")); 494 return (-1); 495 } 496 } 497 #endif /* EUC */ 498 return (0); 499 } 500 501 static int 502 parse_encoded(struct termios *cb 503 #ifdef EUC 504 /* */, ldterm_cs_data_user_t *kcswp, int term 505 #endif /* EUC */ 506 /* */) 507 { 508 unsigned long grab[NUM_FIELDS]; 509 int last, i; 510 #ifdef EUC 511 long l; 512 char s[3]; 513 char *t; 514 char *r; 515 uchar_t *g; 516 ldterm_cs_data_user_t ecswp; 517 #endif /* EUC */ 518 519 /* 520 * Although there are only 16 control chars defined as of April 1995, 521 * parse_encoded() and prencode() will not have to be changed if up to 522 * MAX_CC control chars are defined in the future. 523 * Scan the fields of "stty -g" output into the grab array. 524 * Set a total of NUM_FIELDS fields (NUM_MODES modes + MAX_CC 525 * control chars). 526 */ 527 i = sscanf(s_arg, "%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:" 528 "%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx", 529 &grab[0], &grab[1], &grab[2], &grab[3], &grab[4], &grab[5], 530 &grab[6], &grab[7], &grab[8], &grab[9], &grab[10], &grab[11], 531 &grab[12], &grab[13], &grab[14], &grab[15], &grab[16], &grab[17], 532 &grab[18], &grab[19], &grab[20], &grab[21]); 533 534 if (i < 12) 535 return (0); 536 cb->c_iflag = grab[0]; 537 cb->c_oflag = grab[1]; 538 cb->c_cflag = grab[2]; 539 cb->c_lflag = grab[3]; 540 541 last = i - NUM_MODES; 542 for (i = 0; i < last; i++) 543 cb->c_cc[i] = (unsigned char) grab[i+NUM_MODES]; 544 545 #ifdef EUC 546 /* This is to fulfill PSARC/1999/140 TCR2. */ 547 if (term & CSIW) { 548 r = strdup(s_arg); 549 if (r == (char *)NULL) { 550 (void) fprintf(stderr, gettext( 551 "no more memory - try again later\n")); 552 return (0); 553 } 554 t = strtok(r, ":"); 555 for (i = 0; t != NULL && i < 22; i++) { 556 t = strtok(NULL, ":"); 557 } 558 559 if (t == NULL) { 560 free((void *)r); 561 return (0); 562 } 563 ecswp.version = (uchar_t)strtol(t, (char **)NULL, 16); 564 if (ecswp.version > LDTERM_DATA_VERSION || 565 ecswp.version == 0) { 566 free((void *)r); 567 return (0); 568 } 569 570 if ((t = strtok(NULL, ":")) == NULL) { 571 free((void *)r); 572 return (0); 573 } 574 ecswp.codeset_type = (uchar_t)strtol(t, (char **)NULL, 16); 575 if (ecswp.codeset_type < LDTERM_CS_TYPE_MIN || 576 ecswp.codeset_type > LDTERM_CS_TYPE_MAX) { 577 free((void *)r); 578 return (0); 579 } 580 581 if ((t = strtok(NULL, ":")) == NULL) { 582 free((void *)r); 583 return (0); 584 } 585 ecswp.csinfo_num = (uchar_t)strtol(t, (char **)NULL, 16); 586 if ((ecswp.codeset_type == LDTERM_CS_TYPE_EUC && 587 ecswp.csinfo_num > 3) || 588 (ecswp.codeset_type == LDTERM_CS_TYPE_PCCS && 589 (ecswp.csinfo_num < 1 || ecswp.csinfo_num > 10))) { 590 free((void *)r); 591 return (0); 592 } 593 594 if ((t = strtok(NULL, ":")) == NULL) { 595 free((void *)r); 596 return (0); 597 } 598 s[2] = '\0'; 599 for (i = 0; *t != 0 && i < MAXNAMELEN; i++) { 600 if (*(t + 1) == (char)NULL) { 601 free((void *)r); 602 return (0); 603 } 604 s[0] = *t++; 605 s[1] = *t++; 606 ecswp.locale_name[i] = (char)strtol(s, (char **)NULL, 607 16); 608 } 609 if (i >= MAXNAMELEN) { 610 free((void *)r); 611 return (0); 612 } 613 ecswp.locale_name[i] = '\0'; 614 615 g = (uchar_t *)ecswp.eucpc_data; 616 for (i = 0; i < (LDTERM_CS_MAX_CODESETS * 4); i++) { 617 if ((t = strtok(NULL, ":")) == NULL) { 618 free((void *)r); 619 return (0); 620 } 621 l = strtol(t, (char **)NULL, 16); 622 if (l < 0 || l > 255) { 623 free((void *)r); 624 return (0); 625 } 626 *g++ = (uchar_t)l; 627 } 628 629 /* We got the 'ecswp' all filled up now; let's copy. */ 630 (void) memcpy((void *)kcswp, (const void *)&ecswp, 631 sizeof (ldterm_cs_data_user_t)); 632 } 633 #endif /* EUC */ 634 635 return (1); 636 } 637