1 /* $XTermId: resize.c,v 1.114 2010/05/23 16:04:32 tom Exp $ */
4 * Copyright 2003-2009,2010 by Thomas E. Dickey
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:
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
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.
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
33 * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
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.
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
65 #define CANT_OPEN_DEV_TTY
68 #ifndef USE_TERMINFO /* avoid conflict with configure script */
69 #if defined(__QNX__) || defined(__SCO__) || defined(linux) || defined(__OpenBSD__) || defined(__UNIXWARE__)
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.
82 #if !defined(USE_TERMCAP) && !defined(USE_TERMINFO)
94 #if !defined(SYSV) && !defined(i386)
95 extern struct passwd *getpwuid(); /* does ANYBODY need this? */
96 #endif /* SYSV && i386 */
97 #endif /* X_NOT_POSIX */
100 #define ESCAPE(string) "\047" string
102 #define ESCAPE(string) "\033" string
111 #define SHELL_UNKNOWN 0
113 #define SHELL_BOURNE 2
119 { "csh", SHELL_C }, /* vanilla cshell */
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) */
131 static const char *emuname[EMULATIONS] =
137 static int shell_type = SHELL_UNKNOWN;
138 static const char *getsize[EMULATIONS] =
140 ESCAPE("7") ESCAPE("[r") ESCAPE("[999;999H") ESCAPE("[6n"),
143 #if defined(USE_STRUCT_TTYSIZE)
144 #elif defined(USE_STRUCT_WINSIZE)
145 static const char *getwsize[EMULATIONS] =
146 { /* size in pixels */
150 #endif /* USE_STRUCT_{TTYSIZE|WINSIZE} */
151 static const char *restore[EMULATIONS] =
156 static const char *setname = "";
157 static const char *setsize[EMULATIONS] =
163 #ifdef USE_ANY_SYSV_TERMIO
164 static struct termio tioorig;
165 #elif defined(USE_TERMIOS)
166 static struct termios tioorig;
168 static struct sgttyb sgorig;
169 #endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */
171 static const char *size[EMULATIONS] =
176 static char sunname[] = "sunsize";
180 #if defined(USE_STRUCT_TTYSIZE)
181 #elif defined(USE_STRUCT_WINSIZE)
182 static const char *wsize[EMULATIONS] =
185 ESCAPE("[4;%hd;%hdt"),
187 #endif /* USE_STRUCT_{TTYSIZE|WINSIZE} */
189 static SIGNAL_T onintr(int sig);
190 static SIGNAL_T resize_timeout(int sig);
191 static int checkdigits(char *str);
192 static void Usage(void);
193 static void readstring(FILE *fp, char *buf, const char *str);
197 print_termcap(const char *termcap)
202 while ((ch = *termcap++) != '\0') {
204 case 127: /* undo bug in GNU termcap */
207 case '\'': /* must escape anyway (unlikely) */
209 case '!': /* must escape for SunOS csh */
219 #endif /* USE_TERMCAP */
222 resets termcap string to reflect current screen size
225 main(int argc, char **argv ENVP_ARG)
236 #ifdef USE_ANY_SYSV_TERMIO
238 #elif defined(USE_TERMIOS)
242 #endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */
245 char termcap[TERMCAP_SIZE];
246 char newtc[TERMCAP_SIZE];
247 #endif /* USE_TERMCAP */
249 #ifdef TTYSIZE_STRUCT
253 #ifdef CANT_OPEN_DEV_TTY
254 extern char *ttyname();
257 myname = x_basename(argv[0]);
258 if (strcmp(myname, sunname) == 0)
260 for (argv++, argc--; argc > 0 && **argv == '-'; argv++, argc--) {
261 switch ((*argv)[1]) {
262 case 's': /* Sun emulation */
264 Usage(); /* Never returns */
267 case 'u': /* Bourne (Unix) shell */
268 shell_type = SHELL_BOURNE;
270 case 'c': /* C shell */
271 shell_type = SHELL_C;
274 Usage(); /* Never returns */
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.
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");
288 shell = x_basename(ptr);
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))
294 shell_type = shell_list[i].type;
300 "%s: Can't set window size under %s emulation\n",
301 myname, emuname[emu]);
304 if (!checkdigits(argv[0]) || !checkdigits(argv[1]))
305 Usage(); /* Never returns */
306 } else if (argc != 0)
307 Usage(); /* Never returns */
309 #ifdef CANT_OPEN_DEV_TTY
310 if ((name_of_tty = ttyname(fileno(stderr))) == NULL)
312 name_of_tty = x_strdup("/dev/tty");
314 if ((ttyfp = fopen(name_of_tty, "r+")) == NULL) {
315 fprintf(stderr, "%s: can't open terminal %s\n",
316 myname, name_of_tty);
321 if ((env = x_getenv("TERM")) == 0) {
323 if (SHELL_BOURNE == shell_type)
324 setname = "TERM=" DFT_TERMTYPE ";\nexport TERM;\n";
326 setname = "setenv TERM " DFT_TERMTYPE ";\n";
328 termcap[0] = 0; /* ...just in case we've accidentally gotten terminfo */
329 if (tgetent(termcap, env) <= 0 || termcap[0] == 0)
331 #endif /* USE_TERMCAP */
333 if (x_getenv("TERM") == 0) {
334 if (SHELL_BOURNE == shell_type)
335 setname = "TERM=" DFT_TERMTYPE ";\nexport TERM;\n";
337 setname = "setenv TERM " DFT_TERMTYPE ";\n";
339 #endif /* USE_TERMINFO */
341 #ifdef USE_ANY_SYSV_TERMIO
342 ioctl(tty, TCGETA, &tioorig);
344 UIntClr(tio.c_iflag, (ICRNL | IUCLC));
345 UIntClr(tio.c_lflag, (ICANON | ECHO));
349 #elif defined(USE_TERMIOS)
350 tcgetattr(tty, &tioorig);
352 UIntClr(tio.c_iflag, ICRNL);
353 UIntClr(tio.c_lflag, (ICANON | ECHO));
357 #else /* not USE_TERMIOS */
358 ioctl(tty, TIOCGETP, &sgorig);
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 */
375 char *tmpbuf = TypeMallocN(char,
376 strlen(setsize[emu]) +
381 fprintf(stderr, "%s: Cannot query size\n", myname);
384 sprintf(tmpbuf, setsize[emu], argv[0], argv[1]);
385 IGNORE_RC(write(tty, tmpbuf, strlen(tmpbuf)));
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);
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);
404 #elif defined(USE_STRUCT_WINSIZE)
405 /* finally, set the tty's window size */
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);
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);
430 #endif /* USE_STRUCT_{TTYSIZE|WINSIZE} */
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);
445 /* update termcap string */
446 /* first do columns */
447 if ((ptr = x_strindex(termcap, "co#")) == NULL) {
448 fprintf(stderr, "%s: No `co#'\n", myname);
452 i = ptr - termcap + 3;
453 strncpy(newtc, termcap, (size_t) i);
454 sprintf(newtc + i, "%d", cols);
455 ptr = strchr(ptr, ':');
459 if ((ptr = x_strindex(newtc, "li#")) == NULL) {
460 fprintf(stderr, "%s: No `li#'\n", myname);
465 strncpy(termcap, newtc, (size_t) i);
466 sprintf(termcap + i, "%d", rows);
467 ptr = strchr(ptr, ':');
468 strcat(termcap, ptr);
470 #endif /* USE_TERMCAP */
472 if (SHELL_BOURNE == shell_type) {
476 printf("%sTERMCAP=", setname);
477 print_termcap(termcap);
478 printf(";\nexport TERMCAP;\n");
480 #endif /* USE_TERMCAP */
482 printf("%sCOLUMNS=%d;\nLINES=%d;\nexport COLUMNS LINES;\n",
483 setname, cols, rows);
484 #endif /* USE_TERMINFO */
486 } else { /* not Bourne shell */
490 printf("set noglob;\n%ssetenv TERMCAP ", setname);
491 print_termcap(termcap);
492 printf(";\nunset noglob;\n");
494 #endif /* USE_TERMCAP */
496 printf("set noglob;\n%ssetenv COLUMNS '%d';\nsetenv LINES '%d';\nunset noglob;\n",
497 setname, cols, rows);
498 #endif /* USE_TERMINFO */
504 checkdigits(char *str)
507 if (!isdigit(CharOf(*str)))
515 readstring(FILE *fp, char *buf, const char *str)
518 #if !defined(USG) && !defined(__UNIXOS2__)
519 /* What is the advantage of setitimer() over alarm()? */
523 signal(SIGALRM, resize_timeout);
524 #if defined(USG) || defined(__UNIXOS2__)
527 memset((char *) &it, 0, sizeof(struct itimerval));
528 it.it_value.tv_sec = TIMEOUT;
529 setitimer(ITIMER_REAL, &it, (struct itimerval *) NULL);
531 if ((c = getc(fp)) == 0233) { /* meta-escape, CSI */
539 fprintf(stderr, "%s: unknown character, exiting.\r\n", myname);
542 last = str[strlen(str) - 1];
543 while ((*buf++ = (char) getc(fp)) != last) {
546 #if defined(USG) || defined(__UNIXOS2__)
549 memset((char *) &it, 0, sizeof(struct itimerval));
550 setitimer(ITIMER_REAL, &it, (struct itimerval *) NULL);
558 fprintf(stderr, strcmp(myname, sunname) == 0 ?
559 "Usage: %s [rows cols]\n" :
560 "Usage: %s [-u] [-c] [-s [rows cols]]\n", myname);
565 resize_timeout(int sig)
567 fprintf(stderr, "\n%s: Time out occurred\r\n", myname);
573 onintr(int sig GCC_UNUSED)
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 */