initial commit
[profile/ivi/xterm.git] / resize.c
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
90 int ignore_unused;
91 #endif
92
93 #ifdef X_NOT_POSIX
94 #if !defined(SYSV) && !defined(i386)
95 extern 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* */
115 static 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
131 static const char *emuname[EMULATIONS] =
132 {
133     "VT100",
134     "Sun",
135 };
136 static char *myname;
137 static int shell_type = SHELL_UNKNOWN;
138 static 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)
145 static const char *getwsize[EMULATIONS] =
146 {                               /* size in pixels */
147     0,
148     ESCAPE("[14t"),
149 };
150 #endif /* USE_STRUCT_{TTYSIZE|WINSIZE} */
151 static const char *restore[EMULATIONS] =
152 {
153     ESCAPE("8"),
154     0,
155 };
156 static const char *setname = "";
157 static const char *setsize[EMULATIONS] =
158 {
159     0,
160     ESCAPE("[8;%s;%st"),
161 };
162
163 #ifdef USE_ANY_SYSV_TERMIO
164 static struct termio tioorig;
165 #elif defined(USE_TERMIOS)
166 static struct termios tioorig;
167 #else
168 static struct sgttyb sgorig;
169 #endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */
170
171 static const char *size[EMULATIONS] =
172 {
173     ESCAPE("[%d;%dR"),
174     ESCAPE("[8;%d;%dt"),
175 };
176 static char sunname[] = "sunsize";
177 static int tty;
178 static FILE *ttyfp;
179
180 #if defined(USE_STRUCT_TTYSIZE)
181 #elif defined(USE_STRUCT_WINSIZE)
182 static const char *wsize[EMULATIONS] =
183 {
184     0,
185     ESCAPE("[4;%hd;%hdt"),
186 };
187 #endif /* USE_STRUCT_{TTYSIZE|WINSIZE} */
188
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);
194
195 #ifdef USE_TERMCAP
196 static void
197 print_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  */
224 int
225 main(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
503 static int
504 checkdigits(char *str)
505 {
506     while (*str) {
507         if (!isdigit(CharOf(*str)))
508             return (0);
509         str++;
510     }
511     return (1);
512 }
513
514 static void
515 readstring(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
555 static void
556 Usage(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
564 static SIGNAL_T
565 resize_timeout(int sig)
566 {
567     fprintf(stderr, "\n%s: Time out occurred\r\n", myname);
568     onintr(sig);
569 }
570
571 /* ARGSUSED */
572 static SIGNAL_T
573 onintr(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 }