From f23df6009e987747832cf0adc73c0a3d0b476717 Mon Sep 17 00:00:00 2001 From: Leah Neukirchen Date: Fri, 19 May 2017 18:29:19 +0200 Subject: Initial import of rwc --- Makefile | 23 +++++++++++ README | 54 ++++++++++++++++++++++++++ rwc.1 | 74 ++++++++++++++++++++++++++++++++++++ rwc.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 281 insertions(+) create mode 100644 Makefile create mode 100644 README create mode 100644 rwc.1 create mode 100644 rwc.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3a5b75e --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +ALL=rwc + +CFLAGS=-g -O2 -Wall -Wno-switch -Wextra -Wwrite-strings + +DESTDIR= +PREFIX=/usr/local +BINDIR=$(PREFIX)/bin +MANDIR=$(PREFIX)/share/man + +all: $(ALL) + +README: rwc.1 + mandoc -Tutf8 $< | col -bx >$@ + +clean: FRC + rm -f $(ALL) + +install: FRC all + mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1 + install -m0755 $(ALL) $(DESTDIR)$(BINDIR) + install -m0644 $(ALL:=.1) $(DESTDIR)$(MANDIR)/man1 + +FRC: diff --git a/README b/README new file mode 100644 index 0000000..c537bc8 --- /dev/null +++ b/README @@ -0,0 +1,54 @@ +RWC(1) General Commands Manual RWC(1) + +NAME + rwc – report when changed + +SYNOPSIS + rwc [-0d] [path ...] + +DESCRIPTION + rwc takes a list of files or directories, watches them using inotify(7), + and prints each file name when it changed. If path is a single dash + (‘-’) or absent, rwc reads file names from the standard input. + + Watching a directory will result in watching all changes to files which + resides directly in that directory. + + The options are as follows: + + -0 Read input filenames seperated by NUL bytes. Likewise, output + filenames seperated by NUL bytes. + + -d Also detect file deletion. In this case, deleted files are + prefixed by ‘- ’ (that is, a dash and a space). + +EXIT STATUS + The rwc utility exits 0 on success, and >0 if an error occurs. + +SEE ALSO + entr(1), inotifywatch(1), wendy(1) + +CAVEATS + rwc is limited by some restrictions of inotify(7). You can only watch + files and directories you can read, and the amount of inotify descriptors + is limited. Watching directories is not recursive. + + rwc only uses one watch descriptor per directory, and filters file names + itself. This allows tracking files which get safely written by unlink(2) + and rename(2), and also watching files which don't exist yet. + + Many tools like to create temporary files in their working directory, + which may distort the output. + +AUTHORS + Leah Neukirchen + +LICENSE + rwc is in the public domain. + + To the extent possible under law, the creator of this work has waived all + copyright and related or neighboring rights to this work. + + http://creativecommons.org/publicdomain/zero/1.0/ + +Void Linux May 19, 2017 Void Linux diff --git a/rwc.1 b/rwc.1 new file mode 100644 index 0000000..b34a43a --- /dev/null +++ b/rwc.1 @@ -0,0 +1,74 @@ +.Dd May 19, 2017 +.Dt RWC 1 +.Os +.Sh NAME +.Nm rwc +.Nd report when changed +.Sh SYNOPSIS +.Nm +.Op Fl 0d +.Op Ar path\ ... +.Sh DESCRIPTION +.Nm +takes a list of files or directories, watches them using +.Xr inotify 7 , +and prints each file name when it changed. +If +.Ar path +is a single dash +.Pq Sq - +or absent, +.Nm +reads file names from the standard input. +.Pp +Watching a directory will result in watching all changes to files +which resides directly in that directory. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl 0 +Read input filenames seperated by NUL bytes. +Likewise, output filenames seperated by NUL bytes. +.It Fl d +Also detect file deletion. +In this case, deleted files are prefixed by +.Sq Li "- " +(that is, a dash and a space). +.El +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr entr 1 , +.Xr inotifywatch 1 , +.Xr wendy 1 +.Sh CAVEATS +.Nm +is limited by some restrictions of +.Xr inotify 7 . +You can only watch files and directories you can read, +and the amount of inotify descriptors is limited. +Watching directories is not recursive. +.Pp +.Nm +only uses one watch descriptor per directory, +and filters file names itself. +This allows tracking files which get safely written by +.Xr unlink 2 +and +.Xr rename 2 , +and also watching files which don't exist yet. +.Pp +Many tools like to create temporary files in their working directory, +which may distort the output. +.Sh AUTHORS +.An Leah Neukirchen Aq Mt leah@vuxu.org +.Sh LICENSE +.Nm +is in the public domain. +.Pp +To the extent possible under law, +the creator of this work +has waived all copyright and related or +neighboring rights to this work. +.Pp +.Lk http://creativecommons.org/publicdomain/zero/1.0/ diff --git a/rwc.c b/rwc.c new file mode 100644 index 0000000..3b00d9b --- /dev/null +++ b/rwc.c @@ -0,0 +1,130 @@ +// rwc [-0d] [PATH...] - report when changed +// -0 use NUL instead of newline for input/output separator +// -d detect deletions too (prefixed with "- ") + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +char *argv0; +char ibuf[8192]; +int ifd; + +int dflag; +char input_delim = '\n'; + +static void *root = 0; // tree + +int +order(const void *a, const void *b) +{ + return strcmp((char *)a, (char*)b); +} + +void +add(char *file) +{ + struct stat st; + + char *dir = file; + + tsearch(strdup(file), &root, order); + + // assume non-existing files are regular files + if (lstat(file, &st) < 0 || !S_ISDIR(st.st_mode)) + dir = dirname(file); + + if (inotify_add_watch(ifd, dir, IN_MOVED_TO | IN_CLOSE_WRITE | dflag)<0) + fprintf(stderr, "%s: inotify_add_watch: %s: %s\n", + argv0, dir, strerror(errno)); +} + +int +main(int argc, char *argv[]) +{ + int c, i; + + argv0 = argv[0]; + + while ((c = getopt(argc, argv, "0d")) != -1) + switch(c) { + case '0': input_delim = 0; break; + case 'd': dflag = IN_DELETE | IN_DELETE_SELF; break; + default: + fprintf(stderr, "Usage: %s [-0d] [PATH...]\n", argv0); + exit(2); + } + + ifd = inotify_init(); + if (ifd < 0) { + fprintf(stderr, "%s: inotify_init: %s\n", + argv0, strerror(errno)); + exit(111); + } + + i = optind; + if (optind == argc) + goto from_stdin; + for (; i < argc; i++) { + if (strcmp(argv[i], "-") != 0) { + add(argv[i]); + continue; + } +from_stdin: + while (1) { + char *line = 0; + size_t linelen = 0; + ssize_t rd; + + errno = 0; + rd = getdelim(&line, &linelen, input_delim, stdin); + if (rd == -1) { + if (errno != 0) + return -1; + break; + } + + if (rd > 0 && line[rd-1] == input_delim) + line[rd-1] = 0; // strip delimiter + + add(line); + } + } + + while (1) { + ssize_t len, i; + struct inotify_event *ev; + + len = read(ifd, ibuf, sizeof ibuf); + if (len <= 0) { + fprintf(stderr, "%s: error reading inotify buffer: %s", + argv0, strerror(errno)); + exit(1); + } + + for (i = 0; i < len; i += sizeof (*ev) + ev->len) { + ev = (struct inotify_event *) (ibuf + i); + + if (ev->mask & IN_IGNORED) + continue; + + if (tfind(ev->name, &root, order) || + tfind(dirname(ev->name), &root, order)) { + printf("%s%s%c", + (ev->mask & IN_DELETE ? "- " : ""), + ev->name, + input_delim); + fflush(stdout); + } + } + } + + return 0; +} -- cgit 1.4.1