xref: /haiku/src/bin/watch.c (revision 7abb735ac6031db3171bc4d55327b2dac0a48baf)
1*7abb735aSJérôme Duval /* watch -- execute a program repeatedly, displaying output fullscreen
2*7abb735aSJérôme Duval  *
3*7abb735aSJérôme Duval  * Based on the original 1991 'watch' by Tony Rems <rembo@unisoft.com>
4*7abb735aSJérôme Duval  * (with mods and corrections by Francois Pinard).
5*7abb735aSJérôme Duval  *
6*7abb735aSJérôme Duval  * Substantially reworked, new features (differences option, SIGWINCH
7*7abb735aSJérôme Duval  * handling, unlimited command length, long line handling) added Apr 1999 by
8*7abb735aSJérôme Duval  * Mike Coleman <mkc@acm.org>.
9*7abb735aSJérôme Duval  *
10*7abb735aSJérôme Duval  * Changes by Albert Cahalan, 2002-2003.
11*7abb735aSJérôme Duval  */
12*7abb735aSJérôme Duval 
13*7abb735aSJérôme Duval #define VERSION "0.2.0"
14*7abb735aSJérôme Duval 
15*7abb735aSJérôme Duval #include <ctype.h>
16*7abb735aSJérôme Duval #include <getopt.h>
17*7abb735aSJérôme Duval #include <signal.h>
18*7abb735aSJérôme Duval #include <ncurses.h>
19*7abb735aSJérôme Duval #include <stdio.h>
20*7abb735aSJérôme Duval #include <stdlib.h>
21*7abb735aSJérôme Duval #include <string.h>
22*7abb735aSJérôme Duval #include <sys/ioctl.h>
23*7abb735aSJérôme Duval #include <time.h>
24*7abb735aSJérôme Duval #include <unistd.h>
25*7abb735aSJérôme Duval #include <locale.h>
26*7abb735aSJérôme Duval /* #include "proc/procps.h" */
27*7abb735aSJérôme Duval 
28*7abb735aSJérôme Duval static struct option longopts[] = {
29*7abb735aSJérôme Duval 	{"differences", optional_argument, 0, 'd'},
30*7abb735aSJérôme Duval 	{"help", no_argument, 0, 'h'},
31*7abb735aSJérôme Duval 	{"interval", required_argument, 0, 'n'},
32*7abb735aSJérôme Duval 	{"no-title", no_argument, 0, 't'},
33*7abb735aSJérôme Duval 	{"version", no_argument, 0, 'v'},
34*7abb735aSJérôme Duval 	{0, 0, 0, 0}
35*7abb735aSJérôme Duval };
36*7abb735aSJérôme Duval 
37*7abb735aSJérôme Duval static char usage[] =
38*7abb735aSJérôme Duval     "Usage: %s [-dhntv] [--differences[=cumulative]] [--help] [--interval=<n>] [--no-title] [--version] <command>\n";
39*7abb735aSJérôme Duval 
40*7abb735aSJérôme Duval static char *progname;
41*7abb735aSJérôme Duval 
42*7abb735aSJérôme Duval static int curses_started = 0;
43*7abb735aSJérôme Duval static int height = 24, width = 80;
44*7abb735aSJérôme Duval static int screen_size_changed = 0;
45*7abb735aSJérôme Duval static int first_screen = 1;
46*7abb735aSJérôme Duval static int show_title = 2;  // number of lines used, 2 or 0
47*7abb735aSJérôme Duval 
48*7abb735aSJérôme Duval #define min(x,y) ((x) > (y) ? (y) : (x))
49*7abb735aSJérôme Duval 
50*7abb735aSJérôme Duval //static void do_usage(void) NORETURN;
do_usage(void)51*7abb735aSJérôme Duval static void do_usage(void)
52*7abb735aSJérôme Duval {
53*7abb735aSJérôme Duval 	fprintf(stderr, usage, progname);
54*7abb735aSJérôme Duval 	exit(1);
55*7abb735aSJérôme Duval }
56*7abb735aSJérôme Duval 
57*7abb735aSJérôme Duval //static void do_exit(int status) NORETURN;
do_exit(int status)58*7abb735aSJérôme Duval static void do_exit(int status)
59*7abb735aSJérôme Duval {
60*7abb735aSJérôme Duval 	if (curses_started)
61*7abb735aSJérôme Duval 		endwin();
62*7abb735aSJérôme Duval 	exit(status);
63*7abb735aSJérôme Duval }
64*7abb735aSJérôme Duval 
65*7abb735aSJérôme Duval /* signal handler */
66*7abb735aSJérôme Duval //static void die(int notused) NORETURN;
die(int notused)67*7abb735aSJérôme Duval static void die(int notused)
68*7abb735aSJérôme Duval {
69*7abb735aSJérôme Duval 	(void) notused;
70*7abb735aSJérôme Duval 	do_exit(0);
71*7abb735aSJérôme Duval }
72*7abb735aSJérôme Duval 
73*7abb735aSJérôme Duval static void
winch_handler(int notused)74*7abb735aSJérôme Duval winch_handler(int notused)
75*7abb735aSJérôme Duval {
76*7abb735aSJérôme Duval 	(void) notused;
77*7abb735aSJérôme Duval 	screen_size_changed = 1;
78*7abb735aSJérôme Duval }
79*7abb735aSJérôme Duval 
80*7abb735aSJérôme Duval static void
get_terminal_size(void)81*7abb735aSJérôme Duval get_terminal_size(void)
82*7abb735aSJérôme Duval {
83*7abb735aSJérôme Duval 	struct winsize w;
84*7abb735aSJérôme Duval 	if (ioctl(2, TIOCGWINSZ, &w) == 0) {
85*7abb735aSJérôme Duval 		if (w.ws_row > 0)
86*7abb735aSJérôme Duval 			height = w.ws_row;
87*7abb735aSJérôme Duval 		if (w.ws_col > 0)
88*7abb735aSJérôme Duval 			width = w.ws_col;
89*7abb735aSJérôme Duval 	}
90*7abb735aSJérôme Duval }
91*7abb735aSJérôme Duval 
92*7abb735aSJérôme Duval int
main(int argc,char * argv[])93*7abb735aSJérôme Duval main(int argc, char *argv[])
94*7abb735aSJérôme Duval {
95*7abb735aSJérôme Duval 	int optc;
96*7abb735aSJérôme Duval 	int option_differences = 0,
97*7abb735aSJérôme Duval 	    option_differences_cumulative = 0,
98*7abb735aSJérôme Duval 	    option_help = 0, option_version = 0;
99*7abb735aSJérôme Duval 	int interval = 2;
100*7abb735aSJérôme Duval 	char *command;
101*7abb735aSJérôme Duval 	int command_length = 0;	/* not including final \0 */
102*7abb735aSJérôme Duval 
103*7abb735aSJérôme Duval 	setlocale(LC_ALL, "");
104*7abb735aSJérôme Duval 	progname = argv[0];
105*7abb735aSJérôme Duval 
106*7abb735aSJérôme Duval 	while ((optc = getopt_long(argc, argv, "+d::hn:vt", longopts, (int *) 0))
107*7abb735aSJérôme Duval 	       != EOF) {
108*7abb735aSJérôme Duval 		switch (optc) {
109*7abb735aSJérôme Duval 		case 'd':
110*7abb735aSJérôme Duval 			option_differences = 1;
111*7abb735aSJérôme Duval 			if (optarg)
112*7abb735aSJérôme Duval 				option_differences_cumulative = 1;
113*7abb735aSJérôme Duval 			break;
114*7abb735aSJérôme Duval 		case 'h':
115*7abb735aSJérôme Duval 			option_help = 1;
116*7abb735aSJérôme Duval 			break;
117*7abb735aSJérôme Duval 		case 't':
118*7abb735aSJérôme Duval 			show_title = 0;
119*7abb735aSJérôme Duval 			break;
120*7abb735aSJérôme Duval 		case 'n':
121*7abb735aSJérôme Duval 			{
122*7abb735aSJérôme Duval 				char *str;
123*7abb735aSJérôme Duval 				interval = strtol(optarg, &str, 10);
124*7abb735aSJérôme Duval 				if (!*optarg || *str)
125*7abb735aSJérôme Duval 					do_usage();
126*7abb735aSJérôme Duval 			}
127*7abb735aSJérôme Duval 			break;
128*7abb735aSJérôme Duval 		case 'v':
129*7abb735aSJérôme Duval 			option_version = 1;
130*7abb735aSJérôme Duval 			break;
131*7abb735aSJérôme Duval 		default:
132*7abb735aSJérôme Duval 			do_usage();
133*7abb735aSJérôme Duval 			break;
134*7abb735aSJérôme Duval 		}
135*7abb735aSJérôme Duval 	}
136*7abb735aSJérôme Duval 
137*7abb735aSJérôme Duval 	if (option_version) {
138*7abb735aSJérôme Duval 		fprintf(stderr, "%s\n", VERSION);
139*7abb735aSJérôme Duval 		if (!option_help)
140*7abb735aSJérôme Duval 			exit(0);
141*7abb735aSJérôme Duval 	}
142*7abb735aSJérôme Duval 
143*7abb735aSJérôme Duval 	if (option_help) {
144*7abb735aSJérôme Duval 		fprintf(stderr, usage, progname);
145*7abb735aSJérôme Duval 		fputs("  -d, --differences[=cumulative]\thighlight changes between updates\n", stderr);
146*7abb735aSJérôme Duval 		fputs("\t\t(cumulative means highlighting is cumulative)\n", stderr);
147*7abb735aSJérôme Duval 		fputs("  -h, --help\t\t\t\tprint a summary of the options\n", stderr);
148*7abb735aSJérôme Duval 		fputs("  -n, --interval=<seconds>\t\tseconds to wait between updates\n", stderr);
149*7abb735aSJérôme Duval 		fputs("  -v, --version\t\t\t\tprint the version number\n", stderr);
150*7abb735aSJérôme Duval 		fputs("  -t, --no-title\t\t\tturns off showing the header\n", stderr);
151*7abb735aSJérôme Duval 		exit(0);
152*7abb735aSJérôme Duval 	}
153*7abb735aSJérôme Duval 
154*7abb735aSJérôme Duval 	if (optind >= argc)
155*7abb735aSJérôme Duval 		do_usage();
156*7abb735aSJérôme Duval 
157*7abb735aSJérôme Duval 	command = strdup(argv[optind++]);
158*7abb735aSJérôme Duval 	command_length = strlen(command);
159*7abb735aSJérôme Duval 	for (; optind < argc; optind++) {
160*7abb735aSJérôme Duval 		char *endp;
161*7abb735aSJérôme Duval 		int s = strlen(argv[optind]);
162*7abb735aSJérôme Duval 		command = realloc(command, command_length + s + 2);	/* space and \0 */
163*7abb735aSJérôme Duval 		endp = command + command_length;
164*7abb735aSJérôme Duval 		*endp = ' ';
165*7abb735aSJérôme Duval 		memcpy(endp + 1, argv[optind], s);
166*7abb735aSJérôme Duval 		command_length += 1 + s;	/* space then string length */
167*7abb735aSJérôme Duval 		command[command_length] = '\0';
168*7abb735aSJérôme Duval 	}
169*7abb735aSJérôme Duval 
170*7abb735aSJérôme Duval 	get_terminal_size();
171*7abb735aSJérôme Duval 
172*7abb735aSJérôme Duval 	/* Catch keyboard interrupts so we can put tty back in a sane state.  */
173*7abb735aSJérôme Duval 	signal(SIGINT, die);
174*7abb735aSJérôme Duval 	signal(SIGTERM, die);
175*7abb735aSJérôme Duval 	signal(SIGHUP, die);
176*7abb735aSJérôme Duval 	signal(SIGWINCH, winch_handler);
177*7abb735aSJérôme Duval 
178*7abb735aSJérôme Duval 	/* Set up tty for curses use.  */
179*7abb735aSJérôme Duval 	curses_started = 1;
180*7abb735aSJérôme Duval 	initscr();
181*7abb735aSJérôme Duval 	nonl();
182*7abb735aSJérôme Duval 	noecho();
183*7abb735aSJérôme Duval 	cbreak();
184*7abb735aSJérôme Duval 
185*7abb735aSJérôme Duval 	for (;;) {
186*7abb735aSJérôme Duval 		time_t t = time(NULL);
187*7abb735aSJérôme Duval 		char *ts = ctime(&t);
188*7abb735aSJérôme Duval 		int tsl = strlen(ts);
189*7abb735aSJérôme Duval 		char *header;
190*7abb735aSJérôme Duval 		FILE *p;
191*7abb735aSJérôme Duval 		int x, y;
192*7abb735aSJérôme Duval 		int oldeolseen = 1;
193*7abb735aSJérôme Duval 
194*7abb735aSJérôme Duval 		if (screen_size_changed) {
195*7abb735aSJérôme Duval 			get_terminal_size();
196*7abb735aSJérôme Duval 			resizeterm(height, width);
197*7abb735aSJérôme Duval 			clear();
198*7abb735aSJérôme Duval 			/* redrawwin(stdscr); */
199*7abb735aSJérôme Duval 			screen_size_changed = 0;
200*7abb735aSJérôme Duval 			first_screen = 1;
201*7abb735aSJérôme Duval 		}
202*7abb735aSJérôme Duval 
203*7abb735aSJérôme Duval 		if (show_title) {
204*7abb735aSJérôme Duval 			// left justify interval and command,
205*7abb735aSJérôme Duval 			// right justify time, clipping all to fit window width
206*7abb735aSJérôme Duval 			asprintf(&header, "Every %ds: %.*s",
207*7abb735aSJérôme Duval 				 interval, min(width - 1, command_length), command);
208*7abb735aSJérôme Duval 			mvaddstr(0, 0, header);
209*7abb735aSJérôme Duval 			if (strlen(header) > (size_t) (width - tsl - 1))
210*7abb735aSJérôme Duval 				mvaddstr(0, width - tsl - 4, "...  ");
211*7abb735aSJérôme Duval 			mvaddstr(0, width - tsl + 1, ts);
212*7abb735aSJérôme Duval 			free(header);
213*7abb735aSJérôme Duval 		}
214*7abb735aSJérôme Duval 
215*7abb735aSJérôme Duval 		if (!(p = popen(command, "r"))) {
216*7abb735aSJérôme Duval 			perror("popen");
217*7abb735aSJérôme Duval 			do_exit(2);
218*7abb735aSJérôme Duval 		}
219*7abb735aSJérôme Duval 
220*7abb735aSJérôme Duval 		for (y = show_title; y < height; y++) {
221*7abb735aSJérôme Duval 			int eolseen = 0, tabpending = 0;
222*7abb735aSJérôme Duval 			for (x = 0; x < width; x++) {
223*7abb735aSJérôme Duval 				int c = ' ';
224*7abb735aSJérôme Duval 				int attr = 0;
225*7abb735aSJérôme Duval 
226*7abb735aSJérôme Duval 				if (!eolseen) {
227*7abb735aSJérôme Duval 					/* if there is a tab pending, just spit spaces until the
228*7abb735aSJérôme Duval 					   next stop instead of reading characters */
229*7abb735aSJérôme Duval 					if (!tabpending)
230*7abb735aSJérôme Duval 						do
231*7abb735aSJérôme Duval 							c = getc(p);
232*7abb735aSJérôme Duval 						while (c != EOF && !isprint(c)
233*7abb735aSJérôme Duval 						       && c != '\n'
234*7abb735aSJérôme Duval 						       && c != '\t');
235*7abb735aSJérôme Duval 					if (c == '\n')
236*7abb735aSJérôme Duval 						if (!oldeolseen && x == 0) {
237*7abb735aSJérôme Duval 							x = -1;
238*7abb735aSJérôme Duval 							continue;
239*7abb735aSJérôme Duval 						} else
240*7abb735aSJérôme Duval 							eolseen = 1;
241*7abb735aSJérôme Duval 					else if (c == '\t')
242*7abb735aSJérôme Duval 						tabpending = 1;
243*7abb735aSJérôme Duval 					if (c == EOF || c == '\n' || c == '\t')
244*7abb735aSJérôme Duval 						c = ' ';
245*7abb735aSJérôme Duval 					if (tabpending && (((x + 1) % 8) == 0))
246*7abb735aSJérôme Duval 						tabpending = 0;
247*7abb735aSJérôme Duval 				}
248*7abb735aSJérôme Duval 				move(y, x);
249*7abb735aSJérôme Duval 				if (option_differences) {
250*7abb735aSJérôme Duval 					int oldch = inch();
251*7abb735aSJérôme Duval 					char oldc = oldch & A_CHARTEXT;
252*7abb735aSJérôme Duval 					attr = !first_screen
253*7abb735aSJérôme Duval 					    && (c != oldc
254*7abb735aSJérôme Duval 						||
255*7abb735aSJérôme Duval 						(option_differences_cumulative
256*7abb735aSJérôme Duval 						 && (oldch & A_ATTRIBUTES)));
257*7abb735aSJérôme Duval 				}
258*7abb735aSJérôme Duval 				if (attr)
259*7abb735aSJérôme Duval 					standout();
260*7abb735aSJérôme Duval 				addch(c);
261*7abb735aSJérôme Duval 				if (attr)
262*7abb735aSJérôme Duval 					standend();
263*7abb735aSJérôme Duval 			}
264*7abb735aSJérôme Duval 			oldeolseen = eolseen;
265*7abb735aSJérôme Duval 		}
266*7abb735aSJérôme Duval 
267*7abb735aSJérôme Duval 		pclose(p);
268*7abb735aSJérôme Duval 
269*7abb735aSJérôme Duval 		first_screen = 0;
270*7abb735aSJérôme Duval 		refresh();
271*7abb735aSJérôme Duval 		sleep(interval);
272*7abb735aSJérôme Duval 	}
273*7abb735aSJérôme Duval 
274*7abb735aSJérôme Duval 	endwin();
275*7abb735aSJérôme Duval 
276*7abb735aSJérôme Duval 	return 0;
277*7abb735aSJérôme Duval }
278