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