diff --git a/build/jam/HaikuImage b/build/jam/HaikuImage index 81b0df90f8..4e5b64072c 100644 --- a/build/jam/HaikuImage +++ b/build/jam/HaikuImage @@ -60,7 +60,7 @@ SYSTEM_BIN = "[" addattr alert arp base64 basename bash bc beep bootman bzip2 tr traceroute translate trash true truncate tsort tty uname unchop unexpand unmount uniq unlink unrar unshar unzip unzipsfx updatedb uptime urlwrapper useradd uudecode uuencode - vdir version vim vmstat waitfor wc wget whoami xargs xres yes + vdir version vim vmstat waitfor watch wc wget whoami xargs xres yes zdiff zforce zgrep zip zipcloak zipgrep zipnote zipsplit zmore znew ; diff --git a/headers/posix/sys/ioctl.h b/headers/posix/sys/ioctl.h index dd53321b6c..f8e5e87daa 100644 --- a/headers/posix/sys/ioctl.h +++ b/headers/posix/sys/ioctl.h @@ -5,6 +5,8 @@ #ifndef _SYS_IOCTL_H #define _SYS_IOCTL_H +#include + /* These only work on sockets for now */ #define FIONBIO 0xbe000000 #define FIONREAD 0xbe000001 diff --git a/src/bin/Jamfile b/src/bin/Jamfile index b4282ab83a..98977deef9 100644 --- a/src/bin/Jamfile +++ b/src/bin/Jamfile @@ -5,6 +5,7 @@ SetSubDirSupportedPlatformsBeOSCompatible ; UsePrivateHeaders app shared storage support usb ; UsePrivateSystemHeaders ; SubDirHdrs $(HAIKU_TOP) src add-ons kernel file_cache ; +UseLibraryHeaders ncurses ; local haiku-utils_rsrc = [ FGristFiles haiku-utils.rsrc ] ; @@ -170,6 +171,11 @@ StdBinCommands filepanel.cpp : be tracker : $(haiku-utils_rsrc) ; +# standard commands that need libncurses.a +StdBinCommands + watch.c + : libncurses.a : $(haiku-utils_rsrc) ; + SubInclude HAIKU_TOP src bin addattr ; SubInclude HAIKU_TOP src bin bash ; SubInclude HAIKU_TOP src bin bc ; diff --git a/src/bin/watch.c b/src/bin/watch.c new file mode 100644 index 0000000000..cac3f7ec7f --- /dev/null +++ b/src/bin/watch.c @@ -0,0 +1,277 @@ +/* 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; +}