Add chez-inotify: Linux filesystem monitoring + jerboa wrapper

ober

871a26164757042572ccab1d5784be77f83873be

diff --git a/Makefile b/Makefile
index 03d8a6c..9b78543 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,9 @@
 SCHEME = scheme
 LIBDIRS = lib
 # External chez-* library paths for wrapper modules
-CHEZ_EXT_LIBDIRS = $(HOME)/mine/chez-https/src:$(HOME)/mine/chez-ssl/src:$(HOME)/mine/chez-zlib/src:$(HOME)/mine/chez-pcre2:$(HOME)/mine/chez-yaml:$(HOME)/mine/chez-leveldb:$(HOME)/mine/chez-epoll/src
+CHEZ_EXT_LIBDIRS = $(HOME)/mine/chez-https/src:$(HOME)/mine/chez-ssl/src:$(HOME)/mine/chez-zlib/src:$(HOME)/mine/chez-pcre2:$(HOME)/mine/chez-yaml:$(HOME)/mine/chez-leveldb:$(HOME)/mine/chez-epoll/src:$(HOME)/mine/chez-inotify/src
 # Shared object paths for FFI-based chez-* libraries
-CHEZ_EXT_LDPATH = $(HOME)/mine/chez-ssl:$(HOME)/mine/chez-zlib:$(HOME)/mine/chez-pcre2:$(HOME)/mine/chez-leveldb:$(HOME)/mine/chez-epoll
+CHEZ_EXT_LDPATH = $(HOME)/mine/chez-ssl:$(HOME)/mine/chez-zlib:$(HOME)/mine/chez-pcre2:$(HOME)/mine/chez-leveldb:$(HOME)/mine/chez-epoll:$(HOME)/mine/chez-inotify
 
 .PHONY: test test-reader test-core test-runtime test-stdlib test-ffi test-modules test-expanded test-wrappers clean
 
@@ -60,6 +60,9 @@ test-wrappers:
 	@LD_LIBRARY_PATH="$(CHEZ_EXT_LDPATH):$$LD_LIBRARY_PATH" \
 		$(SCHEME) --libdirs "$(LIBDIRS):$(CHEZ_EXT_LIBDIRS)" --script tests/test-wrapper-epoll.ss 2>/dev/null \
 		|| echo "  epoll: SKIP (requires chez_epoll_shim.so)"
+	@LD_LIBRARY_PATH="$(CHEZ_EXT_LDPATH):$$LD_LIBRARY_PATH" \
+		$(SCHEME) --libdirs "$(LIBDIRS):$(CHEZ_EXT_LIBDIRS)" --script tests/test-wrapper-inotify.ss 2>/dev/null \
+		|| echo "  inotify: SKIP (requires chez_inotify_shim.so)"
 
 test-all: test test-wrappers
 
diff --git a/lib/std/os/inotify.sls b/lib/std/os/inotify.sls
new file mode 100644
index 0000000..26cb1bc
--- /dev/null
+++ b/lib/std/os/inotify.sls
@@ -0,0 +1,22 @@
+#!chezscheme
+;;; :std/os/inotify -- Linux filesystem event monitoring (wraps chez-inotify)
+;;; Requires: chez_inotify_shim.so
+
+(library (std os inotify)
+  (export
+    inotify-init inotify-close
+    inotify-add-watch inotify-rm-watch
+    inotify-read-events inotify-poll
+    make-inotify-event inotify-event?
+    inotify-event-wd inotify-event-mask
+    inotify-event-cookie inotify-event-name
+    IN_ACCESS IN_ATTRIB IN_CLOSE_WRITE IN_CLOSE_NOWRITE
+    IN_CREATE IN_DELETE IN_DELETE_SELF IN_MODIFY
+    IN_MOVE_SELF IN_MOVED_FROM IN_MOVED_TO IN_OPEN
+    IN_ALL_EVENTS IN_MOVE IN_CLOSE
+    IN_DONT_FOLLOW IN_EXCL_UNLINK IN_MASK_ADD IN_ONESHOT IN_ONLYDIR
+    IN_IGNORED IN_ISDIR IN_Q_OVERFLOW IN_UNMOUNT)
+
+  (import (chez-inotify))
+
+  ) ;; end library
diff --git a/tests/test-wrapper-inotify.ss b/tests/test-wrapper-inotify.ss
new file mode 100644
index 0000000..62f127c
--- /dev/null
+++ b/tests/test-wrapper-inotify.ss
@@ -0,0 +1,46 @@
+#!chezscheme
+(import (chezscheme) (std os inotify))
+
+(define pass-count 0)
+(define fail-count 0)
+
+(define-syntax chk
+  (syntax-rules (=>)
+    [(_ expr => expected)
+     (let ([result expr] [exp expected])
+       (if (equal? result exp) (set! pass-count (+ pass-count 1))
+         (begin (set! fail-count (+ fail-count 1))
+                (display "FAIL: ") (write 'expr)
+                (display " => ") (write result)
+                (display " expected ") (write exp) (newline))))]))
+
+;; Constants
+(chk (> IN_CREATE 0) => #t)
+(chk (> IN_DELETE 0) => #t)
+
+;; Create/close
+(let ([fd (inotify-init)])
+  (chk (> fd 0) => #t)
+  (inotify-close fd))
+
+;; Watch + create file + read event
+(let ([dir (format "/tmp/jerboa-inotify-~a" (random 1000000))])
+  (mkdir dir)
+  (let ([fd (inotify-init)])
+    (let ([wd (inotify-add-watch fd dir IN_CREATE)])
+      (chk (>= wd 0) => #t)
+      (call-with-output-file (format "~a/t.txt" dir)
+        (lambda (p) (display "x" p)))
+      (when (inotify-poll fd 100)
+        (let ([evts (inotify-read-events fd)])
+          (chk (> (length evts) 0) => #t)
+          (chk (equal? (inotify-event-name (car evts)) "t.txt") => #t)))
+      (inotify-rm-watch fd wd))
+    (inotify-close fd))
+  (delete-file (format "~a/t.txt" dir))
+  (delete-directory dir #f))
+
+(display "  inotify: ") (display pass-count) (display " passed")
+(when (> fail-count 0) (display ", ") (display fail-count) (display " failed"))
+(newline)
+(when (> fail-count 0) (exit 1))