1 /**************************************************************************** 2 * Copyright (c) 2016-2017,2019 Free Software Foundation, Inc. * 3 * * 4 * Permission is hereby granted, free of charge, to any person obtaining a * 5 * copy of this software and associated documentation files (the * 6 * "Software"), to deal in the Software without restriction, including * 7 * without limitation the rights to use, copy, modify, merge, publish, * 8 * distribute, distribute with modifications, sublicense, and/or sell * 9 * copies of the Software, and to permit persons to whom the Software is * 10 * furnished to do so, subject to the following conditions: * 11 * * 12 * The above copyright notice and this permission notice shall be included * 13 * in all copies or substantial portions of the Software. * 14 * * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 22 * * 23 * Except as contained in this notice, the name(s) of the above copyright * 24 * holders shall not be used in advertising or otherwise to promote the * 25 * sale, use or other dealings in this Software without prior written * 26 * authorization. * 27 ****************************************************************************/ 28 29 /**************************************************************************** 30 * Author: Thomas E. Dickey * 31 ****************************************************************************/ 32 33 #include <reset_cmd.h> 34 #include <tty_settings.h> 35 36 #include <errno.h> 37 #include <stdio.h> 38 #include <fcntl.h> 39 40 #if HAVE_SIZECHANGE 41 # if !defined(sun) || !TERMIOS 42 # if HAVE_SYS_IOCTL_H 43 # include <sys/ioctl.h> 44 # endif 45 # endif 46 #endif 47 48 #if NEED_PTEM_H 49 /* they neglected to define struct winsize in termios.h -- it's only 50 in termio.h */ 51 #include <sys/stream.h> 52 #include <sys/ptem.h> 53 #endif 54 55 MODULE_ID("$Id: reset_cmd.c,v 1.18 2019/07/13 21:35:13 tom Exp $") 56 57 /* 58 * SCO defines TIOCGSIZE and the corresponding struct. Other systems (SunOS, 59 * Solaris, IRIX) define TIOCGWINSZ and struct winsize. 60 */ 61 #ifdef TIOCGSIZE 62 # define IOCTL_GET_WINSIZE TIOCGSIZE 63 # define IOCTL_SET_WINSIZE TIOCSSIZE 64 # define STRUCT_WINSIZE struct ttysize 65 # define WINSIZE_ROWS(n) n.ts_lines 66 # define WINSIZE_COLS(n) n.ts_cols 67 #else 68 # ifdef TIOCGWINSZ 69 # define IOCTL_GET_WINSIZE TIOCGWINSZ 70 # define IOCTL_SET_WINSIZE TIOCSWINSZ 71 # define STRUCT_WINSIZE struct winsize 72 # define WINSIZE_ROWS(n) n.ws_row 73 # define WINSIZE_COLS(n) n.ws_col 74 # endif 75 #endif 76 77 static FILE *my_file; 78 79 static bool use_reset = FALSE; /* invoked as reset */ 80 static bool use_init = FALSE; /* invoked as init */ 81 82 static void 83 failed(const char *msg) 84 { 85 int code = errno; 86 87 (void) fprintf(stderr, "%s: %s: %s\n", _nc_progname, msg, strerror(code)); 88 restore_tty_settings(); 89 (void) fprintf(my_file, "\n"); 90 fflush(my_file); 91 ExitProgram(ErrSystem(code)); 92 /* NOTREACHED */ 93 } 94 95 static bool 96 cat_file(char *file) 97 { 98 FILE *fp; 99 size_t nr; 100 char buf[BUFSIZ]; 101 bool sent = FALSE; 102 103 if (file != 0) { 104 if ((fp = fopen(file, "r")) == 0) 105 failed(file); 106 107 while ((nr = fread(buf, sizeof(char), sizeof(buf), fp)) != 0) { 108 if (fwrite(buf, sizeof(char), nr, my_file) != nr) { 109 failed(file); 110 } 111 sent = TRUE; 112 } 113 fclose(fp); 114 } 115 return sent; 116 } 117 118 static int 119 out_char(int c) 120 { 121 return putc(c, my_file); 122 } 123 124 /************************************************************************** 125 * Mode-setting logic 126 **************************************************************************/ 127 128 /* some BSD systems have these built in, some systems are missing 129 * one or more definitions. The safest solution is to override unless the 130 * commonly-altered ones are defined. 131 */ 132 #if !(defined(CERASE) && defined(CINTR) && defined(CKILL) && defined(CQUIT)) 133 #undef CEOF 134 #undef CERASE 135 #undef CINTR 136 #undef CKILL 137 #undef CLNEXT 138 #undef CRPRNT 139 #undef CQUIT 140 #undef CSTART 141 #undef CSTOP 142 #undef CSUSP 143 #endif 144 145 /* control-character defaults */ 146 #ifndef CEOF 147 #define CEOF CTRL('D') 148 #endif 149 #ifndef CERASE 150 #define CERASE CTRL('H') 151 #endif 152 #ifndef CINTR 153 #define CINTR 127 /* ^? */ 154 #endif 155 #ifndef CKILL 156 #define CKILL CTRL('U') 157 #endif 158 #ifndef CLNEXT 159 #define CLNEXT CTRL('v') 160 #endif 161 #ifndef CRPRNT 162 #define CRPRNT CTRL('r') 163 #endif 164 #ifndef CQUIT 165 #define CQUIT CTRL('\\') 166 #endif 167 #ifndef CSTART 168 #define CSTART CTRL('Q') 169 #endif 170 #ifndef CSTOP 171 #define CSTOP CTRL('S') 172 #endif 173 #ifndef CSUSP 174 #define CSUSP CTRL('Z') 175 #endif 176 177 #if defined(_POSIX_VDISABLE) 178 #define DISABLED(val) (((_POSIX_VDISABLE != -1) \ 179 && ((val) == _POSIX_VDISABLE)) \ 180 || ((val) <= 0)) 181 #else 182 #define DISABLED(val) ((int)(val) <= 0) 183 #endif 184 185 #define CHK(val, dft) (unsigned char) (DISABLED(val) ? dft : val) 186 187 #define reset_char(item, value) \ 188 tty_settings->c_cc[item] = CHK(tty_settings->c_cc[item], value) 189 190 /* 191 * Reset the terminal mode bits to a sensible state. Very useful after 192 * a child program dies in raw mode. 193 */ 194 void 195 reset_tty_settings(int fd, TTY * tty_settings) 196 { 197 GET_TTY(fd, tty_settings); 198 199 #ifdef TERMIOS 200 #if defined(VDISCARD) && defined(CDISCARD) 201 reset_char(VDISCARD, CDISCARD); 202 #endif 203 reset_char(VEOF, CEOF); 204 reset_char(VERASE, CERASE); 205 #if defined(VFLUSH) && defined(CFLUSH) 206 reset_char(VFLUSH, CFLUSH); 207 #endif 208 reset_char(VINTR, CINTR); 209 reset_char(VKILL, CKILL); 210 #if defined(VLNEXT) && defined(CLNEXT) 211 reset_char(VLNEXT, CLNEXT); 212 #endif 213 reset_char(VQUIT, CQUIT); 214 #if defined(VREPRINT) && defined(CRPRNT) 215 reset_char(VREPRINT, CRPRNT); 216 #endif 217 #if defined(VSTART) && defined(CSTART) 218 reset_char(VSTART, CSTART); 219 #endif 220 #if defined(VSTOP) && defined(CSTOP) 221 reset_char(VSTOP, CSTOP); 222 #endif 223 #if defined(VSUSP) && defined(CSUSP) 224 reset_char(VSUSP, CSUSP); 225 #endif 226 #if defined(VWERASE) && defined(CWERASE) 227 reset_char(VWERASE, CWERASE); 228 #endif 229 230 tty_settings->c_iflag &= ~((unsigned) (IGNBRK 231 | PARMRK 232 | INPCK 233 | ISTRIP 234 | INLCR 235 | IGNCR 236 #ifdef IUCLC 237 | IUCLC 238 #endif 239 #ifdef IXANY 240 | IXANY 241 #endif 242 | IXOFF)); 243 244 tty_settings->c_iflag |= (BRKINT 245 | IGNPAR 246 | ICRNL 247 | IXON 248 #ifdef IMAXBEL 249 | IMAXBEL 250 #endif 251 ); 252 253 tty_settings->c_oflag &= ~((unsigned) (0 254 #ifdef OLCUC 255 | OLCUC 256 #endif 257 #ifdef OCRNL 258 | OCRNL 259 #endif 260 #ifdef ONOCR 261 | ONOCR 262 #endif 263 #ifdef ONLRET 264 | ONLRET 265 #endif 266 #ifdef OFILL 267 | OFILL 268 #endif 269 #ifdef OFDEL 270 | OFDEL 271 #endif 272 #ifdef NLDLY 273 | NLDLY 274 #endif 275 #ifdef CRDLY 276 | CRDLY 277 #endif 278 #ifdef TABDLY 279 | TABDLY 280 #endif 281 #ifdef BSDLY 282 | BSDLY 283 #endif 284 #ifdef VTDLY 285 | VTDLY 286 #endif 287 #ifdef FFDLY 288 | FFDLY 289 #endif 290 )); 291 292 tty_settings->c_oflag |= (OPOST 293 #ifdef ONLCR 294 | ONLCR 295 #endif 296 ); 297 298 tty_settings->c_cflag &= ~((unsigned) (CSIZE 299 | CSTOPB 300 | PARENB 301 | PARODD 302 | CLOCAL)); 303 tty_settings->c_cflag |= (CS8 | CREAD); 304 tty_settings->c_lflag &= ~((unsigned) (ECHONL 305 | NOFLSH 306 #ifdef TOSTOP 307 | TOSTOP 308 #endif 309 #ifdef ECHOPTR 310 | ECHOPRT 311 #endif 312 #ifdef XCASE 313 | XCASE 314 #endif 315 )); 316 317 tty_settings->c_lflag |= (ISIG 318 | ICANON 319 | ECHO 320 | ECHOE 321 | ECHOK 322 #ifdef ECHOCTL 323 | ECHOCTL 324 #endif 325 #ifdef ECHOKE 326 | ECHOKE 327 #endif 328 ); 329 #endif 330 331 SET_TTY(fd, tty_settings); 332 } 333 334 /* 335 * Returns a "good" value for the erase character. This is loosely based on 336 * the BSD4.4 logic. 337 */ 338 static int 339 default_erase(void) 340 { 341 int result; 342 343 if (over_strike 344 && VALID_STRING(key_backspace) 345 && strlen(key_backspace) == 1) { 346 result = key_backspace[0]; 347 } else { 348 result = CERASE; 349 } 350 351 return result; 352 } 353 354 /* 355 * Update the values of the erase, interrupt, and kill characters in the TTY 356 * parameter. 357 * 358 * SVr4 tset (e.g., Solaris 2.5) only modifies the intr, quit or erase 359 * characters if they're unset, or if we specify them as options. This differs 360 * from BSD 4.4 tset, which always sets erase. 361 */ 362 void 363 set_control_chars(TTY * tty_settings, int my_erase, int my_intr, int my_kill) 364 { 365 if (DISABLED(tty_settings->c_cc[VERASE]) || my_erase >= 0) { 366 tty_settings->c_cc[VERASE] = UChar((my_erase >= 0) 367 ? my_erase 368 : default_erase()); 369 } 370 371 if (DISABLED(tty_settings->c_cc[VINTR]) || my_intr >= 0) { 372 tty_settings->c_cc[VINTR] = UChar((my_intr >= 0) 373 ? my_intr 374 : CINTR); 375 } 376 377 if (DISABLED(tty_settings->c_cc[VKILL]) || my_kill >= 0) { 378 tty_settings->c_cc[VKILL] = UChar((my_kill >= 0) 379 ? my_kill 380 : CKILL); 381 } 382 } 383 384 /* 385 * Set up various conversions in the TTY parameter, including parity, tabs, 386 * returns, echo, and case, according to the termcap entry. 387 */ 388 void 389 set_conversions(TTY * tty_settings) 390 { 391 #ifdef ONLCR 392 tty_settings->c_oflag |= ONLCR; 393 #endif 394 tty_settings->c_iflag |= ICRNL; 395 tty_settings->c_lflag |= ECHO; 396 #ifdef OXTABS 397 tty_settings->c_oflag |= OXTABS; 398 #endif /* OXTABS */ 399 400 /* test used to be tgetflag("NL") */ 401 if (VALID_STRING(newline) && newline[0] == '\n' && !newline[1]) { 402 /* Newline, not linefeed. */ 403 #ifdef ONLCR 404 tty_settings->c_oflag &= ~((unsigned) ONLCR); 405 #endif 406 tty_settings->c_iflag &= ~((unsigned) ICRNL); 407 } 408 #ifdef OXTABS 409 /* test used to be tgetflag("pt") */ 410 if (VALID_STRING(set_tab) && VALID_STRING(clear_all_tabs)) 411 tty_settings->c_oflag &= ~OXTABS; 412 #endif /* OXTABS */ 413 tty_settings->c_lflag |= (ECHOE | ECHOK); 414 } 415 416 static bool 417 sent_string(const char *s) 418 { 419 bool sent = FALSE; 420 if (VALID_STRING(s)) { 421 tputs(s, 0, out_char); 422 sent = TRUE; 423 } 424 return sent; 425 } 426 427 static bool 428 to_left_margin(void) 429 { 430 if (VALID_STRING(carriage_return)) { 431 sent_string(carriage_return); 432 } else { 433 out_char('\r'); 434 } 435 return TRUE; 436 } 437 438 /* 439 * Set the hardware tabs on the terminal, using the 'ct' (clear all tabs), 440 * 'st' (set one tab) and 'ch' (horizontal cursor addressing) capabilities. 441 * This is done before 'if' and 'is', so they can recover in case of error. 442 * 443 * Return TRUE if we set any tab stops, FALSE if not. 444 */ 445 static bool 446 reset_tabstops(int wide) 447 { 448 if ((init_tabs != 8) 449 && VALID_NUMERIC(init_tabs) 450 && VALID_STRING(set_tab) 451 && VALID_STRING(clear_all_tabs)) { 452 int c; 453 454 to_left_margin(); 455 tputs(clear_all_tabs, 0, out_char); 456 if (init_tabs > 1) { 457 if (init_tabs > wide) 458 init_tabs = (short) wide; 459 for (c = init_tabs; c < wide; c += init_tabs) { 460 fprintf(my_file, "%*s", init_tabs, " "); 461 tputs(set_tab, 0, out_char); 462 } 463 to_left_margin(); 464 } 465 return (TRUE); 466 } 467 return (FALSE); 468 } 469 470 /* Output startup string. */ 471 bool 472 send_init_strings(int fd GCC_UNUSED, TTY * old_settings) 473 { 474 int i; 475 bool need_flush = FALSE; 476 477 (void) old_settings; 478 #ifdef TAB3 479 if (old_settings != 0 && 480 old_settings->c_oflag & (TAB3 | ONLCR | OCRNL | ONLRET)) { 481 old_settings->c_oflag &= (TAB3 | ONLCR | OCRNL | ONLRET); 482 SET_TTY(fd, old_settings); 483 } 484 #endif 485 if (use_reset || use_init) { 486 if (VALID_STRING(init_prog)) { 487 IGNORE_RC(system(init_prog)); 488 } 489 490 need_flush |= sent_string((use_reset && (reset_1string != 0)) 491 ? reset_1string 492 : init_1string); 493 494 need_flush |= sent_string((use_reset && (reset_2string != 0)) 495 ? reset_2string 496 : init_2string); 497 498 if (VALID_STRING(clear_margins)) { 499 need_flush |= sent_string(clear_margins); 500 } else 501 #if defined(set_lr_margin) 502 if (VALID_STRING(set_lr_margin)) { 503 need_flush |= sent_string(TPARM_2(set_lr_margin, 0, 504 columns - 1)); 505 } else 506 #endif 507 #if defined(set_left_margin_parm) && defined(set_right_margin_parm) 508 if (VALID_STRING(set_left_margin_parm) 509 && VALID_STRING(set_right_margin_parm)) { 510 need_flush |= sent_string(TPARM_1(set_left_margin_parm, 0)); 511 need_flush |= sent_string(TPARM_1(set_right_margin_parm, 512 columns - 1)); 513 } else 514 #endif 515 if (VALID_STRING(set_left_margin) 516 && VALID_STRING(set_right_margin)) { 517 need_flush |= to_left_margin(); 518 need_flush |= sent_string(set_left_margin); 519 if (VALID_STRING(parm_right_cursor)) { 520 need_flush |= sent_string(TPARM_1(parm_right_cursor, 521 columns - 1)); 522 } else { 523 for (i = 0; i < columns - 1; i++) { 524 out_char(' '); 525 need_flush = TRUE; 526 } 527 } 528 need_flush |= sent_string(set_right_margin); 529 need_flush |= to_left_margin(); 530 } 531 532 need_flush |= reset_tabstops(columns); 533 534 need_flush |= cat_file((use_reset && reset_file) ? reset_file : init_file); 535 536 need_flush |= sent_string((use_reset && (reset_3string != 0)) 537 ? reset_3string 538 : init_3string); 539 } 540 541 return need_flush; 542 } 543 544 /* 545 * Tell the user if a control key has been changed from the default value. 546 */ 547 static void 548 show_tty_change(TTY * old_settings, 549 TTY * new_settings, 550 const char *name, 551 int which, 552 unsigned def) 553 { 554 unsigned older, newer; 555 char *p; 556 557 newer = new_settings->c_cc[which]; 558 older = old_settings->c_cc[which]; 559 560 if (older == newer && older == def) 561 return; 562 563 (void) fprintf(stderr, "%s %s ", name, older == newer ? "is" : "set to"); 564 565 if (DISABLED(newer)) { 566 (void) fprintf(stderr, "undef.\n"); 567 /* 568 * Check 'delete' before 'backspace', since the key_backspace value 569 * is ambiguous. 570 */ 571 } else if (newer == 0177) { 572 (void) fprintf(stderr, "delete.\n"); 573 } else if ((p = key_backspace) != 0 574 && newer == (unsigned char) p[0] 575 && p[1] == '\0') { 576 (void) fprintf(stderr, "backspace.\n"); 577 } else if (newer < 040) { 578 newer ^= 0100; 579 (void) fprintf(stderr, "control-%c (^%c).\n", UChar(newer), UChar(newer)); 580 } else 581 (void) fprintf(stderr, "%c.\n", UChar(newer)); 582 } 583 584 /************************************************************************** 585 * Miscellaneous. 586 **************************************************************************/ 587 588 void 589 reset_start(FILE *fp, bool is_reset, bool is_init) 590 { 591 my_file = fp; 592 use_reset = is_reset; 593 use_init = is_init; 594 } 595 596 void 597 reset_flush(void) 598 { 599 if (my_file != 0) 600 fflush(my_file); 601 } 602 603 void 604 print_tty_chars(TTY * old_settings, TTY * new_settings) 605 { 606 show_tty_change(old_settings, new_settings, "Erase", VERASE, CERASE); 607 show_tty_change(old_settings, new_settings, "Kill", VKILL, CKILL); 608 show_tty_change(old_settings, new_settings, "Interrupt", VINTR, CINTR); 609 } 610 611 #if HAVE_SIZECHANGE 612 /* 613 * Set window size if not set already, but update our copy of the values if the 614 * size was set. 615 */ 616 void 617 set_window_size(int fd, short *high, short *wide) 618 { 619 STRUCT_WINSIZE win; 620 (void) ioctl(fd, IOCTL_GET_WINSIZE, &win); 621 if (WINSIZE_ROWS(win) == 0 && 622 WINSIZE_COLS(win) == 0) { 623 if (*high > 0 && *wide > 0) { 624 WINSIZE_ROWS(win) = (unsigned short) *high; 625 WINSIZE_COLS(win) = (unsigned short) *wide; 626 (void) ioctl(fd, IOCTL_SET_WINSIZE, &win); 627 } 628 } else if (WINSIZE_ROWS(win) > 0 && 629 WINSIZE_COLS(win) > 0) { 630 *high = (short) WINSIZE_ROWS(win); 631 *wide = (short) WINSIZE_COLS(win); 632 } 633 } 634 #endif 635