1/**************************************************************************** 2 * Copyright 2019,2020 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's only 51 in termio.h */ 52#include <sys/stream.h> 53#include <sys/ptem.h> 54#endif 55 56MODULE_ID("$Id: reset_cmd.c,v 1.24 2020/11/21 22:11:10 tom 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 78static FILE *my_file; 79 80static bool use_reset = FALSE; /* invoked as reset */ 81static bool use_init = FALSE; /* invoked as init */ 82 83static void 84failed(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 96static bool 97cat_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 = 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 119static int 120out_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 */ 195void 196reset_tty_settings(int fd, TTY * tty_settings) 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(VFLUSH) && defined(CFLUSH) 207 reset_char(VFLUSH, CFLUSH); 208#endif 209 reset_char(VINTR, CINTR); 210 reset_char(VKILL, CKILL); 211#if defined(VLNEXT) && defined(CLNEXT) 212 reset_char(VLNEXT, CLNEXT); 213#endif 214 reset_char(VQUIT, CQUIT); 215#if defined(VREPRINT) && defined(CRPRNT) 216 reset_char(VREPRINT, CRPRNT); 217#endif 218#if defined(VSTART) && defined(CSTART) 219 reset_char(VSTART, CSTART); 220#endif 221#if defined(VSTOP) && defined(CSTOP) 222 reset_char(VSTOP, CSTOP); 223#endif 224#if defined(VSUSP) && defined(CSUSP) 225 reset_char(VSUSP, CSUSP); 226#endif 227#if defined(VWERASE) && defined(CWERASE) 228 reset_char(VWERASE, CWERASE); 229#endif 230 231 tty_settings->c_iflag &= ~((unsigned) (IGNBRK 232 | PARMRK 233 | INPCK 234 | ISTRIP 235 | INLCR 236 | IGNCR 237#ifdef IUCLC 238 | IUCLC 239#endif 240#ifdef IXANY 241 | IXANY 242#endif 243 | IXOFF)); 244 245 tty_settings->c_iflag |= (BRKINT 246 | IGNPAR 247 | ICRNL 248 | IXON 249#ifdef IMAXBEL 250 | IMAXBEL 251#endif 252 ); 253 254 tty_settings->c_oflag &= ~((unsigned) (0 255#ifdef OLCUC 256 | OLCUC 257#endif 258#ifdef OCRNL 259 | OCRNL 260#endif 261#ifdef ONOCR 262 | ONOCR 263#endif 264#ifdef ONLRET 265 | ONLRET 266#endif 267#ifdef OFILL 268 | OFILL 269#endif 270#ifdef OFDEL 271 | OFDEL 272#endif 273#ifdef NLDLY 274 | NLDLY 275#endif 276#ifdef CRDLY 277 | CRDLY 278#endif 279#ifdef TABDLY 280 | TABDLY 281#endif 282#ifdef BSDLY 283 | BSDLY 284#endif 285#ifdef VTDLY 286 | VTDLY 287#endif 288#ifdef FFDLY 289 | FFDLY 290#endif 291 )); 292 293 tty_settings->c_oflag |= (OPOST 294#ifdef ONLCR 295 | ONLCR 296#endif 297 ); 298 299 tty_settings->c_cflag &= ~((unsigned) (CSIZE 300 | CSTOPB 301 | PARENB 302 | PARODD 303 | CLOCAL)); 304 tty_settings->c_cflag |= (CS8 | CREAD); 305 tty_settings->c_lflag &= ~((unsigned) (ECHONL 306 | NOFLSH 307#ifdef TOSTOP 308 | TOSTOP 309#endif 310#ifdef ECHOPTR 311 | ECHOPRT 312#endif 313#ifdef XCASE 314 | XCASE 315#endif 316 )); 317 318 tty_settings->c_lflag |= (ISIG 319 | ICANON 320 | ECHO 321 | ECHOE 322 | ECHOK 323#ifdef ECHOCTL 324 | ECHOCTL 325#endif 326#ifdef ECHOKE 327 | ECHOKE 328#endif 329 ); 330#endif 331 332 SET_TTY(fd, tty_settings); 333} 334 335/* 336 * Returns a "good" value for the erase character. This is loosely based on 337 * the BSD4.4 logic. 338 */ 339static int 340default_erase(void) 341{ 342 int result; 343 344 if (over_strike 345 && VALID_STRING(key_backspace) 346 && strlen(key_backspace) == 1) { 347 result = key_backspace[0]; 348 } else { 349 result = CERASE; 350 } 351 352 return result; 353} 354 355/* 356 * Update the values of the erase, interrupt, and kill characters in the TTY 357 * parameter. 358 * 359 * SVr4 tset (e.g., Solaris 2.5) only modifies the intr, quit or erase 360 * characters if they're unset, or if we specify them as options. This differs 361 * from BSD 4.4 tset, which always sets erase. 362 */ 363void 364set_control_chars(TTY * tty_settings, int my_erase, int my_intr, int my_kill) 365{ 366#if defined(EXP_WIN32_DRIVER) 367 /* noop */ 368 (void) tty_settings; 369 (void) my_erase; 370 (void) my_intr; 371 (void) my_kill; 372#else 373 if (DISABLED(tty_settings->c_cc[VERASE]) || my_erase >= 0) { 374 tty_settings->c_cc[VERASE] = UChar((my_erase >= 0) 375 ? my_erase 376 : default_erase()); 377 } 378 379 if (DISABLED(tty_settings->c_cc[VINTR]) || my_intr >= 0) { 380 tty_settings->c_cc[VINTR] = UChar((my_intr >= 0) 381 ? my_intr 382 : CINTR); 383 } 384 385 if (DISABLED(tty_settings->c_cc[VKILL]) || my_kill >= 0) { 386 tty_settings->c_cc[VKILL] = UChar((my_kill >= 0) 387 ? my_kill 388 : CKILL); 389 } 390#endif 391} 392 393/* 394 * Set up various conversions in the TTY parameter, including parity, tabs, 395 * returns, echo, and case, according to the termcap entry. 396 */ 397void 398set_conversions(TTY * tty_settings) 399{ 400#if defined(EXP_WIN32_DRIVER) 401 /* FIXME */ 402#else 403#ifdef ONLCR 404 tty_settings->c_oflag |= ONLCR; 405#endif 406 tty_settings->c_iflag |= ICRNL; 407 tty_settings->c_lflag |= ECHO; 408#ifdef OXTABS 409 tty_settings->c_oflag |= OXTABS; 410#endif /* OXTABS */ 411 412 /* test used to be tgetflag("NL") */ 413 if (VALID_STRING(newline) && newline[0] == '\n' && !newline[1]) { 414 /* Newline, not linefeed. */ 415#ifdef ONLCR 416 tty_settings->c_oflag &= ~((unsigned) ONLCR); 417#endif 418 tty_settings->c_iflag &= ~((unsigned) ICRNL); 419 } 420#ifdef OXTABS 421 /* test used to be tgetflag("pt") */ 422 if (VALID_STRING(set_tab) && VALID_STRING(clear_all_tabs)) 423 tty_settings->c_oflag &= ~OXTABS; 424#endif /* OXTABS */ 425 tty_settings->c_lflag |= (ECHOE | ECHOK); 426#endif 427} 428 429static bool 430sent_string(const char *s) 431{ 432 bool sent = FALSE; 433 if (VALID_STRING(s)) { 434 tputs(s, 0, out_char); 435 sent = TRUE; 436 } 437 return sent; 438} 439 440static bool 441to_left_margin(void) 442{ 443 if (VALID_STRING(carriage_return)) { 444 sent_string(carriage_return); 445 } else { 446 out_char('\r'); 447 } 448 return TRUE; 449} 450 451/* 452 * Set the hardware tabs on the terminal, using the 'ct' (clear all tabs), 453 * 'st' (set one tab) and 'ch' (horizontal cursor addressing) capabilities. 454 * This is done before 'if' and 'is', so they can recover in case of error. 455 * 456 * Return TRUE if we set any tab stops, FALSE if not. 457 */ 458static bool 459reset_tabstops(int wide) 460{ 461 if ((init_tabs != 8) 462 && VALID_NUMERIC(init_tabs) 463 && VALID_STRING(set_tab) 464 && VALID_STRING(clear_all_tabs)) { 465 int c; 466 467 to_left_margin(); 468 tputs(clear_all_tabs, 0, out_char); 469 if (init_tabs > 1) { 470 if (init_tabs > wide) 471 init_tabs = (short) wide; 472 for (c = init_tabs; c < wide; c += init_tabs) { 473 fprintf(my_file, "%*s", init_tabs, " "); 474 tputs(set_tab, 0, out_char); 475 } 476 to_left_margin(); 477 } 478 return (TRUE); 479 } 480 return (FALSE); 481} 482 483/* Output startup string. */ 484bool 485send_init_strings(int fd GCC_UNUSED, TTY * old_settings) 486{ 487 int i; 488 bool need_flush = FALSE; 489 490 (void) old_settings; 491#ifdef TAB3 492 if (old_settings != 0 && 493 old_settings->c_oflag & (TAB3 | ONLCR | OCRNL | ONLRET)) { 494 old_settings->c_oflag &= (TAB3 | ONLCR | OCRNL | ONLRET); 495 SET_TTY(fd, old_settings); 496 } 497#endif 498 if (use_reset || use_init) { 499 if (VALID_STRING(init_prog)) { 500 IGNORE_RC(system(init_prog)); 501 } 502 503 need_flush |= sent_string((use_reset && (reset_1string != 0)) 504 ? reset_1string 505 : init_1string); 506 507 need_flush |= sent_string((use_reset && (reset_2string != 0)) 508 ? reset_2string 509 : init_2string); 510 511 if (VALID_STRING(clear_margins)) { 512 need_flush |= sent_string(clear_margins); 513 } else 514#if defined(set_lr_margin) 515 if (VALID_STRING(set_lr_margin)) { 516 need_flush |= sent_string(TIPARM_2(set_lr_margin, 0, columns - 1)); 517 } else 518#endif 519#if defined(set_left_margin_parm) && defined(set_right_margin_parm) 520 if (VALID_STRING(set_left_margin_parm) 521 && VALID_STRING(set_right_margin_parm)) { 522 need_flush |= sent_string(TIPARM_1(set_left_margin_parm, 0)); 523 need_flush |= sent_string(TIPARM_1(set_right_margin_parm, 524 columns - 1)); 525 } else 526#endif 527 if (VALID_STRING(set_left_margin) 528 && VALID_STRING(set_right_margin)) { 529 need_flush |= to_left_margin(); 530 need_flush |= sent_string(set_left_margin); 531 if (VALID_STRING(parm_right_cursor)) { 532 need_flush |= sent_string(TIPARM_1(parm_right_cursor, 533 columns - 1)); 534 } else { 535 for (i = 0; i < columns - 1; i++) { 536 out_char(' '); 537 need_flush = TRUE; 538 } 539 } 540 need_flush |= sent_string(set_right_margin); 541 need_flush |= to_left_margin(); 542 } 543 544 need_flush |= reset_tabstops(columns); 545 546 need_flush |= cat_file((use_reset && reset_file) ? reset_file : init_file); 547 548 need_flush |= sent_string((use_reset && (reset_3string != 0)) 549 ? reset_3string 550 : init_3string); 551 } 552 553 return need_flush; 554} 555 556/* 557 * Tell the user if a control key has been changed from the default value. 558 */ 559static void 560show_tty_change(TTY * old_settings, 561 TTY * new_settings, 562 const char *name, 563 int which, 564 unsigned def) 565{ 566 unsigned older = 0, newer = 0; 567 char *p; 568 569#if defined(EXP_WIN32_DRIVER) 570 /* noop */ 571 (void) old_settings; 572 (void) new_settings; 573 (void) name; 574 (void) which; 575 (void) def; 576#else 577 newer = new_settings->c_cc[which]; 578 older = old_settings->c_cc[which]; 579 580 if (older == newer && older == def) 581 return; 582#endif 583 (void) fprintf(stderr, "%s %s ", name, older == newer ? "is" : "set to"); 584 585 if (DISABLED(newer)) { 586 (void) fprintf(stderr, "undef.\n"); 587 /* 588 * Check 'delete' before 'backspace', since the key_backspace value 589 * is ambiguous. 590 */ 591 } else if (newer == 0177) { 592 (void) fprintf(stderr, "delete.\n"); 593 } else if ((p = key_backspace) != 0 594 && newer == (unsigned char) p[0] 595 && p[1] == '\0') { 596 (void) fprintf(stderr, "backspace.\n"); 597 } else if (newer < 040) { 598 newer ^= 0100; 599 (void) fprintf(stderr, "control-%c (^%c).\n", UChar(newer), UChar(newer)); 600 } else 601 (void) fprintf(stderr, "%c.\n", UChar(newer)); 602} 603 604/************************************************************************** 605 * Miscellaneous. 606 **************************************************************************/ 607 608void 609reset_start(FILE *fp, bool is_reset, bool is_init) 610{ 611 my_file = fp; 612 use_reset = is_reset; 613 use_init = is_init; 614} 615 616void 617reset_flush(void) 618{ 619 if (my_file != 0) 620 fflush(my_file); 621} 622 623void 624print_tty_chars(TTY * old_settings, TTY * new_settings) 625{ 626#if defined(EXP_WIN32_DRIVER) 627 /* noop */ 628#else 629 show_tty_change(old_settings, new_settings, "Erase", VERASE, CERASE); 630 show_tty_change(old_settings, new_settings, "Kill", VKILL, CKILL); 631 show_tty_change(old_settings, new_settings, "Interrupt", VINTR, CINTR); 632#endif 633} 634 635#if HAVE_SIZECHANGE 636/* 637 * Set window size if not set already, but update our copy of the values if the 638 * size was set. 639 */ 640void 641set_window_size(int fd, short *high, short *wide) 642{ 643 STRUCT_WINSIZE win; 644 (void) ioctl(fd, IOCTL_GET_WINSIZE, &win); 645 if (WINSIZE_ROWS(win) == 0 && 646 WINSIZE_COLS(win) == 0) { 647 if (*high > 0 && *wide > 0) { 648 WINSIZE_ROWS(win) = (unsigned short) *high; 649 WINSIZE_COLS(win) = (unsigned short) *wide; 650 (void) ioctl(fd, IOCTL_SET_WINSIZE, &win); 651 } 652 } else if (WINSIZE_ROWS(win) > 0 && 653 WINSIZE_COLS(win) > 0) { 654 *high = (short) WINSIZE_ROWS(win); 655 *wide = (short) WINSIZE_COLS(win); 656 } 657} 658#endif 659