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