aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn <john@ankarstrom.se>2019-03-03 13:28:43 +0100
committerJohn <john@ankarstrom.se>2019-03-03 13:28:43 +0100
commit82852ce2fafe3fe3c2b8fbee77b2f10cc0067c2e (patch)
tree45cb0ae373f91242e491f39b02b650e3a797f774
parent15fc4542704e9e3bb3f31f7eae911b483db42a53 (diff)
downloadwhen-82852ce2fafe3fe3c2b8fbee77b2f10cc0067c2e.tar.gz
add support for multiple files
-rw-r--r--watch.122
-rw-r--r--watch.c108
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 <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
+#include <sys/resource.h>
-#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]);
}