resizewin.c revision 297897
1/*
2 * resizewin
3 *
4 * Query terminal for size and inform the kernel
5 *
6 * Copyright 2015 EMC / Isilon Storage Division
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/usr.bin/resizewin/resizewin.c 297897 2016-04-13 00:30:42Z cem $");
32#include <sys/ioctl.h>
33#include <sys/time.h>
34#include <errno.h>
35#include <fcntl.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <strings.h>
39#include <termios.h>
40#include <unistd.h>
41
42/* screen doesn't support ESC[18t (return terminal size) so do it the hard way */
43static const char query[] =
44    "\0337"		/* Save cursor position */
45    "\033[r"		/* Scroll whole screen */
46    "\033[999;999H"	/* Move cursor */
47    "\033[6n"		/* Get cursor position */
48    "\0338";		/* Restore cursor position */
49int
50main(__unused int argc, __unused char **argv)
51{
52	struct termios old, new;
53	struct winsize w;
54	int ret, fd, cnt, err;
55	char data[20];
56	struct timeval then, now;
57
58	err = 0;
59
60	if ((fd = open("/dev/tty", O_RDWR | O_NONBLOCK)) == -1)
61		exit(1);
62
63	/* Disable echo */
64	if (tcgetattr(fd, &old) == -1)
65		exit(1);
66
67	new = old;
68	new.c_cflag |= (CLOCAL | CREAD);
69	new.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
70	if (tcsetattr(fd, TCSANOW, &new) == -1)
71		exit(1);
72
73	if (write(fd, query, sizeof(query)) != sizeof(query)) {
74		err = 1;
75		goto out;
76	}
77
78	/* Read the response */
79	bzero(data, sizeof(data));
80	gettimeofday(&then, NULL);
81	cnt = 0;
82	while (1) {
83		ret = read(fd, data + cnt, 1);
84
85		if (ret == -1) {
86			if (errno == EAGAIN) {
87				gettimeofday(&now, NULL);
88				timersub(&now, &then, &now);
89				if (now.tv_sec >= 2) {
90					fprintf(stderr, "\n\n\nTimeout reading from terminal\n");
91					fprintf(stderr, "Read %d bytes, %s\n", cnt, data);
92					err = 1;
93					goto out;
94				}
95
96				usleep(20000);
97				continue;
98			}
99			err = 1;
100			goto out;
101		}
102		if (data[cnt] == 'R')
103			break;
104
105		cnt++;
106		if (cnt == sizeof(data) - 2) {
107			fprintf(stderr, "Response too long\n");
108			err = 1;
109			goto out;
110		}
111	}
112
113	/* Parse */
114	if (sscanf(data, "\033[%hu;%huR", &w.ws_row, &w.ws_col) != 2) {
115		err = 1;
116		fprintf(stderr, "Unable to parse response\n");
117		goto out;
118	}
119
120	/* Finally, what we want */
121	if (ioctl(fd, TIOCSWINSZ, &w) == -1)
122		err = 1;
123 out:
124	/* Restore echo */
125	tcsetattr(fd, TCSANOW, &old);
126
127	close(fd);
128	exit(err);
129}
130