From 82852ce2fafe3fe3c2b8fbee77b2f10cc0067c2e Mon Sep 17 00:00:00 2001 From: John Date: Sun, 3 Mar 2019 13:28:43 +0100 Subject: add support for multiple files --- watch.1 | 22 +++++++------ watch.c | 108 +++++++++++++++++++++++++++++++++++++++------------------------- 2 files changed, 79 insertions(+), 51 deletions(-) diff --git a/watch.1 b/watch.1 index ec8b051..0f0d89b 100644 --- a/watch.1 +++ b/watch.1 @@ -8,11 +8,11 @@ .Sh SYNPOSIS .Nm watch .Op Fl i -.Ar file +.Ar file ... .\" .Sh DESCRIPTION .Nm watch -is a simple OpenBSD program that watches a given +is a simple OpenBSD program that watches any given .Ar file for changes. When the file changes, its name (as specified in the arguments to @@ -21,25 +21,29 @@ is printed on standard output. If the .Fl i -argument is provided, an initial such line is printed when the -program starts, before beginning to watch for changes. +argument is provided, an initial such line is printed when the program +starts, before beginning to watch for changes. .Nm watch exits on SIGINT (C-c). .\" .Sh ERRORS -If the watched file is deleted, a message is printed on standard +If one of the watched files is deleted, a message is printed on standard error and the program exits with error code 1. -If the file is renamed, a notice is printed on standard error, but -the program keeps running. +If a file is renamed, a notice is printed on standard error, but the +program keeps running. .\" .Sh EXAMPLES .Bd -literal -offset indent -watch document.ms | while read; do - make document.pdf && kill -9 mupdf +$ watch *.ms | while read file; do + make ${file%.ms}.pdf && kill -9 mupdf done .Ed +.Bd -literal -offset indent +$ alias each='xargs -L0 -I {}' +$ watch -i * | each cp {} /mnt/usb/ +.Ed .\" .Sh AUTHORS .Nm watch diff --git a/watch.c b/watch.c index af72e94..8c2ddb8 100644 --- a/watch.c +++ b/watch.c @@ -8,70 +8,94 @@ #include #include #include +#include -#define try(x) if ((x) != -1) {} +#define LIMIT 256 -bool initial = false; /* print one initial line at startup */ +bool initial = false; // print one initial line at startup int main(int argc, char *argv[]) { - size_t file = 1; /* argv index */ + char **filenames; + int first_file_index, kq; + size_t file_count; + struct kevent changes[LIMIT]; + struct kevent events[LIMIT]; + struct rlimit rlp; + + file_count = argc - 1; + first_file_index = 1; // index of first file argument setbuf(stdout, NULL); // disable buffering even non-interactively - if (argc < 2) goto usage; - if (argc == 3) { - if (strncmp(argv[1], "-i", 2) == 0) { - file = 2; + if (argc < 2) goto usage; // quit if no arguments + if (argv[1][0] == '-') { + if (argc < 3) goto usage; // quit if no filenames + + /* adjust indices */ + file_count = argc - 2; + first_file_index = 2; + + if (strcmp(argv[1], "-i") == 0) initial = true; - } else { + else goto usage; - } } - int fd; - try (fd = open(argv[file], O_RDONLY)) else err(1, "%s", argv[file]); + if (file_count > LIMIT) + errx(1, "more than %d files", LIMIT); - if (initial) - printf("%s\n", argv[file]); + if ((kq = kqueue()) == -1) err(1, "kqueue"); - try (pledge("stdio", NULL)) else err(1, "pledge"); + /* save filenames by file descriptor */ + if (getrlimit(RLIMIT_NOFILE, &rlp) == -1) + err(1, "getrlimit"); + if ((filenames = reallocarray(NULL, rlp.rlim_max, sizeof(char **))) == NULL) + err(1, "reallocarray"); - int kq; - try (kq = kqueue()) else err(1, "kqueue"); + /* add each file to change list */ + for (int i = first_file_index; i < argc; i++) { + int fd; + if ((fd = open(argv[i], O_RDONLY)) == -1) + err(1, "%s", argv[i]); - struct kevent change = { // event to watch for - .ident = fd, - .filter = EVFILT_VNODE, - .flags = EV_ADD | EV_CLEAR, - .fflags = NOTE_WRITE | NOTE_RENAME | NOTE_DELETE, - .data = 0, - .udata = NULL - }; + filenames[fd] = argv[i]; - // Register event to watch for - try (kevent(kq, &change, 1, NULL, 0, NULL)) else err(1, "kevent"); + if (initial) + printf("%s\n", argv[i]); + + struct kevent change = { // event to watch for + .ident = fd, + .filter = EVFILT_VNODE, + .flags = EV_ADD | EV_CLEAR, + .fflags = NOTE_WRITE | NOTE_DELETE, + .data = 0, + .udata = NULL + }; + + changes[i - first_file_index] = change; + } - if (change.flags & EV_ERROR) - errx(1, "event error: %s", strerror(change.data)); + if (pledge("stdio", NULL) == -1) err(1, "pledge"); - int n; - struct kevent event; - while (1) { - // Wait for event - try (n = kevent(kq, NULL, 0, &event, 1, NULL)) - else err(1, "kevent wait"); + for (;;) { + int n; + /* register changes and wait for events */ + if ((n = kevent(kq, changes, file_count, events, file_count, NULL)) == -1) + err(1, "kevent wait"); if (n > 0) { - if (event.fflags & NOTE_WRITE) - printf("%s\n", argv[file]); - if (event.fflags & NOTE_RENAME) - warnx("file was renamed\n"); - if (event.fflags & NOTE_DELETE) - errx(1, "file was deleted\n"); + for (int i = 0; i < n; i++) { + if (events[i].flags & EV_ERROR) + errx(1, "event error: %s", strerror(events[i].data)); + if (events[i].fflags & NOTE_WRITE) + printf("%s\n", filenames[events[i].ident]); + if (events[i].fflags & NOTE_DELETE) + errx(1, "%s was deleted", filenames[events[i].ident]); + } } } - // assume that the kernel closes the file descriptors + return 0; // assume that the kernel closes the file descriptors usage: - errx(1, "usage: %s [-i] file\n", argv[0]); + fprintf(stderr, "usage: %s [-i] file\n", argv[0]); } -- cgit v1.2.3