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