resize.c revision d522f475
1/* $XTermId: resize.c,v 1.106 2007/12/31 21:10:07 tom Exp $ */
2
3/*
4 * Copyright 2003-2006,2007 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#ifndef bzero
96#define	bzero(s, n)	memset(s, 0, n)
97#endif
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    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 char *emuname[EMULATIONS] =
132{
133    "VT100",
134    "Sun",
135};
136static char *myname;
137static int shell_type = SHELL_UNKNOWN;
138static 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 char *getwsize[EMULATIONS] =
146{				/* size in pixels */
147    0,
148    ESCAPE("[14t"),
149};
150#endif /* USE_STRUCT_{TTYSIZE|WINSIZE} */
151static char *restore[EMULATIONS] =
152{
153    ESCAPE("8"),
154    0,
155};
156static char *setname = "";
157static 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 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 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, 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    register char *env;
229#endif
230    register char *ptr;
231    register 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 = "/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 = "/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    tio.c_iflag &= ~(ICRNL | IUCLC);
345    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    tio.c_iflag &= ~ICRNL;
353    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    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	}
384	sprintf(tmpbuf, setsize[emu], argv[0], argv[1]);
385	write(tty, tmpbuf, strlen(tmpbuf));
386	free(tmpbuf);
387    }
388    write(tty, getsize[emu], strlen(getsize[emu]));
389    readstring(ttyfp, buf, size[emu]);
390    if (sscanf(buf, size[emu], &rows, &cols) != 2) {
391	fprintf(stderr, "%s: Can't get rows and columns\r\n", myname);
392	onintr(0);
393    }
394    if (restore[emu])
395	write(tty, restore[emu], strlen(restore[emu]));
396#if defined(USE_STRUCT_TTYSIZE)
397    /* finally, set the tty's window size */
398    if (ioctl(tty, TIOCGSIZE, &ts) != -1) {
399	TTYSIZE_ROWS(ts) = rows;
400	TTYSIZE_COLS(ts) = cols;
401	SET_TTYSIZE(tty, ts);
402    }
403#elif defined(USE_STRUCT_WINSIZE)
404    /* finally, set the tty's window size */
405    if (getwsize[emu]) {
406	/* get the window size in pixels */
407	write(tty, getwsize[emu], strlen(getwsize[emu]));
408	readstring(ttyfp, buf, wsize[emu]);
409	if (sscanf(buf, wsize[emu], &ts.ws_xpixel, &ts.ws_ypixel) != 2) {
410	    fprintf(stderr, "%s: Can't get window size\r\n", myname);
411	    onintr(0);
412	}
413	TTYSIZE_ROWS(ts) = rows;
414	TTYSIZE_COLS(ts) = cols;
415	SET_TTYSIZE(tty, ts);
416    } else if (ioctl(tty, TIOCGWINSZ, &ts) != -1) {
417	/* we don't have any way of directly finding out
418	   the current height & width of the window in pixels.  We try
419	   our best by computing the font height and width from the "old"
420	   window-size values, and multiplying by these ratios... */
421	if (TTYSIZE_COLS(ts) != 0)
422	    ts.ws_xpixel = cols * (ts.ws_xpixel / TTYSIZE_COLS(ts));
423	if (TTYSIZE_ROWS(ts) != 0)
424	    ts.ws_ypixel = rows * (ts.ws_ypixel / TTYSIZE_ROWS(ts));
425	TTYSIZE_ROWS(ts) = rows;
426	TTYSIZE_COLS(ts) = cols;
427	SET_TTYSIZE(tty, ts);
428    }
429#endif /* USE_STRUCT_{TTYSIZE|WINSIZE} */
430
431#ifdef USE_ANY_SYSV_TERMIO
432    ioctl(tty, TCSETAW, &tioorig);
433#elif defined(USE_TERMIOS)
434    tcsetattr(tty, TCSADRAIN, &tioorig);
435#else /* not USE_TERMIOS */
436    ioctl(tty, TIOCSETP, &sgorig);
437#endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */
438    signal(SIGINT, SIG_DFL);
439    signal(SIGQUIT, SIG_DFL);
440    signal(SIGTERM, SIG_DFL);
441
442#ifdef USE_TERMCAP
443    if (ok_tcap) {
444	/* update termcap string */
445	/* first do columns */
446	if ((ptr = x_strindex(termcap, "co#")) == NULL) {
447	    fprintf(stderr, "%s: No `co#'\n", myname);
448	    exit(1);
449	}
450
451	i = ptr - termcap + 3;
452	strncpy(newtc, termcap, (unsigned) i);
453	sprintf(newtc + i, "%d", cols);
454	ptr = strchr(ptr, ':');
455	strcat(newtc, ptr);
456
457	/* now do lines */
458	if ((ptr = x_strindex(newtc, "li#")) == NULL) {
459	    fprintf(stderr, "%s: No `li#'\n", myname);
460	    exit(1);
461	}
462
463	i = ptr - newtc + 3;
464	strncpy(termcap, newtc, (unsigned) i);
465	sprintf(termcap + i, "%d", rows);
466	ptr = strchr(ptr, ':');
467	strcat(termcap, ptr);
468    }
469#endif /* USE_TERMCAP */
470
471    if (SHELL_BOURNE == shell_type) {
472
473#ifdef USE_TERMCAP
474	if (ok_tcap) {
475	    printf("%sTERMCAP=", setname);
476	    print_termcap(termcap);
477	    printf(";\nexport TERMCAP;\n");
478	}
479#endif /* USE_TERMCAP */
480#ifdef USE_TERMINFO
481	printf("%sCOLUMNS=%d;\nLINES=%d;\nexport COLUMNS LINES;\n",
482	       setname, cols, rows);
483#endif /* USE_TERMINFO */
484
485    } else {			/* not Bourne shell */
486
487#ifdef USE_TERMCAP
488	if (ok_tcap) {
489	    printf("set noglob;\n%ssetenv TERMCAP ", setname);
490	    print_termcap(termcap);
491	    printf(";\nunset noglob;\n");
492	}
493#endif /* USE_TERMCAP */
494#ifdef USE_TERMINFO
495	printf("set noglob;\n%ssetenv COLUMNS '%d';\nsetenv LINES '%d';\nunset noglob;\n",
496	       setname, cols, rows);
497#endif /* USE_TERMINFO */
498    }
499    exit(0);
500}
501
502static int
503checkdigits(register char *str)
504{
505    while (*str) {
506	if (!isdigit(CharOf(*str)))
507	    return (0);
508	str++;
509    }
510    return (1);
511}
512
513static void
514readstring(register FILE *fp, register char *buf, char *str)
515{
516    register int last, c;
517#if !defined(USG) && !defined(__UNIXOS2__)
518    /* What is the advantage of setitimer() over alarm()? */
519    struct itimerval it;
520#endif
521
522    signal(SIGALRM, resize_timeout);
523#if defined(USG) || defined(__UNIXOS2__)
524    alarm(TIMEOUT);
525#else
526    bzero((char *) &it, sizeof(struct itimerval));
527    it.it_value.tv_sec = TIMEOUT;
528    setitimer(ITIMER_REAL, &it, (struct itimerval *) NULL);
529#endif
530    if ((c = getc(fp)) == 0233) {	/* meta-escape, CSI */
531	*buf++ = c = ESCAPE("")[0];
532	*buf++ = '[';
533    } else {
534	*buf++ = c;
535    }
536    if (c != *str) {
537	fprintf(stderr, "%s: unknown character, exiting.\r\n", myname);
538	onintr(0);
539    }
540    last = str[strlen(str) - 1];
541    while ((*buf++ = getc(fp)) != last) ;
542#if defined(USG) || defined(__UNIXOS2__)
543    alarm(0);
544#else
545    bzero((char *) &it, sizeof(struct itimerval));
546    setitimer(ITIMER_REAL, &it, (struct itimerval *) NULL);
547#endif
548    *buf = 0;
549}
550
551static void
552Usage(void)
553{
554    fprintf(stderr, strcmp(myname, sunname) == 0 ?
555	    "Usage: %s [rows cols]\n" :
556	    "Usage: %s [-u] [-c] [-s [rows cols]]\n", myname);
557    exit(1);
558}
559
560static SIGNAL_T
561resize_timeout(int sig)
562{
563    fprintf(stderr, "\n%s: Time out occurred\r\n", myname);
564    onintr(sig);
565}
566
567/* ARGSUSED */
568static SIGNAL_T
569onintr(int sig GCC_UNUSED)
570{
571#ifdef USE_ANY_SYSV_TERMIO
572    ioctl(tty, TCSETAW, &tioorig);
573#elif defined(USE_TERMIOS)
574    tcsetattr(tty, TCSADRAIN, &tioorig);
575#else /* not USE_TERMIOS */
576    ioctl(tty, TIOCSETP, &sgorig);
577#endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */
578    exit(1);
579}
580