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 <locale.h> 26 /* #include "proc/procps.h" */ 27 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} 35 }; 36 37 static char usage[] = 38 "Usage: %s [-dhntv] [--differences[=cumulative]] [--help] [--interval=<n>] [--no-title] [--version] <command>\n"; 39 40 static char *progname; 41 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 47 48 #define min(x,y) ((x) > (y) ? (y) : (x)) 49 50 //static void do_usage(void) NORETURN; 51 static void do_usage(void) 52 { 53 fprintf(stderr, usage, progname); 54 exit(1); 55 } 56 57 //static void do_exit(int status) NORETURN; 58 static void do_exit(int status) 59 { 60 if (curses_started) 61 endwin(); 62 exit(status); 63 } 64 65 /* signal handler */ 66 //static void die(int notused) NORETURN; 67 static void die(int notused) 68 { 69 (void) notused; 70 do_exit(0); 71 } 72 73 static void 74 winch_handler(int notused) 75 { 76 (void) notused; 77 screen_size_changed = 1; 78 } 79 80 static void 81 get_terminal_size(void) 82 { 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; 89 } 90 } 91 92 int 93 main(int argc, char *argv[]) 94 { 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 */ 102 103 setlocale(LC_ALL, ""); 104 progname = argv[0]; 105 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': 121 { 122 char *str; 123 interval = strtol(optarg, &str, 10); 124 if (!*optarg || *str) 125 do_usage(); 126 } 127 break; 128 case 'v': 129 option_version = 1; 130 break; 131 default: 132 do_usage(); 133 break; 134 } 135 } 136 137 if (option_version) { 138 fprintf(stderr, "%s\n", VERSION); 139 if (!option_help) 140 exit(0); 141 } 142 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); 152 } 153 154 if (optind >= argc) 155 do_usage(); 156 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'; 168 } 169 170 get_terminal_size(); 171 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); 177 178 /* Set up tty for curses use. */ 179 curses_started = 1; 180 initscr(); 181 nonl(); 182 noecho(); 183 cbreak(); 184 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; 193 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; 201 } 202 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); 213 } 214 215 if (!(p = popen(command, "r"))) { 216 perror("popen"); 217 do_exit(2); 218 } 219 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; 225 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) 230 do 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; 247 } 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 254 || 255 (option_differences_cumulative 256 && (oldch & A_ATTRIBUTES))); 257 } 258 if (attr) 259 standout(); 260 addch(c); 261 if (attr) 262 standend(); 263 } 264 oldeolseen = eolseen; 265 } 266 267 pclose(p); 268 269 first_screen = 0; 270 refresh(); 271 sleep(interval); 272 } 273 274 endwin(); 275 276 return 0; 277 } 278