resize.c revision 50027b5b
1/* $XTermId: resize.c,v 1.148 2022/02/18 20:32:48 tom Exp $ */ 2 3/* 4 * Copyright 2003-2021,2022 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 92int 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/* *INDENT-OFF* */ 111static struct { 112 const char *name; 113 int type; 114} shell_list[] = { 115 { "csh", SHELL_C }, /* vanilla cshell */ 116 { "jcsh", SHELL_C }, 117 { "tcsh", SHELL_C }, 118 { "sh", SHELL_BOURNE }, /* vanilla Bourne shell */ 119 { "ash", SHELL_BOURNE }, 120 { "bash", SHELL_BOURNE }, /* GNU Bourne again shell */ 121 { "dash", SHELL_BOURNE }, 122 { "jsh", SHELL_BOURNE }, 123 { "ksh", SHELL_BOURNE }, /* Korn shell (from AT&T toolchest) */ 124 { "ksh-i", SHELL_BOURNE }, /* another name for Korn shell */ 125 { "ksh93", SHELL_BOURNE }, /* Korn shell */ 126 { "mksh", SHELL_BOURNE }, 127 { "pdksh", SHELL_BOURNE }, 128 { "zsh", SHELL_BOURNE }, 129 { NULL, SHELL_BOURNE } /* default (same as xterm's) */ 130}; 131/* *INDENT-ON* */ 132 133static const char *const emuname[EMULATIONS] = 134{ 135 "VT100", 136 "Sun", 137}; 138static char *myname; 139static int shell_type = SHELL_UNKNOWN; 140static const char *const getsize[EMULATIONS] = 141{ 142 ESCAPE("7") ESCAPE("[r") ESCAPE("[9999;9999H") ESCAPE("[6n"), 143 ESCAPE("[18t"), 144}; 145#if defined(USE_STRUCT_WINSIZE) 146static const char *const getwsize[EMULATIONS] = 147{ /* size in pixels */ 148 0, 149 ESCAPE("[14t"), 150}; 151#endif /* USE_STRUCT_WINSIZE */ 152static const char *const restore[EMULATIONS] = 153{ 154 ESCAPE("8"), 155 0, 156}; 157static const char *const setsize[EMULATIONS] = 158{ 159 0, 160 ESCAPE("[8;%s;%st"), 161}; 162 163#ifdef USE_ANY_SYSV_TERMIO 164static struct termio tioorig; 165#elif defined(USE_TERMIOS) 166static struct termios tioorig; 167#else 168static struct sgttyb sgorig; 169#endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */ 170 171static const char *const size[EMULATIONS] = 172{ 173 ESCAPE("[%d;%dR"), 174 ESCAPE("[8;%d;%dt"), 175}; 176static const char sunname[] = "sunsize"; 177static int tty; 178static FILE *ttyfp; 179 180#if defined(USE_STRUCT_WINSIZE) 181static const char *wsize[EMULATIONS] = 182{ 183 0, 184 ESCAPE("[4;%hd;%hdt"), 185}; 186#endif /* USE_STRUCT_WINSIZE */ 187 188static GCC_NORETURN void failed(const char *); 189static GCC_NORETURN void onintr(int); 190static GCC_NORETURN void resize_timeout(int); 191static GCC_NORETURN void Usage(void); 192 193static void 194failed(const char *s) 195{ 196 int save = errno; 197 IGNORE_RC(write(2, myname, strlen(myname))); 198 IGNORE_RC(write(2, ": ", (size_t) 2)); 199 errno = save; 200 perror(s); 201 exit(EXIT_FAILURE); 202} 203 204/* ARGSUSED */ 205static void 206onintr(int sig GCC_UNUSED) 207{ 208#ifdef USE_ANY_SYSV_TERMIO 209 (void) ioctl(tty, TCSETAW, &tioorig); 210#elif defined(USE_TERMIOS) 211 (void) tcsetattr(tty, TCSADRAIN, &tioorig); 212#else /* not USE_TERMIOS */ 213 (void) ioctl(tty, TIOCSETP, &sgorig); 214#endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */ 215 exit(EXIT_FAILURE); 216} 217 218static void 219resize_timeout(int sig) 220{ 221 fprintf(stderr, "\n%s: Time out occurred\r\n", myname); 222 onintr(sig); 223} 224 225static void 226Usage(void) 227{ 228 fprintf(stderr, strcmp(myname, sunname) == 0 ? 229 "Usage: %s [rows cols]\n" : 230 "Usage: %s [-v] [-u] [-c] [-s [rows cols]]\n", myname); 231 exit(EXIT_FAILURE); 232} 233 234#ifdef USE_TERMCAP 235static void 236print_termcap(const char *termcap) 237{ 238 int ch; 239 240 putchar('\''); 241 while ((ch = *termcap++) != '\0') { 242 switch (ch & 0xff) { 243 case 127: /* undo bug in GNU termcap */ 244 printf("^?"); 245 break; 246 case '\'': /* must escape anyway (unlikely) */ 247 /* FALLTHRU */ 248 case '!': /* must escape for SunOS csh */ 249 putchar('\\'); 250 /* FALLTHRU */ 251 default: 252 putchar(ch); 253 break; 254 } 255 } 256 putchar('\''); 257} 258#endif /* USE_TERMCAP */ 259 260static int 261checkdigits(char *str) 262{ 263 while (*str) { 264 if (!isdigit(CharOf(*str))) 265 return (0); 266 str++; 267 } 268 return (1); 269} 270 271static void 272unexpected_char(int c) 273{ 274 fprintf(stderr, "%s: unknown character %#x, exiting.\r\n", myname, c); 275 onintr(0); 276} 277 278static void 279readstring(FILE *fp, char *buf, const char *str) 280{ 281 int last, c; 282#if !defined(USG) && !defined(__minix) 283 /* What is the advantage of setitimer() over alarm()? */ 284 struct itimerval it; 285#endif 286 int limit = (BUFSIZ - 3); 287 288 signal(SIGALRM, resize_timeout); 289#if defined(USG) || defined(__minix) 290 alarm(TIMEOUT); 291#else 292 memset((char *) &it, 0, sizeof(struct itimerval)); 293 it.it_value.tv_sec = TIMEOUT; 294 setitimer(ITIMER_REAL, &it, (struct itimerval *) NULL); 295#endif 296 if ((c = getc(fp)) == 0233) { /* meta-escape, CSI */ 297 c = ESCAPE("")[0]; 298 *buf++ = (char) c; 299 *buf++ = '['; 300 } else { 301 *buf++ = (char) c; 302 } 303 if (c != *str) { 304 unexpected_char(c); 305 } 306 last = str[strlen(str) - 1]; 307 while ((c = getc(fp)) != EOF) { 308 if (--limit <= 0) { 309 fprintf(stderr, "%s: unexpected response\n", myname); 310 onintr(0); 311 } 312 if (c < 32 || c > 126) { 313 unexpected_char(c); 314 } 315 *buf++ = (char) c; 316 if (c == last) 317 break; 318 } 319#if defined(USG) || defined(__minix) 320 alarm(0); 321#else 322 memset((char *) &it, 0, sizeof(struct itimerval)); 323 setitimer(ITIMER_REAL, &it, (struct itimerval *) NULL); 324#endif 325 *buf = 0; 326} 327 328/* 329 resets termcap string to reflect current screen size 330 */ 331int 332main(int argc, char **argv ENVP_ARG) 333{ 334#ifdef USE_TERMCAP 335 char *env; 336#endif 337 char *ptr; 338 int emu = VT100; 339 char *shell; 340 int i; 341 int rc; 342 int rows, cols; 343#ifdef USE_ANY_SYSV_TERMIO 344 struct termio tio; 345#elif defined(USE_TERMIOS) 346 struct termios tio; 347#else 348 struct sgttyb sg; 349#endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */ 350#ifdef USE_TERMCAP 351 int ok_tcap = 1; 352 char termcap[TERMCAP_SIZE]; 353 char newtc[TERMCAP_SIZE]; 354#endif /* USE_TERMCAP */ 355 char buf[BUFSIZ]; 356#ifdef TTYSIZE_STRUCT 357 TTYSIZE_STRUCT ts; 358#endif 359 char *name_of_tty; 360#ifdef CANT_OPEN_DEV_TTY 361 extern char *ttyname(); 362#endif 363 const char *setname = ""; 364 365 myname = x_basename(argv[0]); 366 if (strcmp(myname, sunname) == 0) 367 emu = SUN; 368 for (argv++, argc--; argc > 0 && **argv == '-'; argv++, argc--) { 369 switch ((*argv)[1]) { 370 case 's': /* Sun emulation */ 371 if (emu == SUN) 372 Usage(); /* Never returns */ 373 emu = SUN; 374 break; 375 case 'u': /* Bourne (Unix) shell */ 376 shell_type = SHELL_BOURNE; 377 break; 378 case 'c': /* C shell */ 379 shell_type = SHELL_C; 380 break; 381 case 'v': 382 printf("%s\n", xtermVersion()); 383 exit(EXIT_SUCCESS); 384 default: 385 Usage(); /* Never returns */ 386 } 387 } 388 389 if (SHELL_UNKNOWN == shell_type) { 390 /* Find out what kind of shell this user is running. 391 * This is the same algorithm that xterm uses. 392 */ 393 if ((ptr = x_getenv("SHELL")) == NULL) { 394 uid_t uid = getuid(); 395 struct passwd pw; 396 397 if (x_getpwuid(uid, &pw)) { 398 (void) x_getlogin(uid, &pw); 399 } 400 if (!OkPasswd(&pw) 401 || *(ptr = pw.pw_shell) == 0) { 402 /* this is the same default that xterm uses */ 403 ptr = x_strdup("/bin/sh"); 404 } 405 } 406 407 shell = x_basename(ptr); 408 409 /* now that we know, what kind is it? */ 410 for (i = 0; shell_list[i].name; i++) { 411 if (!strcmp(shell_list[i].name, shell)) { 412 break; 413 } 414 } 415 shell_type = shell_list[i].type; 416 } 417 418 if (argc == 2) { 419 if (!setsize[emu]) { 420 fprintf(stderr, 421 "%s: Can't set window size under %s emulation\n", 422 myname, emuname[emu]); 423 exit(EXIT_FAILURE); 424 } 425 if (!checkdigits(argv[0]) || !checkdigits(argv[1])) { 426 Usage(); /* Never returns */ 427 } 428 } else if (argc != 0) { 429 Usage(); /* Never returns */ 430 } 431#ifdef CANT_OPEN_DEV_TTY 432 if ((name_of_tty = ttyname(fileno(stderr))) == NULL) 433#endif 434 name_of_tty = x_strdup("/dev/tty"); 435 436 if ((ttyfp = fopen(name_of_tty, "r+")) == NULL) { 437 fprintf(stderr, "%s: can't open terminal %s\n", 438 myname, name_of_tty); 439 exit(EXIT_FAILURE); 440 } 441 tty = fileno(ttyfp); 442#ifdef USE_TERMCAP 443 if ((env = x_getenv("TERM")) == 0) { 444 env = x_strdup(DFT_TERMTYPE); 445 if (SHELL_BOURNE == shell_type) { 446 setname = "TERM=" DFT_TERMTYPE ";\nexport TERM;\n"; 447 } else { 448 setname = "setenv TERM " DFT_TERMTYPE ";\n"; 449 } 450 } 451 termcap[0] = 0; /* ...just in case we've accidentally gotten terminfo */ 452 if (tgetent(termcap, env) <= 0 || termcap[0] == 0) { 453 ok_tcap = 0; 454 } 455#endif /* USE_TERMCAP */ 456#ifdef USE_TERMINFO 457 if (x_getenv("TERM") == 0) { 458 if (SHELL_BOURNE == shell_type) { 459 setname = "TERM=" DFT_TERMTYPE ";\nexport TERM;\n"; 460 } else { 461 setname = "setenv TERM " DFT_TERMTYPE ";\n"; 462 } 463 } 464#endif /* USE_TERMINFO */ 465 466#ifdef USE_ANY_SYSV_TERMIO 467 rc = ioctl(tty, TCGETA, &tioorig); 468 tio = tioorig; 469 UIntClr(tio.c_iflag, (ICRNL | IUCLC)); 470 UIntClr(tio.c_lflag, (ICANON | ECHO)); 471 tio.c_cflag |= CS8; 472 tio.c_cc[VMIN] = 6; 473 tio.c_cc[VTIME] = 1; 474#elif defined(USE_TERMIOS) 475 rc = tcgetattr(tty, &tioorig); 476 tio = tioorig; 477 UIntClr(tio.c_iflag, ICRNL); 478 UIntClr(tio.c_lflag, (ICANON | ECHO)); 479 tio.c_cflag |= CS8; 480 tio.c_cc[VMIN] = 6; 481 tio.c_cc[VTIME] = 1; 482#else /* not USE_TERMIOS */ 483 rc = ioctl(tty, TIOCGETP, &sgorig); 484 sg = sgorig; 485 sg.sg_flags |= RAW; 486 UIntClr(sg.sg_flags, ECHO); 487#endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */ 488 if (rc != 0) 489 failed("get tty settings"); 490 491 signal(SIGINT, onintr); 492 signal(SIGQUIT, onintr); 493 signal(SIGTERM, onintr); 494 495#ifdef USE_ANY_SYSV_TERMIO 496 rc = ioctl(tty, TCSETAW, &tio); 497#elif defined(USE_TERMIOS) 498 rc = tcsetattr(tty, TCSADRAIN, &tio); 499#else /* not USE_TERMIOS */ 500 rc = ioctl(tty, TIOCSETP, &sg); 501#endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */ 502 if (rc != 0) 503 failed("set tty settings"); 504 505 if (argc == 2) { /* look for optional parameters of "-s" */ 506 char *tmpbuf = TypeMallocN(char, 507 strlen(setsize[emu]) + 508 strlen(argv[0]) + 509 strlen(argv[1]) + 510 1); 511 if (tmpbuf == 0) { 512 fprintf(stderr, "%s: Cannot query size\n", myname); 513 onintr(0); 514 } else { 515 sprintf(tmpbuf, setsize[emu], argv[0], argv[1]); 516 IGNORE_RC(write(tty, tmpbuf, strlen(tmpbuf))); 517 free(tmpbuf); 518 } 519 } 520 IGNORE_RC(write(tty, getsize[emu], strlen(getsize[emu]))); 521 readstring(ttyfp, buf, size[emu]); 522 if (sscanf(buf, size[emu], &rows, &cols) != 2) { 523 fprintf(stderr, "%s: Can't get rows and columns\r\n", myname); 524 onintr(0); 525 } 526 if (restore[emu]) 527 IGNORE_RC(write(tty, restore[emu], strlen(restore[emu]))); 528#if defined(USE_STRUCT_WINSIZE) 529 /* finally, set the tty's window size */ 530 if (getwsize[emu]) { 531 /* get the window size in pixels */ 532 IGNORE_RC(write(tty, getwsize[emu], strlen(getwsize[emu]))); 533 readstring(ttyfp, buf, wsize[emu]); 534 if (sscanf(buf, wsize[emu], &ts.ws_xpixel, &ts.ws_ypixel) != 2) { 535 fprintf(stderr, "%s: Can't get window size\r\n", myname); 536 onintr(0); 537 } 538 setup_winsize(ts, rows, cols, 0, 0); 539 SET_TTYSIZE(tty, ts); 540 } else if (ioctl(tty, TIOCGWINSZ, &ts) != -1) { 541 /* we don't have any way of directly finding out 542 the current height & width of the window in pixels. We try 543 our best by computing the font height and width from the "old" 544 window-size values, and multiplying by these ratios... */ 545#define scaled(old,new,len) (old)?((unsigned)(new)*(len)/(old)):(len) 546 setup_winsize(ts, rows, cols, 547 scaled(TTYSIZE_ROWS(ts), rows, ts.ws_ypixel), 548 scaled(TTYSIZE_COLS(ts), cols, ts.ws_xpixel)); 549 SET_TTYSIZE(tty, ts); 550 } 551#endif /* USE_STRUCT_WINSIZE */ 552 553#ifdef USE_ANY_SYSV_TERMIO 554 rc = ioctl(tty, TCSETAW, &tioorig); 555#elif defined(USE_TERMIOS) 556 rc = tcsetattr(tty, TCSADRAIN, &tioorig); 557#else /* not USE_TERMIOS */ 558 rc = ioctl(tty, TIOCSETP, &sgorig); 559#endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */ 560 if (rc != 0) 561 failed("set tty settings"); 562 563 signal(SIGINT, SIG_DFL); 564 signal(SIGQUIT, SIG_DFL); 565 signal(SIGTERM, SIG_DFL); 566 567#ifdef USE_TERMCAP 568 if (ok_tcap) { 569 /* update termcap string */ 570 /* first do columns */ 571 if ((ptr = x_strindex(termcap, "co#")) == NULL) { 572 fprintf(stderr, "%s: No `co#'\n", myname); 573 exit(EXIT_FAILURE); 574 } 575 576 i = (int) (ptr - termcap) + 3; 577 strncpy(newtc, termcap, (size_t) i); 578 sprintf(newtc + i, "%d", cols); 579 if ((ptr = strchr(ptr, ':')) != 0) 580 strcat(newtc, ptr); 581 582 /* now do lines */ 583 if ((ptr = x_strindex(newtc, "li#")) == NULL) { 584 fprintf(stderr, "%s: No `li#'\n", myname); 585 exit(EXIT_FAILURE); 586 } 587 588 i = (int) (ptr - newtc) + 3; 589 strncpy(termcap, newtc, (size_t) i); 590 sprintf(termcap + i, "%d", rows); 591 if ((ptr = strchr(ptr, ':')) != 0) 592 strcat(termcap, ptr); 593 } 594#endif /* USE_TERMCAP */ 595 596 if (SHELL_BOURNE == shell_type) { 597 598#ifdef USE_TERMCAP 599 if (ok_tcap) { 600 printf("%sTERMCAP=", setname); 601 print_termcap(termcap); 602 printf(";\nexport TERMCAP;\n"); 603 } 604#endif /* USE_TERMCAP */ 605#ifdef USE_TERMINFO 606 printf("%sCOLUMNS=%d;\nLINES=%d;\nexport COLUMNS LINES;\n", 607 setname, cols, rows); 608#endif /* USE_TERMINFO */ 609 610 } else { /* not Bourne shell */ 611 612#ifdef USE_TERMCAP 613 if (ok_tcap) { 614 printf("set noglob;\n%ssetenv TERMCAP ", setname); 615 print_termcap(termcap); 616 printf(";\nunset noglob;\n"); 617 } 618#endif /* USE_TERMCAP */ 619#ifdef USE_TERMINFO 620 printf("set noglob;\n%ssetenv COLUMNS '%d';\nsetenv LINES '%d';\nunset noglob;\n", 621 setname, cols, rows); 622#endif /* USE_TERMINFO */ 623 } 624 exit(EXIT_SUCCESS); 625} 626