There are no available options for this view.

Parent Directory Parent Directory | Revision Log Revision Log

Revision 1.16 - (show annotations) (download) (as text)
Mon May 28 03:18:52 2007 UTC (7 years, 6 months ago) by albert
Branch: MAIN
CVS Tags: HEAD
Changes since 1.15: +2 -2 lines
File MIME type: text/x-chdr
watch: tolerate umlauts
1 /* watch -- execute a program repeatedly, displaying output fullscreen
2 *
3 * Based on the original 1991 'watch' by Tony Rems <rembo@unisoft.com>
4 * (with mods and corrections by Francois Pinard).
5 *
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>.
9 *
10 * Changes by Albert Cahalan, 2002-2003.
11 */
12
13 #define VERSION "0.2.0"
14
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 <termios.h>
26 #include <locale.h>
27 #include "proc/procps.h"
28
29 #ifdef FORCE_8BIT
30 #undef isprint
31 #define isprint(x) ( (x>=' '&&x<='~') || (x>=0xa0) )
32 #endif
33
34 static struct option longopts[] = {
35 {"differences", optional_argument, 0, 'd'},
36 {"help", no_argument, 0, 'h'},
37 {"interval", required_argument, 0, 'n'},
38 {"no-title", no_argument, 0, 't'},
39 {"version", no_argument, 0, 'v'},
40 {0, 0, 0, 0}
41 };
42
43 static char usage[] =
44 "Usage: %s [-dhntv] [--differences[=cumulative]] [--help] [--interval=<n>] [--no-title] [--version] <command>\n";
45
46 static char *progname;
47
48 static int curses_started = 0;
49 static int height = 24, width = 80;
50 static int screen_size_changed = 0;
51 static int first_screen = 1;
52 static int show_title = 2; // number of lines used, 2 or 0
53
54 #define min(x,y) ((x) > (y) ? (y) : (x))
55
56 static void do_usage(void) NORETURN;
57 static void do_usage(void)
58 {
59 fprintf(stderr, usage, progname);
60 exit(1);
61 }
62
63 static void do_exit(int status) NORETURN;
64 static void do_exit(int status)
65 {
66 if (curses_started)
67 endwin();
68 exit(status);
69 }
70
71 /* signal handler */
72 static void die(int notused) NORETURN;
73 static void die(int notused)
74 {
75 (void) notused;
76 do_exit(0);
77 }
78
79 static void
80 winch_handler(int notused)
81 {
82 (void) notused;
83 screen_size_changed = 1;
84 }
85
86 static char env_col_buf[24];
87 static char env_row_buf[24];
88 static int incoming_cols;
89 static int incoming_rows;
90
91 static void
92 get_terminal_size(void)
93 {
94 struct winsize w;
95 if(!incoming_cols){ // have we checked COLUMNS?
96 const char *s = getenv("COLUMNS");
97 incoming_cols = -1;
98 if(s && *s){
99 long t;
100 char *endptr;
101 t = strtol(s, &endptr, 0);
102 if(!*endptr && (t>0) && (t<(long)666)) incoming_cols = (int)t;
103 width = incoming_cols;
104 snprintf(env_col_buf, sizeof env_col_buf, "COLUMNS=%d", width);
105 putenv(env_col_buf);
106 }
107 }
108 if(!incoming_rows){ // have we checked LINES?
109 const char *s = getenv("LINES");
110 incoming_rows = -1;
111 if(s && *s){
112 long t;
113 char *endptr;
114 t = strtol(s, &endptr, 0);
115 if(!*endptr && (t>0) && (t<(long)666)) incoming_rows = (int)t;
116 height = incoming_rows;
117 snprintf(env_row_buf, sizeof env_row_buf, "LINES=%d", height);
118 putenv(env_row_buf);
119 }
120 }
121 if (incoming_cols<0 || incoming_rows<0){
122 if (ioctl(2, TIOCGWINSZ, &w) == 0) {
123 if (incoming_rows<0 && w.ws_row > 0){
124 height = w.ws_row;
125 snprintf(env_row_buf, sizeof env_row_buf, "LINES=%d", height);
126 putenv(env_row_buf);
127 }
128 if (incoming_cols<0 && w.ws_col > 0){
129 width = w.ws_col;
130 snprintf(env_col_buf, sizeof env_col_buf, "COLUMNS=%d", width);
131 putenv(env_col_buf);
132 }
133 }
134 }
135 }
136
137 int
138 main(int argc, char *argv[])
139 {
140 int optc;
141 int option_differences = 0,
142 option_differences_cumulative = 0,
143 option_help = 0, option_version = 0;
144 double interval = 2;
145 char *command;
146 int command_length = 0; /* not including final \0 */
147
148 setlocale(LC_ALL, "");
149 progname = argv[0];
150
151 while ((optc = getopt_long(argc, argv, "+d::hn:vt", longopts, (int *) 0))
152 != EOF) {
153 switch (optc) {
154 case 'd':
155 option_differences = 1;
156 if (optarg)
157 option_differences_cumulative = 1;
158 break;
159 case 'h':
160 option_help = 1;
161 break;
162 case 't':
163 show_title = 0;
164 break;
165 case 'n':
166 {
167 char *str;
168 interval = strtod(optarg, &str);
169 if (!*optarg || *str)
170 do_usage();
171 if(interval < 0.1)
172 interval = 0.1;
173 if(interval > ~0u/1000000)
174 interval = ~0u/1000000;
175 }
176 break;
177 case 'v':
178 option_version = 1;
179 break;
180 default:
181 do_usage();
182 break;
183 }
184 }
185
186 if (option_version) {
187 fprintf(stderr, "%s\n", VERSION);
188 if (!option_help)
189 exit(0);
190 }
191
192 if (option_help) {
193 fprintf(stderr, usage, progname);
194 fputs(" -d, --differences[=cumulative]\thighlight changes between updates\n", stderr);
195 fputs("\t\t(cumulative means highlighting is cumulative)\n", stderr);
196 fputs(" -h, --help\t\t\t\tprint a summary of the options\n", stderr);
197 fputs(" -n, --interval=<seconds>\t\tseconds to wait between updates\n", stderr);
198 fputs(" -v, --version\t\t\t\tprint the version number\n", stderr);
199 fputs(" -t, --no-title\t\t\tturns off showing the header\n", stderr);
200 exit(0);
201 }
202
203 if (optind >= argc)
204 do_usage();
205
206 command = strdup(argv[optind++]);
207 command_length = strlen(command);
208 for (; optind < argc; optind++) {
209 char *endp;
210 int s = strlen(argv[optind]);
211 command = realloc(command, command_length + s + 2); /* space and \0 */
212 endp = command + command_length;
213 *endp = ' ';
214 memcpy(endp + 1, argv[optind], s);
215 command_length += 1 + s; /* space then string length */
216 command[command_length] = '\0';
217 }
218
219 get_terminal_size();
220
221 /* Catch keyboard interrupts so we can put tty back in a sane state. */
222 signal(SIGINT, die);
223 signal(SIGTERM, die);
224 signal(SIGHUP, die);
225 signal(SIGWINCH, winch_handler);
226
227 /* Set up tty for curses use. */
228 curses_started = 1;
229 initscr();
230 nonl();
231 noecho();
232 cbreak();
233
234 for (;;) {
235 time_t t = time(NULL);
236 char *ts = ctime(&t);
237 int tsl = strlen(ts);
238 char *header;
239 FILE *p;
240 int x, y;
241 int oldeolseen = 1;
242
243 if (screen_size_changed) {
244 get_terminal_size();
245 resizeterm(height, width);
246 clear();
247 /* redrawwin(stdscr); */
248 screen_size_changed = 0;
249 first_screen = 1;
250 }
251
252 if (show_title) {
253 // left justify interval and command,
254 // right justify time, clipping all to fit window width
255 asprintf(&header, "Every %.1fs: %.*s",
256 interval, min(width - 1, command_length), command);
257 mvaddstr(0, 0, header);
258 if (strlen(header) > (size_t) (width - tsl - 1))
259 mvaddstr(0, width - tsl - 4, "... ");
260 mvaddstr(0, width - tsl + 1, ts);
261 free(header);
262 }
263
264 if (!(p = popen(command, "r"))) {
265 perror("popen");
266 do_exit(2);
267 }
268
269 for (y = show_title; y < height; y++) {
270 int eolseen = 0, tabpending = 0;
271 for (x = 0; x < width; x++) {
272 int c = ' ';
273 int attr = 0;
274
275 if (!eolseen) {
276 /* if there is a tab pending, just spit spaces until the
277 next stop instead of reading characters */
278 if (!tabpending)
279 do
280 c = getc(p);
281 while (c != EOF && !isprint(c)
282 && c != '\n'
283 && c != '\t');
284 if (c == '\n')
285 if (!oldeolseen && x == 0) {
286 x = -1;
287 continue;
288 } else
289 eolseen = 1;
290 else if (c == '\t')
291 tabpending = 1;
292 if (c == EOF || c == '\n' || c == '\t')
293 c = ' ';
294 if (tabpending && (((x + 1) % 8) == 0))
295 tabpending = 0;
296 }
297 move(y, x);
298 if (option_differences) {
299 chtype oldch = inch();
300 char oldc = oldch & A_CHARTEXT;
301 attr = !first_screen
302 && ((char)c != oldc
303 ||
304 (option_differences_cumulative
305 && (oldch & A_ATTRIBUTES)));
306 }
307 if (attr)
308 standout();
309 addch(c);
310 if (attr)
311 standend();
312 }
313 oldeolseen = eolseen;
314 }
315
316 pclose(p);
317
318 first_screen = 0;
319 refresh();
320 usleep(interval * 1000000);
321 }
322
323 endwin();
324
325 return 0;
326 }