1 /* $XTermId: resize.c,v 1.144 2020/06/03 00:26:23 tom Exp $ */ 2 3 /* 4 * Copyright 2003-2018,2020 by Thomas E. Dickey 5 * 6 * All Rights Reserved 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the 10 * "Software"), to deal in the Software without restriction, including 11 * without limitation the rights to use, copy, modify, merge, publish, 12 * distribute, sublicense, and/or sell copies of the Software, and to 13 * permit persons to whom the Software is furnished to do so, subject to 14 * the following conditions: 15 * 16 * The above copyright notice and this permission notice shall be included 17 * in all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 * 27 * Except as contained in this notice, the name(s) of the above copyright 28 * holders shall not be used in advertising or otherwise to promote the 29 * sale, use or other dealings in this Software without prior written 30 * authorization. 31 * 32 * 33 * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. 34 * 35 * All Rights Reserved 36 * 37 * Permission to use, copy, modify, and distribute this software and its 38 * documentation for any purpose and without fee is hereby granted, 39 * provided that the above copyright notice appear in all copies and that 40 * both that copyright notice and this permission notice appear in 41 * supporting documentation, and that the name of Digital Equipment 42 * Corporation not be used in advertising or publicity pertaining to 43 * distribution of the software without specific, written prior permission. 44 * 45 * 46 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 47 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 48 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 49 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 50 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 51 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 52 * SOFTWARE. 53 */ 54 55 /* resize.c */ 56 57 #include <stdio.h> 58 #include <ctype.h> 59 60 #ifdef RESIZE_ONLY 61 #include "resize.h" 62 #else 63 #include <xterm.h> 64 #include <version.h> 65 #include <xstrings.h> 66 #include <xtermcap.h> 67 #include <xterm_io.h> 68 #endif 69 70 #ifndef USE_TERMINFO /* avoid conflict with configure script */ 71 #if defined(__QNX__) || defined(__SCO__) || defined(linux) || defined(__OpenBSD__) || defined(__UNIXWARE__) 72 #define USE_TERMINFO 73 #endif 74 #endif 75 76 #if defined(__QNX__) 77 #include <unix.h> 78 #endif 79 80 /* 81 * Some OS's may want to use both, like SCO for example. We catch here anyone 82 * who hasn't decided what they want. 83 */ 84 #if !defined(USE_TERMCAP) && !defined(USE_TERMINFO) 85 #define USE_TERMINFO 86 #endif 87 88 #include <signal.h> 89 #include <pwd.h> 90 91 #ifdef USE_IGNORE_RC 92 int ignore_unused; 93 #endif 94 95 #ifdef __MVS__ 96 #define ESCAPE(string) "\047" string 97 #else 98 #define ESCAPE(string) "\033" string 99 #endif 100 101 #define EMULATIONS 2 102 #define SUN 1 103 #define VT100 0 104 105 #define TIMEOUT 10 106 107 #define SHELL_UNKNOWN 0 108 #define SHELL_C 1 109 #define SHELL_BOURNE 2 110 111 /* *INDENT-OFF* */ 112 static struct { 113 const char *name; 114 int type; 115 } shell_list[] = { 116 { "csh", SHELL_C }, /* vanilla cshell */ 117 { "jcsh", SHELL_C }, 118 { "tcsh", SHELL_C }, 119 { "sh", SHELL_BOURNE }, /* vanilla Bourne shell */ 120 { "ash", SHELL_BOURNE }, 121 { "bash", SHELL_BOURNE }, /* GNU Bourne again shell */ 122 { "dash", SHELL_BOURNE }, 123 { "jsh", SHELL_BOURNE }, 124 { "ksh", SHELL_BOURNE }, /* Korn shell (from AT&T toolchest) */ 125 { "ksh-i", SHELL_BOURNE }, /* another name for Korn shell */ 126 { "ksh93", SHELL_BOURNE }, /* Korn shell */ 127 { "mksh", SHELL_BOURNE }, 128 { "pdksh", SHELL_BOURNE }, 129 { "zsh", SHELL_BOURNE }, 130 { NULL, SHELL_BOURNE } /* default (same as xterm's) */ 131 }; 132 /* *INDENT-ON* */ 133 134 static const char *const emuname[EMULATIONS] = 135 { 136 "VT100", 137 "Sun", 138 }; 139 static char *myname; 140 static int shell_type = SHELL_UNKNOWN; 141 static const char *const getsize[EMULATIONS] = 142 { 143 ESCAPE("7") ESCAPE("[r") ESCAPE("[9999;9999H") ESCAPE("[6n"), 144 ESCAPE("[18t"), 145 }; 146 #if defined(USE_STRUCT_WINSIZE) 147 static const char *const getwsize[EMULATIONS] = 148 { /* size in pixels */ 149 0, 150 ESCAPE("[14t"), 151 }; 152 #endif /* USE_STRUCT_WINSIZE */ 153 static const char *const restore[EMULATIONS] = 154 { 155 ESCAPE("8"), 156 0, 157 }; 158 static const char *const setsize[EMULATIONS] = 159 { 160 0, 161 ESCAPE("[8;%s;%st"), 162 }; 163 164 #ifdef USE_ANY_SYSV_TERMIO 165 static struct termio tioorig; 166 #elif defined(USE_TERMIOS) 167 static struct termios tioorig; 168 #else 169 static struct sgttyb sgorig; 170 #endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */ 171 172 static const char *const size[EMULATIONS] = 173 { 174 ESCAPE("[%d;%dR"), 175 ESCAPE("[8;%d;%dt"), 176 }; 177 static const char sunname[] = "sunsize"; 178 static int tty; 179 static FILE *ttyfp; 180 181 #if defined(USE_STRUCT_WINSIZE) 182 static const char *wsize[EMULATIONS] = 183 { 184 0, 185 ESCAPE("[4;%hd;%hdt"), 186 }; 187 #endif /* USE_STRUCT_WINSIZE */ 188 189 static void failed(const char *) GCC_NORETURN; 190 static void onintr(int) GCC_NORETURN; 191 static void resize_timeout(int) GCC_NORETURN; 192 static void Usage(void) GCC_NORETURN; 193 194 static void 195 failed(const char *s) 196 { 197 int save = errno; 198 IGNORE_RC(write(2, myname, strlen(myname))); 199 IGNORE_RC(write(2, ": ", (size_t) 2)); 200 errno = save; 201 perror(s); 202 exit(EXIT_FAILURE); 203 } 204 205 /* ARGSUSED */ 206 static void 207 onintr(int sig GCC_UNUSED) 208 { 209 #ifdef USE_ANY_SYSV_TERMIO 210 (void) ioctl(tty, TCSETAW, &tioorig); 211 #elif defined(USE_TERMIOS) 212 (void) tcsetattr(tty, TCSADRAIN, &tioorig); 213 #else /* not USE_TERMIOS */ 214 (void) ioctl(tty, TIOCSETP, &sgorig); 215 #endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */ 216 exit(EXIT_FAILURE); 217 } 218 219 static void 220 resize_timeout(int sig) 221 { 222 fprintf(stderr, "\n%s: Time out occurred\r\n", myname); 223 onintr(sig); 224 } 225 226 static void 227 Usage(void) 228 { 229 fprintf(stderr, strcmp(myname, sunname) == 0 ? 230 "Usage: %s [rows cols]\n" : 231 "Usage: %s [-v] [-u] [-c] [-s [rows cols]]\n", myname); 232 exit(EXIT_FAILURE); 233 } 234 235 #ifdef USE_TERMCAP 236 static void 237 print_termcap(const char *termcap) 238 { 239 int ch; 240 241 putchar('\''); 242 while ((ch = *termcap++) != '\0') { 243 switch (ch & 0xff) { 244 case 127: /* undo bug in GNU termcap */ 245 printf("^?"); 246 break; 247 case '\'': /* must escape anyway (unlikely) */ 248 /* FALLTHRU */ 249 case '!': /* must escape for SunOS csh */ 250 putchar('\\'); 251 /* FALLTHRU */ 252 default: 253 putchar(ch); 254 break; 255 } 256 } 257 putchar('\''); 258 } 259 #endif /* USE_TERMCAP */ 260 261 static int 262 checkdigits(char *str) 263 { 264 while (*str) { 265 if (!isdigit(CharOf(*str))) 266 return (0); 267 str++; 268 } 269 return (1); 270 } 271 272 static void 273 readstring(FILE *fp, char *buf, const char *str) 274 { 275 int last, c; 276 #if !defined(USG) && !defined(__minix) 277 /* What is the advantage of setitimer() over alarm()? */ 278 struct itimerval it; 279 #endif 280 281 signal(SIGALRM, resize_timeout); 282 #if defined(USG) || defined(__minix) 283 alarm(TIMEOUT); 284 #else 285 memset((char *) &it, 0, sizeof(struct itimerval)); 286 it.it_value.tv_sec = TIMEOUT; 287 setitimer(ITIMER_REAL, &it, (struct itimerval *) NULL); 288 #endif 289 if ((c = getc(fp)) == 0233) { /* meta-escape, CSI */ 290 c = ESCAPE("")[0]; 291 *buf++ = (char) c; 292 *buf++ = '['; 293 } else { 294 *buf++ = (char) c; 295 } 296 if (c != *str) { 297 fprintf(stderr, "%s: unknown character, exiting.\r\n", myname); 298 onintr(0); 299 } 300 last = str[strlen(str) - 1]; 301 while ((*buf++ = (char) getc(fp)) != last) { 302 ; 303 } 304 #if defined(USG) || defined(__minix) 305 alarm(0); 306 #else 307 memset((char *) &it, 0, sizeof(struct itimerval)); 308 setitimer(ITIMER_REAL, &it, (struct itimerval *) NULL); 309 #endif 310 *buf = 0; 311 } 312 313 /* 314 resets termcap string to reflect current screen size 315 */ 316 int 317 main(int argc, char **argv ENVP_ARG) 318 { 319 #ifdef USE_TERMCAP 320 char *env; 321 #endif 322 char *ptr; 323 int emu = VT100; 324 char *shell; 325 int i; 326 int rc; 327 int rows, cols; 328 #ifdef USE_ANY_SYSV_TERMIO 329 struct termio tio; 330 #elif defined(USE_TERMIOS) 331 struct termios tio; 332 #else 333 struct sgttyb sg; 334 #endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */ 335 #ifdef USE_TERMCAP 336 int ok_tcap = 1; 337 char termcap[TERMCAP_SIZE]; 338 char newtc[TERMCAP_SIZE]; 339 #endif /* USE_TERMCAP */ 340 char buf[BUFSIZ]; 341 #ifdef TTYSIZE_STRUCT 342 TTYSIZE_STRUCT ts; 343 #endif 344 char *name_of_tty; 345 #ifdef CANT_OPEN_DEV_TTY 346 extern char *ttyname(); 347 #endif 348 const char *setname = ""; 349 350 myname = x_basename(argv[0]); 351 if (strcmp(myname, sunname) == 0) 352 emu = SUN; 353 for (argv++, argc--; argc > 0 && **argv == '-'; argv++, argc--) { 354 switch ((*argv)[1]) { 355 case 's': /* Sun emulation */ 356 if (emu == SUN) 357 Usage(); /* Never returns */ 358 emu = SUN; 359 break; 360 case 'u': /* Bourne (Unix) shell */ 361 shell_type = SHELL_BOURNE; 362 break; 363 case 'c': /* C shell */ 364 shell_type = SHELL_C; 365 break; 366 case 'v': 367 printf("%s\n", xtermVersion()); 368 exit(EXIT_SUCCESS); 369 default: 370 Usage(); /* Never returns */ 371 } 372 } 373 374 if (SHELL_UNKNOWN == shell_type) { 375 /* Find out what kind of shell this user is running. 376 * This is the same algorithm that xterm uses. 377 */ 378 if ((ptr = x_getenv("SHELL")) == NULL) { 379 uid_t uid = getuid(); 380 struct passwd pw; 381 382 if (x_getpwuid(uid, &pw)) { 383 (void) x_getlogin(uid, &pw); 384 } 385 if (!OkPasswd(&pw) 386 || *(ptr = pw.pw_shell) == 0) { 387 /* this is the same default that xterm uses */ 388 ptr = x_strdup("/bin/sh"); 389 } 390 } 391 392 shell = x_basename(ptr); 393 394 /* now that we know, what kind is it? */ 395 for (i = 0; shell_list[i].name; i++) { 396 if (!strcmp(shell_list[i].name, shell)) { 397 break; 398 } 399 } 400 shell_type = shell_list[i].type; 401 } 402 403 if (argc == 2) { 404 if (!setsize[emu]) { 405 fprintf(stderr, 406 "%s: Can't set window size under %s emulation\n", 407 myname, emuname[emu]); 408 exit(EXIT_FAILURE); 409 } 410 if (!checkdigits(argv[0]) || !checkdigits(argv[1])) { 411 Usage(); /* Never returns */ 412 } 413 } else if (argc != 0) { 414 Usage(); /* Never returns */ 415 } 416 #ifdef CANT_OPEN_DEV_TTY 417 if ((name_of_tty = ttyname(fileno(stderr))) == NULL) 418 #endif 419 name_of_tty = x_strdup("/dev/tty"); 420 421 if ((ttyfp = fopen(name_of_tty, "r+")) == NULL) { 422 fprintf(stderr, "%s: can't open terminal %s\n", 423 myname, name_of_tty); 424 exit(EXIT_FAILURE); 425 } 426 tty = fileno(ttyfp); 427 #ifdef USE_TERMCAP 428 if ((env = x_getenv("TERM")) == 0) { 429 env = x_strdup(DFT_TERMTYPE); 430 if (SHELL_BOURNE == shell_type) { 431 setname = "TERM=" DFT_TERMTYPE ";\nexport TERM;\n"; 432 } else { 433 setname = "setenv TERM " DFT_TERMTYPE ";\n"; 434 } 435 } 436 termcap[0] = 0; /* ...just in case we've accidentally gotten terminfo */ 437 if (tgetent(termcap, env) <= 0 || termcap[0] == 0) { 438 ok_tcap = 0; 439 } 440 #endif /* USE_TERMCAP */ 441 #ifdef USE_TERMINFO 442 if (x_getenv("TERM") == 0) { 443 if (SHELL_BOURNE == shell_type) { 444 setname = "TERM=" DFT_TERMTYPE ";\nexport TERM;\n"; 445 } else { 446 setname = "setenv TERM " DFT_TERMTYPE ";\n"; 447 } 448 } 449 #endif /* USE_TERMINFO */ 450 451 #ifdef USE_ANY_SYSV_TERMIO 452 rc = ioctl(tty, TCGETA, &tioorig); 453 tio = tioorig; 454 UIntClr(tio.c_iflag, (ICRNL | IUCLC)); 455 UIntClr(tio.c_lflag, (ICANON | ECHO)); 456 tio.c_cflag |= CS8; 457 tio.c_cc[VMIN] = 6; 458 tio.c_cc[VTIME] = 1; 459 #elif defined(USE_TERMIOS) 460 rc = tcgetattr(tty, &tioorig); 461 tio = tioorig; 462 UIntClr(tio.c_iflag, ICRNL); 463 UIntClr(tio.c_lflag, (ICANON | ECHO)); 464 tio.c_cflag |= CS8; 465 tio.c_cc[VMIN] = 6; 466 tio.c_cc[VTIME] = 1; 467 #else /* not USE_TERMIOS */ 468 rc = ioctl(tty, TIOCGETP, &sgorig); 469 sg = sgorig; 470 sg.sg_flags |= RAW; 471 UIntClr(sg.sg_flags, ECHO); 472 #endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */ 473 if (rc != 0) 474 failed("get tty settings"); 475 476 signal(SIGINT, onintr); 477 signal(SIGQUIT, onintr); 478 signal(SIGTERM, onintr); 479 480 #ifdef USE_ANY_SYSV_TERMIO 481 rc = ioctl(tty, TCSETAW, &tio); 482 #elif defined(USE_TERMIOS) 483 rc = tcsetattr(tty, TCSADRAIN, &tio); 484 #else /* not USE_TERMIOS */ 485 rc = ioctl(tty, TIOCSETP, &sg); 486 #endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */ 487 if (rc != 0) 488 failed("set tty settings"); 489 490 if (argc == 2) { /* look for optional parameters of "-s" */ 491 char *tmpbuf = TypeMallocN(char, 492 strlen(setsize[emu]) + 493 strlen(argv[0]) + 494 strlen(argv[1]) + 495 1); 496 if (tmpbuf == 0) { 497 fprintf(stderr, "%s: Cannot query size\n", myname); 498 onintr(0); 499 } else { 500 sprintf(tmpbuf, setsize[emu], argv[0], argv[1]); 501 IGNORE_RC(write(tty, tmpbuf, strlen(tmpbuf))); 502 free(tmpbuf); 503 } 504 } 505 IGNORE_RC(write(tty, getsize[emu], strlen(getsize[emu]))); 506 readstring(ttyfp, buf, size[emu]); 507 if (sscanf(buf, size[emu], &rows, &cols) != 2) { 508 fprintf(stderr, "%s: Can't get rows and columns\r\n", myname); 509 onintr(0); 510 } 511 if (restore[emu]) 512 IGNORE_RC(write(tty, restore[emu], strlen(restore[emu]))); 513 #if defined(USE_STRUCT_WINSIZE) 514 /* finally, set the tty's window size */ 515 if (getwsize[emu]) { 516 /* get the window size in pixels */ 517 IGNORE_RC(write(tty, getwsize[emu], strlen(getwsize[emu]))); 518 readstring(ttyfp, buf, wsize[emu]); 519 if (sscanf(buf, wsize[emu], &ts.ws_xpixel, &ts.ws_ypixel) != 2) { 520 fprintf(stderr, "%s: Can't get window size\r\n", myname); 521 onintr(0); 522 } 523 setup_winsize(ts, rows, cols, 0, 0); 524 SET_TTYSIZE(tty, ts); 525 } else if (ioctl(tty, TIOCGWINSZ, &ts) != -1) { 526 /* we don't have any way of directly finding out 527 the current height & width of the window in pixels. We try 528 our best by computing the font height and width from the "old" 529 window-size values, and multiplying by these ratios... */ 530 #define scaled(old,new,len) (old)?((unsigned)(new)*(len)/(old)):(len) 531 setup_winsize(ts, rows, cols, 532 scaled(TTYSIZE_ROWS(ts), rows, ts.ws_ypixel), 533 scaled(TTYSIZE_COLS(ts), cols, ts.ws_xpixel)); 534 SET_TTYSIZE(tty, ts); 535 } 536 #endif /* USE_STRUCT_WINSIZE */ 537 538 #ifdef USE_ANY_SYSV_TERMIO 539 rc = ioctl(tty, TCSETAW, &tioorig); 540 #elif defined(USE_TERMIOS) 541 rc = tcsetattr(tty, TCSADRAIN, &tioorig); 542 #else /* not USE_TERMIOS */ 543 rc = ioctl(tty, TIOCSETP, &sgorig); 544 #endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */ 545 if (rc != 0) 546 failed("set tty settings"); 547 548 signal(SIGINT, SIG_DFL); 549 signal(SIGQUIT, SIG_DFL); 550 signal(SIGTERM, SIG_DFL); 551 552 #ifdef USE_TERMCAP 553 if (ok_tcap) { 554 /* update termcap string */ 555 /* first do columns */ 556 if ((ptr = x_strindex(termcap, "co#")) == NULL) { 557 fprintf(stderr, "%s: No `co#'\n", myname); 558 exit(EXIT_FAILURE); 559 } 560 561 i = (int) (ptr - termcap) + 3; 562 strncpy(newtc, termcap, (size_t) i); 563 sprintf(newtc + i, "%d", cols); 564 if ((ptr = strchr(ptr, ':')) != 0) 565 strcat(newtc, ptr); 566 567 /* now do lines */ 568 if ((ptr = x_strindex(newtc, "li#")) == NULL) { 569 fprintf(stderr, "%s: No `li#'\n", myname); 570 exit(EXIT_FAILURE); 571 } 572 573 i = (int) (ptr - newtc) + 3; 574 strncpy(termcap, newtc, (size_t) i); 575 sprintf(termcap + i, "%d", rows); 576 if ((ptr = strchr(ptr, ':')) != 0) 577 strcat(termcap, ptr); 578 } 579 #endif /* USE_TERMCAP */ 580 581 if (SHELL_BOURNE == shell_type) { 582 583 #ifdef USE_TERMCAP 584 if (ok_tcap) { 585 printf("%sTERMCAP=", setname); 586 print_termcap(termcap); 587 printf(";\nexport TERMCAP;\n"); 588 } 589 #endif /* USE_TERMCAP */ 590 #ifdef USE_TERMINFO 591 printf("%sCOLUMNS=%d;\nLINES=%d;\nexport COLUMNS LINES;\n", 592 setname, cols, rows); 593 #endif /* USE_TERMINFO */ 594 595 } else { /* not Bourne shell */ 596 597 #ifdef USE_TERMCAP 598 if (ok_tcap) { 599 printf("set noglob;\n%ssetenv TERMCAP ", setname); 600 print_termcap(termcap); 601 printf(";\nunset noglob;\n"); 602 } 603 #endif /* USE_TERMCAP */ 604 #ifdef USE_TERMINFO 605 printf("set noglob;\n%ssetenv COLUMNS '%d';\nsetenv LINES '%d';\nunset noglob;\n", 606 setname, cols, rows); 607 #endif /* USE_TERMINFO */ 608 } 609 exit(EXIT_SUCCESS); 610 } 611