+(defun unwatch-raw (notify handle)
+ "Disables watching the path associated with HANDLE."
+ (let ((result (c-inotify-rm-watch (inotify-fd notify) handle)))
+ (when (minusp result)
+ (error "inotify_rm_watch failed: ~A" result)))
+ (values))
+
+;;;; support functions, making life easier
+
+(defstruct (registered-inotify-instance
+ (:include inotify-instance)
+ (:constructor make-registered-inotify-instance ())
+ (:conc-name inotify-))
+ "Additionally to the information in INOTIFY-INSTANCE, records watched
+paths in a dictionary."
+ watched)
+
+(defun make-notify (&optional (nonblocking T))
+ (let ((result (make-registered-inotify-instance)))
+ (init-unregistered-notify result nonblocking)
+ (with-slots (watched) result
+ (setf watched (make-hash-table :test 'equal)))
+ result))
+
+(defun watchedp (notify pathname)
+ "Returns the tuple (HANDLE . FLAGS) if PATHNAME is being watched by NOTIFY,
+else NIL."
+ (awhen (gethash pathname (inotify-watched notify))
+ (values (car it) (cdr it))))
+
+;; TODO: handle additional flags, save only list of flags
+(defun watch (notify pathname flags)
+ (let ((handle (watch-raw notify pathname flags)))
+ (with-slots (watched) notify
+ (setf (gethash pathname watched) (cons handle flags)))
+ handle))
+
+(defun unwatch (notify &key pathname handle)
+ (unless (or pathname handle)
+ (error "either PATHNAME or HANDLE has to be specified"))
+ (if handle
+ (unwatch-raw notify handle)
+ (let ((handle (watchedp notify pathname)))
+ (unless handle
+ (error "PATHNAME ~S isn't being watched" pathname))
+ (unwatch-raw notify handle)
+ (remhash pathname (inotify-watched notify))))
+ (values))
+
+(defun list-watched (notify)
+ "Returns a list of all watched pathnames in particular order."
+ (let (result)
+ (maphash (lambda (k v)
+ (declare (ignore v))
+ (push k result))
+ (inotify-watched notify))
+ result))
+
+(defun unix-eagainp (fd-stream)
+ "Returns T on a FD-STREAM, if trying to read raised a EAGAIN error."
+ (multiple-value-bind (result error)
+ (sb-unix:unix-read (sb-sys:fd-stream-fd fd-stream) NIL 0)
+ (declare (ignore result))
+ (= error sb-unix:eagain)))
+
+(defun event-availablep (notify)
+ "Returns T if an event is available on the queue."
+ (if (inotify-nonblocking notify)
+ (not (unix-eagainp (inotify-stream notify)))
+ (listen (inotify-stream notify))))
+
+(defun read-event (notify)
+ "Reads an event from the queue. Blocks if no event is available."
+ (read-event-from-stream (inotify-stream notify)))
+
+(defun next-event (notify)
+ "Reads an event from the queue. Returns NIL if none is available."
+ (when (event-availablep notify)
+ (read-event notify)))
+
+(defmacro! do-events ((var o!notify) &body body)
+ "Loops BODY with VAR bound to the events retrieved from NOTIFY. The macro
+uses NEXT-EVENT, so that reading an event won't block."
+ `(loop as ,var = (next-event ,g!notify)
+ while ,var
+ do (progn ,.body)))
+
+(defun next-events (notify)
+ "Reads all available events from the queue. Returns a list of events."
+ (let (result)
+ (do-events (event notify)
+ (push event result))
+ (nreverse result)))