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 else if (eq("erase2") && --argc) 135 cb->c_cc[VERASE2] = gct(*++argv, term); 136 } 137 if (match) 138 continue; 139 if (eq("ek")) { 140 cb->c_cc[VERASE] = CERASE; 141 if (term & TERMIOS) 142 cb->c_cc[VERASE2] = CERASE2; 143 cb->c_cc[VKILL] = CKILL; 144 } else if (eq("line") && 145 !(term & TERMIOS) && --argc) { 146 ocb->c_line = atoi(*++argv); 147 continue; 148 } else if (eq("raw")) { 149 cb->c_cc[VMIN] = 1; 150 cb->c_cc[VTIME] = 0; 151 } else if (eq("-raw") | eq("cooked")) { 152 cb->c_cc[VEOF] = CEOF; 153 cb->c_cc[VEOL] = CNUL; 154 } else if (eq("sane")) { 155 cb->c_cc[VERASE] = CERASE; 156 if (term & TERMIOS) 157 cb->c_cc[VERASE2] = CERASE2; 158 cb->c_cc[VKILL] = CKILL; 159 cb->c_cc[VQUIT] = CQUIT; 160 cb->c_cc[VINTR] = CINTR; 161 cb->c_cc[VEOF] = CEOF; 162 cb->c_cc[VEOL] = CNUL; 163 cb->c_cc[VSTATUS] = CSTATUS; 164 /* SWTCH purposely not set */ 165 #ifdef EUC 166 } else if (eq("defeucw")) { 167 kwp->eucw[0] = '\001'; 168 kwp->eucw[1] = 169 (unsigned char)(wp->_eucw1 & 0177); 170 kwp->eucw[2] = 171 (unsigned char)(wp->_eucw2 & 0177); 172 kwp->eucw[3] = 173 (unsigned char)(wp->_eucw3 & 0177); 174 175 kwp->scrw[0] = '\001'; 176 kwp->scrw[1] = 177 (unsigned char)(wp->_scrw1 & 0177); 178 kwp->scrw[2] = 179 (unsigned char)(wp->_scrw2 & 0177); 180 kwp->scrw[3] = 181 (unsigned char)(wp->_scrw3 & 0177); 182 183 (void) memcpy((void *)kcswp, (const void *)cswp, 184 sizeof (ldterm_cs_data_user_t)); 185 #endif /* EUC */ 186 } else if ((term & TERMIOS) && eq("ospeed") && --argc) { 187 s_arg = *++argv; 188 for (match = 0, i = 0; speeds[i].string; i++) { 189 if (eq(speeds[i].string)) { 190 (void) cfsetospeed(cb, 191 speeds[i].code); 192 break; 193 } 194 } 195 if (!match) 196 return (s_arg); 197 continue; 198 199 } else if ((term & TERMIOS) && eq("ispeed") && --argc) { 200 s_arg = *++argv; 201 for (match = 0, i = 0; speeds[i].string; i++) { 202 if (eq(speeds[i].string)) { 203 (void) cfsetispeed(cb, 204 speeds[i].code); 205 break; 206 } 207 } 208 if (!match) 209 return (s_arg); 210 continue; 211 212 } else { 213 for (match = 0, i = 0; speeds[i].string; i++) { 214 if (eq(speeds[i].string)) { 215 (void) cfsetospeed(cb, 216 speeds[i].code); 217 (void) cfsetispeed(cb, 218 speeds[i].code); 219 break; 220 } 221 } 222 } 223 } 224 if (!(term & ASYNC) && eq("ctab") && --argc) { 225 cb->c_cc[7] = gct(*++argv, term); 226 continue; 227 } 228 229 for (i = 0; imodes[i].string; i++) 230 if (eq(imodes[i].string)) { 231 cb->c_iflag &= ~imodes[i].reset; 232 cb->c_iflag |= imodes[i].set; 233 #ifdef EUC 234 if (wp->_multibyte && 235 (eq("-raw") || eq("cooked") || eq("sane"))) 236 cb->c_iflag &= ~ISTRIP; 237 #endif /* EUC */ 238 } 239 if (term & TERMIOS) { 240 for (i = 0; nimodes[i].string; i++) 241 if (eq(nimodes[i].string)) { 242 cb->c_iflag &= ~nimodes[i].reset; 243 cb->c_iflag |= nimodes[i].set; 244 } 245 } 246 247 for (i = 0; omodes[i].string; i++) 248 if (eq(omodes[i].string)) { 249 cb->c_oflag &= ~omodes[i].reset; 250 cb->c_oflag |= omodes[i].set; 251 } 252 if (!(term & ASYNC) && eq("sane")) { 253 cb->c_oflag |= TAB3; 254 continue; 255 } 256 for (i = 0; cmodes[i].string; i++) 257 if (eq(cmodes[i].string)) { 258 cb->c_cflag &= ~cmodes[i].reset; 259 cb->c_cflag |= cmodes[i].set; 260 #ifdef EUC 261 if (wp->_multibyte && 262 (eq("-raw") || eq("cooked") || 263 eq("sane"))) { 264 cb->c_cflag &= ~(CS7|PARENB); 265 cb->c_cflag |= CS8; 266 } 267 #endif /* EUC */ 268 } 269 if (term & TERMIOS) 270 for (i = 0; ncmodes[i].string; i++) 271 if (eq(ncmodes[i].string)) { 272 cb->c_cflag &= ~ncmodes[i].reset; 273 cb->c_cflag |= ncmodes[i].set; 274 } 275 for (i = 0; lmodes[i].string; i++) 276 if (eq(lmodes[i].string)) { 277 cb->c_lflag &= ~lmodes[i].reset; 278 cb->c_lflag |= lmodes[i].set; 279 } 280 if (term & TERMIOS) 281 for (i = 0; nlmodes[i].string; i++) 282 if (eq(nlmodes[i].string)) { 283 cb->c_lflag &= ~nlmodes[i].reset; 284 cb->c_lflag |= nlmodes[i].set; 285 } 286 if (term & FLOW) { 287 for (i = 0; hmodes[i].string; i++) 288 if (eq(hmodes[i].string)) { 289 termiox->x_hflag &= ~hmodes[i].reset; 290 termiox->x_hflag |= hmodes[i].set; 291 } 292 for (i = 0; clkmodes[i].string; i++) 293 if (eq(clkmodes[i].string)) { 294 termiox->x_cflag &= ~clkmodes[i].reset; 295 termiox->x_cflag |= clkmodes[i].set; 296 } 297 298 } 299 300 if (eq("rows") && --argc) 301 winsize->ws_row = atoi(*++argv); 302 else if ((eq("columns") || eq("cols")) && --argc) 303 winsize->ws_col = atoi(*++argv); 304 else if (eq("xpixels") && --argc) 305 winsize->ws_xpixel = atoi(*++argv); 306 else if (eq("ypixels") && --argc) 307 winsize->ws_ypixel = atoi(*++argv); 308 309 if (!match) { 310 #ifdef EUC 311 if (!parse_encoded(cb, kcswp, term)) { 312 #else 313 if (!parse_encoded(cb)) { 314 #endif /* EUC */ 315 return (s_arg); /* parsing failed */ 316 } 317 } 318 } 319 return ((char *)0); 320 } 321 322 static int 323 eq(const char *string) 324 { 325 int i; 326 327 if (!s_arg) 328 return (0); 329 i = 0; 330 loop: 331 if (s_arg[i] != string[i]) 332 return (0); 333 if (s_arg[i++] != '\0') 334 goto loop; 335 match++; 336 return (1); 337 } 338 339 /* get pseudo control characters from terminal */ 340 /* and convert to internal representation */ 341 static int 342 gct(char *cp, int term) 343 { 344 int c; 345 346 c = *cp; 347 if (c == '^') { 348 c = *++cp; 349 if (c == '?') 350 c = 0177; /* map '^?' to 0177 */ 351 else if (c == '-') { 352 /* map '^-' to undefined */ 353 c = (term & TERMIOS) ? _POSIX_VDISABLE : 0200; 354 } else 355 c &= 037; 356 } else if (strcmp(cp, "undef") == 0) { 357 /* map "undef" to undefined */ 358 c = (term & TERMIOS) ? _POSIX_VDISABLE : 0200; 359 } 360 return (c); 361 } 362 363 /* get modes of tty device and fill in applicable structures */ 364 int 365 get_ttymode(int fd, struct termio *termio, struct termios *termios, 366 struct stio *stermio, struct termiox *termiox, struct winsize *winsize 367 #ifdef EUC 368 /* */, struct eucioc *kwp, ldterm_cs_data_user_t *kcswp 369 #endif /* EUC */ 370 /* */) 371 { 372 int i; 373 int term = 0; 374 #ifdef EUC 375 struct strioctl cmd; 376 #endif /* EUC */ 377 if (ioctl(fd, STGET, stermio) == -1) { 378 term |= ASYNC; 379 if (ioctl(fd, TCGETS, termios) == -1) { 380 if (ioctl(fd, TCGETA, termio) == -1) 381 return (-1); 382 termios->c_lflag = termio->c_lflag; 383 termios->c_oflag = termio->c_oflag; 384 termios->c_iflag = termio->c_iflag; 385 termios->c_cflag = termio->c_cflag; 386 for (i = 0; i < NCC; i++) 387 termios->c_cc[i] = termio->c_cc[i]; 388 } else 389 term |= TERMIOS; 390 } else { 391 termios->c_cc[7] = (unsigned)stermio->tab; 392 termios->c_lflag = stermio->lmode; 393 termios->c_oflag = stermio->omode; 394 termios->c_iflag = stermio->imode; 395 } 396 397 if (ioctl(fd, TCGETX, termiox) == 0) 398 term |= FLOW; 399 400 if (ioctl(fd, TIOCGWINSZ, winsize) == 0) 401 term |= WINDOW; 402 #ifdef EUC 403 cmd.ic_cmd = EUC_WGET; 404 cmd.ic_timout = 0; 405 cmd.ic_len = sizeof (struct eucioc); 406 cmd.ic_dp = (char *)kwp; 407 408 if (ioctl(fd, I_STR, &cmd) == 0) 409 term |= EUCW; 410 411 cmd.ic_cmd = CSDATA_GET; 412 cmd.ic_timout = 0; 413 cmd.ic_len = sizeof (ldterm_cs_data_user_t); 414 cmd.ic_dp = (char *)kcswp; 415 416 if (ioctl(fd, I_STR, &cmd) == 0) 417 term |= CSIW; 418 else 419 (void) memset((void *)kcswp, 0, sizeof (ldterm_cs_data_user_t)); 420 #endif /* EUC */ 421 return (term); 422 } 423 424 /* set tty modes */ 425 int 426 set_ttymode(int fd, int term, struct termio *termio, struct termios *termios, 427 struct stio *stermio, struct termiox *termiox, struct winsize *winsize, 428 struct winsize *owinsize 429 #ifdef EUC 430 /* */, struct eucioc *kwp, ldterm_cs_data_user_t *kcswp, 431 int invalid_ldterm_dat_file 432 #endif /* EUC */ 433 /* */) 434 { 435 int i; 436 #ifdef EUC 437 struct strioctl cmd; 438 #endif /* EUC */ 439 440 if (term & ASYNC) { 441 if (term & TERMIOS) { 442 if (ioctl(fd, TCSETSW, termios) == -1) 443 return (-1); 444 } else { 445 termio->c_lflag = termios->c_lflag; 446 termio->c_oflag = termios->c_oflag; 447 termio->c_iflag = termios->c_iflag; 448 termio->c_cflag = termios->c_cflag; 449 for (i = 0; i < NCC; i++) 450 termio->c_cc[i] = termios->c_cc[i]; 451 if (ioctl(fd, TCSETAW, termio) == -1) 452 return (-1); 453 } 454 455 } else { 456 stermio->imode = termios->c_iflag; 457 stermio->omode = termios->c_oflag; 458 stermio->lmode = termios->c_lflag; 459 stermio->tab = termios->c_cc[7]; 460 if (ioctl(fd, STSET, stermio) == -1) 461 return (-1); 462 } 463 if (term & FLOW) { 464 if (ioctl(fd, TCSETXW, termiox) == -1) 465 return (-1); 466 } 467 if ((owinsize->ws_col != winsize->ws_col || 468 owinsize->ws_row != winsize->ws_row || 469 owinsize->ws_xpixel != winsize->ws_xpixel || 470 owinsize->ws_ypixel != winsize->ws_ypixel) && 471 ioctl(0, TIOCSWINSZ, winsize) != 0) 472 return (-1); 473 #ifdef EUC 474 /* 475 * If the ldterm.dat file contains valid, non-EUC codeset info, 476 * send downstream CSDATA_SET. Otherwise, try EUC_WSET. 477 */ 478 if (invalid_ldterm_dat_file) { 479 (void) fprintf(stderr, gettext( 480 "stty: can't set codeset width due to invalid ldterm.dat.\n")); 481 return (-1); 482 } else if ((term & CSIW) && kcswp->version) { 483 cmd.ic_cmd = CSDATA_SET; 484 cmd.ic_timout = 0; 485 cmd.ic_len = sizeof (ldterm_cs_data_user_t); 486 cmd.ic_dp = (char *)kcswp; 487 if (ioctl(fd, I_STR, &cmd) != 0) { 488 (void) fprintf(stderr, gettext( 489 "stty: can't set codeset width.\n")); 490 return (-1); 491 } 492 } else if (term & EUCW) { 493 cmd.ic_cmd = EUC_WSET; 494 cmd.ic_timout = 0; 495 cmd.ic_len = sizeof (struct eucioc); 496 cmd.ic_dp = (char *)kwp; 497 if (ioctl(fd, I_STR, &cmd) != 0) { 498 (void) fprintf(stderr, gettext( 499 "stty: can't set EUC codeset width.\n")); 500 return (-1); 501 } 502 } 503 #endif /* EUC */ 504 return (0); 505 } 506 507 static int 508 parse_encoded(struct termios *cb 509 #ifdef EUC 510 /* */, ldterm_cs_data_user_t *kcswp, int term 511 #endif /* EUC */ 512 /* */) 513 { 514 unsigned long grab[NUM_FIELDS]; 515 int last, i; 516 #ifdef EUC 517 long l; 518 char s[3]; 519 char *t; 520 char *r; 521 uchar_t *g; 522 ldterm_cs_data_user_t ecswp; 523 #endif /* EUC */ 524 525 /* 526 * Although there are only 16 control chars defined as of April 1995, 527 * parse_encoded() and prencode() will not have to be changed if up to 528 * MAX_CC control chars are defined in the future. 529 * Scan the fields of "stty -g" output into the grab array. 530 * Set a total of NUM_FIELDS fields (NUM_MODES modes + MAX_CC 531 * control chars). 532 */ 533 i = sscanf(s_arg, "%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:" 534 "%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx", 535 &grab[0], &grab[1], &grab[2], &grab[3], &grab[4], &grab[5], 536 &grab[6], &grab[7], &grab[8], &grab[9], &grab[10], &grab[11], 537 &grab[12], &grab[13], &grab[14], &grab[15], &grab[16], &grab[17], 538 &grab[18], &grab[19], &grab[20], &grab[21]); 539 540 if (i < 12) 541 return (0); 542 cb->c_iflag = grab[0]; 543 cb->c_oflag = grab[1]; 544 cb->c_cflag = grab[2]; 545 cb->c_lflag = grab[3]; 546 547 last = i - NUM_MODES; 548 for (i = 0; i < last; i++) 549 cb->c_cc[i] = (unsigned char) grab[i+NUM_MODES]; 550 551 #ifdef EUC 552 /* This is to fulfill PSARC/1999/140 TCR2. */ 553 if (term & CSIW) { 554 r = strdup(s_arg); 555 if (r == (char *)NULL) { 556 (void) fprintf(stderr, gettext( 557 "no more memory - try again later\n")); 558 return (0); 559 } 560 t = strtok(r, ":"); 561 for (i = 0; t != NULL && i < 22; i++) { 562 t = strtok(NULL, ":"); 563 } 564 565 if (t == NULL) { 566 free((void *)r); 567 return (0); 568 } 569 ecswp.version = (uchar_t)strtol(t, (char **)NULL, 16); 570 if (ecswp.version > LDTERM_DATA_VERSION || 571 ecswp.version == 0) { 572 free((void *)r); 573 return (0); 574 } 575 576 if ((t = strtok(NULL, ":")) == NULL) { 577 free((void *)r); 578 return (0); 579 } 580 ecswp.codeset_type = (uchar_t)strtol(t, (char **)NULL, 16); 581 if (ecswp.codeset_type < LDTERM_CS_TYPE_MIN || 582 ecswp.codeset_type > LDTERM_CS_TYPE_MAX) { 583 free((void *)r); 584 return (0); 585 } 586 587 if ((t = strtok(NULL, ":")) == NULL) { 588 free((void *)r); 589 return (0); 590 } 591 ecswp.csinfo_num = (uchar_t)strtol(t, (char **)NULL, 16); 592 if ((ecswp.codeset_type == LDTERM_CS_TYPE_EUC && 593 ecswp.csinfo_num > 3) || 594 (ecswp.codeset_type == LDTERM_CS_TYPE_PCCS && 595 (ecswp.csinfo_num < 1 || ecswp.csinfo_num > 10))) { 596 free((void *)r); 597 return (0); 598 } 599 600 if ((t = strtok(NULL, ":")) == NULL) { 601 free((void *)r); 602 return (0); 603 } 604 s[2] = '\0'; 605 for (i = 0; *t != 0 && i < MAXNAMELEN; i++) { 606 if (*(t + 1) == (char)NULL) { 607 free((void *)r); 608 return (0); 609 } 610 s[0] = *t++; 611 s[1] = *t++; 612 ecswp.locale_name[i] = (char)strtol(s, (char **)NULL, 613 16); 614 } 615 if (i >= MAXNAMELEN) { 616 free((void *)r); 617 return (0); 618 } 619 ecswp.locale_name[i] = '\0'; 620 621 g = (uchar_t *)ecswp.eucpc_data; 622 for (i = 0; i < (LDTERM_CS_MAX_CODESETS * 4); i++) { 623 if ((t = strtok(NULL, ":")) == NULL) { 624 free((void *)r); 625 return (0); 626 } 627 l = strtol(t, (char **)NULL, 16); 628 if (l < 0 || l > 255) { 629 free((void *)r); 630 return (0); 631 } 632 *g++ = (uchar_t)l; 633 } 634 635 /* We got the 'ecswp' all filled up now; let's copy. */ 636 (void) memcpy((void *)kcswp, (const void *)&ecswp, 637 sizeof (ldterm_cs_data_user_t)); 638 } 639 #endif /* EUC */ 640 641 return (1); 642 } 643