1 /* $XTermId: resize.c,v 1.144 2020/06/03 00:26:23 tom Exp $ */
2
3 /*
4 * Copyright 2003-2018,2020 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 <stdio.h>
58 #include <ctype.h>
59
60 #ifdef RESIZE_ONLY
61 #include "resize.h"
62 #else
63 #include <xterm.h>
64 #include <version.h>
65 #include <xstrings.h>
66 #include <xtermcap.h>
67 #include <xterm_io.h>
68 #endif
69
70 #ifndef USE_TERMINFO /* avoid conflict with configure script */
71 #if defined(__QNX__) || defined(__SCO__) || defined(linux) || defined(__OpenBSD__) || defined(__UNIXWARE__)
72 #define USE_TERMINFO
73 #endif
74 #endif
75
76 #if defined(__QNX__)
77 #include <unix.h>
78 #endif
79
80 /*
81 * Some OS's may want to use both, like SCO for example. We catch here anyone
82 * who hasn't decided what they want.
83 */
84 #if !defined(USE_TERMCAP) && !defined(USE_TERMINFO)
85 #define USE_TERMINFO
86 #endif
87
88 #include <signal.h>
89 #include <pwd.h>
90
91 #ifdef USE_IGNORE_RC
92 int ignore_unused;
93 #endif
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
111 /* *INDENT-OFF* */
112 static struct {
113 const char *name;
114 int type;
115 } shell_list[] = {
116 { "csh", SHELL_C }, /* vanilla cshell */
117 { "jcsh", SHELL_C },
118 { "tcsh", SHELL_C },
119 { "sh", SHELL_BOURNE }, /* vanilla Bourne shell */
120 { "ash", SHELL_BOURNE },
121 { "bash", SHELL_BOURNE }, /* GNU Bourne again shell */
122 { "dash", SHELL_BOURNE },
123 { "jsh", SHELL_BOURNE },
124 { "ksh", SHELL_BOURNE }, /* Korn shell (from AT&T toolchest) */
125 { "ksh-i", SHELL_BOURNE }, /* another name for Korn shell */
126 { "ksh93", SHELL_BOURNE }, /* Korn shell */
127 { "mksh", SHELL_BOURNE },
128 { "pdksh", SHELL_BOURNE },
129 { "zsh", SHELL_BOURNE },
130 { NULL, SHELL_BOURNE } /* default (same as xterm's) */
131 };
132 /* *INDENT-ON* */
133
134 static const char *const emuname[EMULATIONS] =
135 {
136 "VT100",
137 "Sun",
138 };
139 static char *myname;
140 static int shell_type = SHELL_UNKNOWN;
141 static const char *const getsize[EMULATIONS] =
142 {
143 ESCAPE("7") ESCAPE("[r") ESCAPE("[9999;9999H") ESCAPE("[6n"),
144 ESCAPE("[18t"),
145 };
146 #if defined(USE_STRUCT_WINSIZE)
147 static const char *const getwsize[EMULATIONS] =
148 { /* size in pixels */
149 0,
150 ESCAPE("[14t"),
151 };
152 #endif /* USE_STRUCT_WINSIZE */
153 static const char *const restore[EMULATIONS] =
154 {
155 ESCAPE("8"),
156 0,
157 };
158 static const char *const setsize[EMULATIONS] =
159 {
160 0,
161 ESCAPE("[8;%s;%st"),
162 };
163
164 #ifdef USE_ANY_SYSV_TERMIO
165 static struct termio tioorig;
166 #elif defined(USE_TERMIOS)
167 static struct termios tioorig;
168 #else
169 static struct sgttyb sgorig;
170 #endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */
171
172 static const char *const size[EMULATIONS] =
173 {
174 ESCAPE("[%d;%dR"),
175 ESCAPE("[8;%d;%dt"),
176 };
177 static const char sunname[] = "sunsize";
178 static int tty;
179 static FILE *ttyfp;
180
181 #if defined(USE_STRUCT_WINSIZE)
182 static const char *wsize[EMULATIONS] =
183 {
184 0,
185 ESCAPE("[4;%hd;%hdt"),
186 };
187 #endif /* USE_STRUCT_WINSIZE */
188
189 static void failed(const char *) GCC_NORETURN;
190 static void onintr(int) GCC_NORETURN;
191 static void resize_timeout(int) GCC_NORETURN;
192 static void Usage(void) GCC_NORETURN;
193
194 static void
failed(const char * s)195 failed(const char *s)
196 {
197 int save = errno;
198 IGNORE_RC(write(2, myname, strlen(myname)));
199 IGNORE_RC(write(2, ": ", (size_t) 2));
200 errno = save;
201 perror(s);
202 exit(EXIT_FAILURE);
203 }
204
205 /* ARGSUSED */
206 static void
onintr(int sig GCC_UNUSED)207 onintr(int sig GCC_UNUSED)
208 {
209 #ifdef USE_ANY_SYSV_TERMIO
210 (void) ioctl(tty, TCSETAW, &tioorig);
211 #elif defined(USE_TERMIOS)
212 (void) tcsetattr(tty, TCSADRAIN, &tioorig);
213 #else /* not USE_TERMIOS */
214 (void) ioctl(tty, TIOCSETP, &sgorig);
215 #endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */
216 exit(EXIT_FAILURE);
217 }
218
219 static void
resize_timeout(int sig)220 resize_timeout(int sig)
221 {
222 fprintf(stderr, "\n%s: Time out occurred\r\n", myname);
223 onintr(sig);
224 }
225
226 static void
Usage(void)227 Usage(void)
228 {
229 fprintf(stderr, strcmp(myname, sunname) == 0 ?
230 "Usage: %s [rows cols]\n" :
231 "Usage: %s [-v] [-u] [-c] [-s [rows cols]]\n", myname);
232 exit(EXIT_FAILURE);
233 }
234
235 #ifdef USE_TERMCAP
236 static void
print_termcap(const char * termcap)237 print_termcap(const char *termcap)
238 {
239 int ch;
240
241 putchar('\'');
242 while ((ch = *termcap++) != '\0') {
243 switch (ch & 0xff) {
244 case 127: /* undo bug in GNU termcap */
245 printf("^?");
246 break;
247 case '\'': /* must escape anyway (unlikely) */
248 /* FALLTHRU */
249 case '!': /* must escape for SunOS csh */
250 putchar('\\');
251 /* FALLTHRU */
252 default:
253 putchar(ch);
254 break;
255 }
256 }
257 putchar('\'');
258 }
259 #endif /* USE_TERMCAP */
260
261 static int
checkdigits(char * str)262 checkdigits(char *str)
263 {
264 while (*str) {
265 if (!isdigit(CharOf(*str)))
266 return (0);
267 str++;
268 }
269 return (1);
270 }
271
272 static void
readstring(FILE * fp,char * buf,const char * str)273 readstring(FILE *fp, char *buf, const char *str)
274 {
275 int last, c;
276 #if !defined(USG) && !defined(__minix)
277 /* What is the advantage of setitimer() over alarm()? */
278 struct itimerval it;
279 #endif
280
281 signal(SIGALRM, resize_timeout);
282 #if defined(USG) || defined(__minix)
283 alarm(TIMEOUT);
284 #else
285 memset((char *) &it, 0, sizeof(struct itimerval));
286 it.it_value.tv_sec = TIMEOUT;
287 setitimer(ITIMER_REAL, &it, (struct itimerval *) NULL);
288 #endif
289 if ((c = getc(fp)) == 0233) { /* meta-escape, CSI */
290 c = ESCAPE("")[0];
291 *buf++ = (char) c;
292 *buf++ = '[';
293 } else {
294 *buf++ = (char) c;
295 }
296 if (c != *str) {
297 fprintf(stderr, "%s: unknown character, exiting.\r\n", myname);
298 onintr(0);
299 }
300 last = str[strlen(str) - 1];
301 while ((*buf++ = (char) getc(fp)) != last) {
302 ;
303 }
304 #if defined(USG) || defined(__minix)
305 alarm(0);
306 #else
307 memset((char *) &it, 0, sizeof(struct itimerval));
308 setitimer(ITIMER_REAL, &it, (struct itimerval *) NULL);
309 #endif
310 *buf = 0;
311 }
312
313 /*
314 resets termcap string to reflect current screen size
315 */
316 int
main(int argc,char ** argv ENVP_ARG)317 main(int argc, char **argv ENVP_ARG)
318 {
319 #ifdef USE_TERMCAP
320 char *env;
321 #endif
322 char *ptr;
323 int emu = VT100;
324 char *shell;
325 int i;
326 int rc;
327 int rows, cols;
328 #ifdef USE_ANY_SYSV_TERMIO
329 struct termio tio;
330 #elif defined(USE_TERMIOS)
331 struct termios tio;
332 #else
333 struct sgttyb sg;
334 #endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */
335 #ifdef USE_TERMCAP
336 int ok_tcap = 1;
337 char termcap[TERMCAP_SIZE];
338 char newtc[TERMCAP_SIZE];
339 #endif /* USE_TERMCAP */
340 char buf[BUFSIZ];
341 #ifdef TTYSIZE_STRUCT
342 TTYSIZE_STRUCT ts;
343 #endif
344 char *name_of_tty;
345 #ifdef CANT_OPEN_DEV_TTY
346 extern char *ttyname();
347 #endif
348 const char *setname = "";
349
350 myname = x_basename(argv[0]);
351 if (strcmp(myname, sunname) == 0)
352 emu = SUN;
353 for (argv++, argc--; argc > 0 && **argv == '-'; argv++, argc--) {
354 switch ((*argv)[1]) {
355 case 's': /* Sun emulation */
356 if (emu == SUN)
357 Usage(); /* Never returns */
358 emu = SUN;
359 break;
360 case 'u': /* Bourne (Unix) shell */
361 shell_type = SHELL_BOURNE;
362 break;
363 case 'c': /* C shell */
364 shell_type = SHELL_C;
365 break;
366 case 'v':
367 printf("%s\n", xtermVersion());
368 exit(EXIT_SUCCESS);
369 default:
370 Usage(); /* Never returns */
371 }
372 }
373
374 if (SHELL_UNKNOWN == shell_type) {
375 /* Find out what kind of shell this user is running.
376 * This is the same algorithm that xterm uses.
377 */
378 if ((ptr = x_getenv("SHELL")) == NULL) {
379 uid_t uid = getuid();
380 struct passwd pw;
381
382 if (x_getpwuid(uid, &pw)) {
383 (void) x_getlogin(uid, &pw);
384 }
385 if (!OkPasswd(&pw)
386 || *(ptr = pw.pw_shell) == 0) {
387 /* this is the same default that xterm uses */
388 ptr = x_strdup("/bin/sh");
389 }
390 }
391
392 shell = x_basename(ptr);
393
394 /* now that we know, what kind is it? */
395 for (i = 0; shell_list[i].name; i++) {
396 if (!strcmp(shell_list[i].name, shell)) {
397 break;
398 }
399 }
400 shell_type = shell_list[i].type;
401 }
402
403 if (argc == 2) {
404 if (!setsize[emu]) {
405 fprintf(stderr,
406 "%s: Can't set window size under %s emulation\n",
407 myname, emuname[emu]);
408 exit(EXIT_FAILURE);
409 }
410 if (!checkdigits(argv[0]) || !checkdigits(argv[1])) {
411 Usage(); /* Never returns */
412 }
413 } else if (argc != 0) {
414 Usage(); /* Never returns */
415 }
416 #ifdef CANT_OPEN_DEV_TTY
417 if ((name_of_tty = ttyname(fileno(stderr))) == NULL)
418 #endif
419 name_of_tty = x_strdup("/dev/tty");
420
421 if ((ttyfp = fopen(name_of_tty, "r+")) == NULL) {
422 fprintf(stderr, "%s: can't open terminal %s\n",
423 myname, name_of_tty);
424 exit(EXIT_FAILURE);
425 }
426 tty = fileno(ttyfp);
427 #ifdef USE_TERMCAP
428 if ((env = x_getenv("TERM")) == 0) {
429 env = x_strdup(DFT_TERMTYPE);
430 if (SHELL_BOURNE == shell_type) {
431 setname = "TERM=" DFT_TERMTYPE ";\nexport TERM;\n";
432 } else {
433 setname = "setenv TERM " DFT_TERMTYPE ";\n";
434 }
435 }
436 termcap[0] = 0; /* ...just in case we've accidentally gotten terminfo */
437 if (tgetent(termcap, env) <= 0 || termcap[0] == 0) {
438 ok_tcap = 0;
439 }
440 #endif /* USE_TERMCAP */
441 #ifdef USE_TERMINFO
442 if (x_getenv("TERM") == 0) {
443 if (SHELL_BOURNE == shell_type) {
444 setname = "TERM=" DFT_TERMTYPE ";\nexport TERM;\n";
445 } else {
446 setname = "setenv TERM " DFT_TERMTYPE ";\n";
447 }
448 }
449 #endif /* USE_TERMINFO */
450
451 #ifdef USE_ANY_SYSV_TERMIO
452 rc = ioctl(tty, TCGETA, &tioorig);
453 tio = tioorig;
454 UIntClr(tio.c_iflag, (ICRNL | IUCLC));
455 UIntClr(tio.c_lflag, (ICANON | ECHO));
456 tio.c_cflag |= CS8;
457 tio.c_cc[VMIN] = 6;
458 tio.c_cc[VTIME] = 1;
459 #elif defined(USE_TERMIOS)
460 rc = tcgetattr(tty, &tioorig);
461 tio = tioorig;
462 UIntClr(tio.c_iflag, ICRNL);
463 UIntClr(tio.c_lflag, (ICANON | ECHO));
464 tio.c_cflag |= CS8;
465 tio.c_cc[VMIN] = 6;
466 tio.c_cc[VTIME] = 1;
467 #else /* not USE_TERMIOS */
468 rc = ioctl(tty, TIOCGETP, &sgorig);
469 sg = sgorig;
470 sg.sg_flags |= RAW;
471 UIntClr(sg.sg_flags, ECHO);
472 #endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */
473 if (rc != 0)
474 failed("get tty settings");
475
476 signal(SIGINT, onintr);
477 signal(SIGQUIT, onintr);
478 signal(SIGTERM, onintr);
479
480 #ifdef USE_ANY_SYSV_TERMIO
481 rc = ioctl(tty, TCSETAW, &tio);
482 #elif defined(USE_TERMIOS)
483 rc = tcsetattr(tty, TCSADRAIN, &tio);
484 #else /* not USE_TERMIOS */
485 rc = ioctl(tty, TIOCSETP, &sg);
486 #endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */
487 if (rc != 0)
488 failed("set tty settings");
489
490 if (argc == 2) { /* look for optional parameters of "-s" */
491 char *tmpbuf = TypeMallocN(char,
492 strlen(setsize[emu]) +
493 strlen(argv[0]) +
494 strlen(argv[1]) +
495 1);
496 if (tmpbuf == 0) {
497 fprintf(stderr, "%s: Cannot query size\n", myname);
498 onintr(0);
499 } else {
500 sprintf(tmpbuf, setsize[emu], argv[0], argv[1]);
501 IGNORE_RC(write(tty, tmpbuf, strlen(tmpbuf)));
502 free(tmpbuf);
503 }
504 }
505 IGNORE_RC(write(tty, getsize[emu], strlen(getsize[emu])));
506 readstring(ttyfp, buf, size[emu]);
507 if (sscanf(buf, size[emu], &rows, &cols) != 2) {
508 fprintf(stderr, "%s: Can't get rows and columns\r\n", myname);
509 onintr(0);
510 }
511 if (restore[emu])
512 IGNORE_RC(write(tty, restore[emu], strlen(restore[emu])));
513 #if defined(USE_STRUCT_WINSIZE)
514 /* finally, set the tty's window size */
515 if (getwsize[emu]) {
516 /* get the window size in pixels */
517 IGNORE_RC(write(tty, getwsize[emu], strlen(getwsize[emu])));
518 readstring(ttyfp, buf, wsize[emu]);
519 if (sscanf(buf, wsize[emu], &ts.ws_xpixel, &ts.ws_ypixel) != 2) {
520 fprintf(stderr, "%s: Can't get window size\r\n", myname);
521 onintr(0);
522 }
523 setup_winsize(ts, rows, cols, 0, 0);
524 SET_TTYSIZE(tty, ts);
525 } else if (ioctl(tty, TIOCGWINSZ, &ts) != -1) {
526 /* we don't have any way of directly finding out
527 the current height & width of the window in pixels. We try
528 our best by computing the font height and width from the "old"
529 window-size values, and multiplying by these ratios... */
530 #define scaled(old,new,len) (old)?((unsigned)(new)*(len)/(old)):(len)
531 setup_winsize(ts, rows, cols,
532 scaled(TTYSIZE_ROWS(ts), rows, ts.ws_ypixel),
533 scaled(TTYSIZE_COLS(ts), cols, ts.ws_xpixel));
534 SET_TTYSIZE(tty, ts);
535 }
536 #endif /* USE_STRUCT_WINSIZE */
537
538 #ifdef USE_ANY_SYSV_TERMIO
539 rc = ioctl(tty, TCSETAW, &tioorig);
540 #elif defined(USE_TERMIOS)
541 rc = tcsetattr(tty, TCSADRAIN, &tioorig);
542 #else /* not USE_TERMIOS */
543 rc = ioctl(tty, TIOCSETP, &sgorig);
544 #endif /* USE_ANY_SYSV_TERMIO/USE_TERMIOS */
545 if (rc != 0)
546 failed("set tty settings");
547
548 signal(SIGINT, SIG_DFL);
549 signal(SIGQUIT, SIG_DFL);
550 signal(SIGTERM, SIG_DFL);
551
552 #ifdef USE_TERMCAP
553 if (ok_tcap) {
554 /* update termcap string */
555 /* first do columns */
556 if ((ptr = x_strindex(termcap, "co#")) == NULL) {
557 fprintf(stderr, "%s: No `co#'\n", myname);
558 exit(EXIT_FAILURE);
559 }
560
561 i = (int) (ptr - termcap) + 3;
562 strncpy(newtc, termcap, (size_t) i);
563 sprintf(newtc + i, "%d", cols);
564 if ((ptr = strchr(ptr, ':')) != 0)
565 strcat(newtc, ptr);
566
567 /* now do lines */
568 if ((ptr = x_strindex(newtc, "li#")) == NULL) {
569 fprintf(stderr, "%s: No `li#'\n", myname);
570 exit(EXIT_FAILURE);
571 }
572
573 i = (int) (ptr - newtc) + 3;
574 strncpy(termcap, newtc, (size_t) i);
575 sprintf(termcap + i, "%d", rows);
576 if ((ptr = strchr(ptr, ':')) != 0)
577 strcat(termcap, ptr);
578 }
579 #endif /* USE_TERMCAP */
580
581 if (SHELL_BOURNE == shell_type) {
582
583 #ifdef USE_TERMCAP
584 if (ok_tcap) {
585 printf("%sTERMCAP=", setname);
586 print_termcap(termcap);
587 printf(";\nexport TERMCAP;\n");
588 }
589 #endif /* USE_TERMCAP */
590 #ifdef USE_TERMINFO
591 printf("%sCOLUMNS=%d;\nLINES=%d;\nexport COLUMNS LINES;\n",
592 setname, cols, rows);
593 #endif /* USE_TERMINFO */
594
595 } else { /* not Bourne shell */
596
597 #ifdef USE_TERMCAP
598 if (ok_tcap) {
599 printf("set noglob;\n%ssetenv TERMCAP ", setname);
600 print_termcap(termcap);
601 printf(";\nunset noglob;\n");
602 }
603 #endif /* USE_TERMCAP */
604 #ifdef USE_TERMINFO
605 printf("set noglob;\n%ssetenv COLUMNS '%d';\nsetenv LINES '%d';\nunset noglob;\n",
606 setname, cols, rows);
607 #endif /* USE_TERMINFO */
608 }
609 exit(EXIT_SUCCESS);
610 }
611