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