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