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