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