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