/* watch -- execute a program repeatedly, displaying output fullscreen * * Based on the original 1991 'watch' by Tony Rems * (with mods and corrections by Francois Pinard). * * Substantially reworked, new features (differences option, SIGWINCH * handling, unlimited command length, long line handling) added Apr 1999 by * Mike Coleman . * * Changes by Albert Cahalan, 2002-2003. */ #define VERSION "0.2.0" #include #include #include #include #include #include #include #include #include #include #include /* #include "proc/procps.h" */ static struct option longopts[] = { {"differences", optional_argument, 0, 'd'}, {"help", no_argument, 0, 'h'}, {"interval", required_argument, 0, 'n'}, {"no-title", no_argument, 0, 't'}, {"version", no_argument, 0, 'v'}, {0, 0, 0, 0} }; static char usage[] = "Usage: %s [-dhntv] [--differences[=cumulative]] [--help] [--interval=] [--no-title] [--version] \n"; static char *progname; static int curses_started = 0; static int height = 24, width = 80; static int screen_size_changed = 0; static int first_screen = 1; static int show_title = 2; // number of lines used, 2 or 0 #define min(x,y) ((x) > (y) ? (y) : (x)) //static void do_usage(void) NORETURN; static void do_usage(void) { fprintf(stderr, usage, progname); exit(1); } //static void do_exit(int status) NORETURN; static void do_exit(int status) { if (curses_started) endwin(); exit(status); } /* signal handler */ //static void die(int notused) NORETURN; static void die(int notused) { (void) notused; do_exit(0); } static void winch_handler(int notused) { (void) notused; screen_size_changed = 1; } static void get_terminal_size(void) { struct winsize w; if (ioctl(2, TIOCGWINSZ, &w) == 0) { if (w.ws_row > 0) height = w.ws_row; if (w.ws_col > 0) width = w.ws_col; } } int main(int argc, char *argv[]) { int optc; int option_differences = 0, option_differences_cumulative = 0, option_help = 0, option_version = 0; int interval = 2; char *command; int command_length = 0; /* not including final \0 */ setlocale(LC_ALL, ""); progname = argv[0]; while ((optc = getopt_long(argc, argv, "+d::hn:vt", longopts, (int *) 0)) != EOF) { switch (optc) { case 'd': option_differences = 1; if (optarg) option_differences_cumulative = 1; break; case 'h': option_help = 1; break; case 't': show_title = 0; break; case 'n': { char *str; interval = strtol(optarg, &str, 10); if (!*optarg || *str) do_usage(); } break; case 'v': option_version = 1; break; default: do_usage(); break; } } if (option_version) { fprintf(stderr, "%s\n", VERSION); if (!option_help) exit(0); } if (option_help) { fprintf(stderr, usage, progname); fputs(" -d, --differences[=cumulative]\thighlight changes between updates\n", stderr); fputs("\t\t(cumulative means highlighting is cumulative)\n", stderr); fputs(" -h, --help\t\t\t\tprint a summary of the options\n", stderr); fputs(" -n, --interval=\t\tseconds to wait between updates\n", stderr); fputs(" -v, --version\t\t\t\tprint the version number\n", stderr); fputs(" -t, --no-title\t\t\tturns off showing the header\n", stderr); exit(0); } if (optind >= argc) do_usage(); command = strdup(argv[optind++]); command_length = strlen(command); for (; optind < argc; optind++) { char *endp; int s = strlen(argv[optind]); command = realloc(command, command_length + s + 2); /* space and \0 */ endp = command + command_length; *endp = ' '; memcpy(endp + 1, argv[optind], s); command_length += 1 + s; /* space then string length */ command[command_length] = '\0'; } get_terminal_size(); /* Catch keyboard interrupts so we can put tty back in a sane state. */ signal(SIGINT, die); signal(SIGTERM, die); signal(SIGHUP, die); signal(SIGWINCH, winch_handler); /* Set up tty for curses use. */ curses_started = 1; initscr(); nonl(); noecho(); cbreak(); for (;;) { time_t t = time(NULL); char *ts = ctime(&t); int tsl = strlen(ts); char *header; FILE *p; int x, y; int oldeolseen = 1; if (screen_size_changed) { get_terminal_size(); resizeterm(height, width); clear(); /* redrawwin(stdscr); */ screen_size_changed = 0; first_screen = 1; } if (show_title) { // left justify interval and command, // right justify time, clipping all to fit window width asprintf(&header, "Every %ds: %.*s", interval, min(width - 1, command_length), command); mvaddstr(0, 0, header); if (strlen(header) > (size_t) (width - tsl - 1)) mvaddstr(0, width - tsl - 4, "... "); mvaddstr(0, width - tsl + 1, ts); free(header); } if (!(p = popen(command, "r"))) { perror("popen"); do_exit(2); } for (y = show_title; y < height; y++) { int eolseen = 0, tabpending = 0; for (x = 0; x < width; x++) { int c = ' '; int attr = 0; if (!eolseen) { /* if there is a tab pending, just spit spaces until the next stop instead of reading characters */ if (!tabpending) do c = getc(p); while (c != EOF && !isprint(c) && c != '\n' && c != '\t'); if (c == '\n') if (!oldeolseen && x == 0) { x = -1; continue; } else eolseen = 1; else if (c == '\t') tabpending = 1; if (c == EOF || c == '\n' || c == '\t') c = ' '; if (tabpending && (((x + 1) % 8) == 0)) tabpending = 0; } move(y, x); if (option_differences) { int oldch = inch(); char oldc = oldch & A_CHARTEXT; attr = !first_screen && (c != oldc || (option_differences_cumulative && (oldch & A_ATTRIBUTES))); } if (attr) standout(); addch(c); if (attr) standend(); } oldeolseen = eolseen; } pclose(p); first_screen = 0; refresh(); sleep(interval); } endwin(); return 0; }