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"
22 #include <sys/ioctl.h>
27 #include "proc/procps.h"
31 #define isprint(x) ( (x>=' '&&x<='~') || (x>=0xa0) )
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'},
44 "Usage: %s [-dhntv] [--differences[=cumulative]] [--help] [--interval=<n>] [--no-title] [--version] <command>\n";
46 static char *progname;
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
54 #define min(x,y) ((x) > (y) ? (y) : (x))
56 static void do_usage(void) NORETURN;
57 static void do_usage(void)
59 fprintf(stderr, usage, progname);
63 static void do_exit(int status) NORETURN;
64 static void do_exit(int status)
72 static void die(int notused) NORETURN;
73 static void die(int notused)
80 winch_handler(int notused)
83 screen_size_changed = 1;
86 static char env_col_buf[24];
87 static char env_row_buf[24];
88 static int incoming_cols;
89 static int incoming_rows;
92 get_terminal_size(void)
95 if(!incoming_cols){ // have we checked COLUMNS?
96 const char *s = getenv("COLUMNS");
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);
108 if(!incoming_rows){ // have we checked LINES?
109 const char *s = getenv("LINES");
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);
121 if (incoming_cols<0 || incoming_rows<0){
122 if (ioctl(2, TIOCGWINSZ, &w) == 0) {
123 if (incoming_rows<0 && w.ws_row > 0){
125 snprintf(env_row_buf, sizeof env_row_buf, "LINES=%d", height);
128 if (incoming_cols<0 && w.ws_col > 0){
130 snprintf(env_col_buf, sizeof env_col_buf, "COLUMNS=%d", width);
138 main(int argc, char *argv[])
141 int option_differences = 0,
142 option_differences_cumulative = 0,
143 option_help = 0, option_version = 0;
146 int command_length = 0; /* not including final \0 */
148 setlocale(LC_ALL, "");
151 while ((optc = getopt_long(argc, argv, "+d::hn:vt", longopts, (int *) 0))
155 option_differences = 1;
157 option_differences_cumulative = 1;
168 interval = strtod(optarg, &str);
169 if (!*optarg || *str)
173 if(interval > ~0u/1000000)
174 interval = ~0u/1000000;
186 if (option_version) {
187 fprintf(stderr, "%s\n", VERSION);
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);
206 command = strdup(argv[optind++]);
207 command_length = strlen(command);
208 for (; optind < argc; optind++) {
210 int s = strlen(argv[optind]);
211 command = realloc(command, command_length + s + 2); /* space and \0 */
212 endp = command + command_length;
214 memcpy(endp + 1, argv[optind], s);
215 command_length += 1 + s; /* space then string length */
216 command[command_length] = '\0';
221 /* Catch keyboard interrupts so we can put tty back in a sane state. */
223 signal(SIGTERM, die);
225 signal(SIGWINCH, winch_handler);
227 /* Set up tty for curses use. */
235 time_t t = time(NULL);
236 char *ts = ctime(&t);
237 int tsl = strlen(ts);
243 if (screen_size_changed) {
245 resizeterm(height, width);
247 /* redrawwin(stdscr); */
248 screen_size_changed = 0;
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);
264 if (!(p = popen(command, "r"))) {
269 for (y = show_title; y < height; y++) {
270 int eolseen = 0, tabpending = 0;
271 for (x = 0; x < width; x++) {
276 /* if there is a tab pending, just spit spaces until the
277 next stop instead of reading characters */
281 while (c != EOF && !isprint(c)
285 if (!oldeolseen && x == 0) {
292 if (c == EOF || c == '\n' || c == '\t')
294 if (tabpending && (((x + 1) % 8) == 0))
298 if (option_differences) {
299 chtype oldch = inch();
300 char oldc = oldch & A_CHARTEXT;
304 (option_differences_cumulative
305 && (oldch & A_ATTRIBUTES)));
313 oldeolseen = eolseen;
320 usleep(interval * 1000000);