Added two macros; more intro for the README.
authorOlof-Joachim Frahm <olof@macrolet.net>
Sat, 25 Feb 2012 10:46:35 +0000 (11:46 +0100)
committerOlof-Joachim Frahm <olof@macrolet.net>
Sat, 25 Feb 2012 10:46:35 +0000 (11:46 +0100)
Also exported more symbols and update copyright info.

README.md
cl-inotify.asd
inotify.lisp
package.lisp

index 655aaf2..457cfe6 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 CL-INOTIFY - Interface to the Linux inotify(7) API.
 
-Copyright (C) 2011 Olof-Joachim Frahm
+Copyright (C) 2011-12 Olof-Joachim Frahm
 
 Released under a Simplified BSD license.
 
@@ -9,24 +9,76 @@ Working, but unfinished.
 Implementations currently running on: SBCL.
 
 Uses CFFI, binary-types (from [my Github][1] or see [CLiki][2]) and
-trivial-utf-8.  Doesn't use iolib, because we don't need most of the
+trivial-utf-8.  Doesn't use iolib, because I don't need most of the
 functionality, although it might gain us some implementation
-independence (patches which can be conditionally compiled are welcome).
+independence (patches which can be conditionally compiled are most
+welcome; in any case patches are welcome).
 
 A similar package is at [stassats Github][3].
 
+This document helps only with the aspects of this binding, so reading
+the man-page and other information on the inotify-facility may be
+needed.  Reading the next sections and the docstrings of exported
+symbols should get you going, otherwise the source itself may also be of
+some value.
+
+
+# REPLSHOT
+
+Macros make keeping track easier, so the following example is
+straightforward:
+
+    > (with-inotify (inotify T ("." :all-events))
+    >   (loop (format T "~{~A~%~}" (next-events inotify))))
+    > =>
+    > #S(CL-INOTIFY::INOTIFY-EVENT :WD 1 :MASK (CREATE) :COOKIE 0 :NAME .zshist.LOCK)
+    > #S(CL-INOTIFY::INOTIFY-EVENT :WD 1 :MASK (OPEN) :COOKIE 0 :NAME .zshist)
+    > #S(CL-INOTIFY::INOTIFY-EVENT :WD 1 :MASK (MODIFY) :COOKIE 0 :NAME .zshist)
+    > #S(CL-INOTIFY::INOTIFY-EVENT :WD 1 :MASK (CLOSE-WRITE) :COOKIE 0 :NAME .zshist)
+    > #S(CL-INOTIFY::INOTIFY-EVENT :WD 1 :MASK (DELETE) :COOKIE 0 :NAME .zshist.LOCK)
+    > ...
+
+(Tilde-expansion has to happen at another level, else I would've used
+that.)
+
+The first parameter is (per convention) the symbol to which the queue is
+bound, the second is the parameter to `MAKE-INOTIFY`.  The `&REST` list
+consists of parameter lists for the `WATCH`-function, which is called
+for every list before the `&BODY` is executed.  We don't actually need
+to `UNWATCH` every watched path as closing the queue will also take care
+of that.
+
+
+# LOWER-LEVEL USAGE EXAMPLE
+
+You don't have to use macros: all functionality is available in function
+form, although some care should be taken as currently no cleanup handler
+is registered for opened queues, or rather their file handles.
+
+    > (use-package '#:cl-inotify)
+    > (defvar *tmp*)
+    > (setf *tmp* (make-notify))
+    > (watch *tmp* "/var/tmp/" :all-events)
+    > (next-events *tmp*)
+    > (close-inotify *tmp*)
+
 
 # HOWTO
 
+So this section deals in depth with the various bits which make the
+examples above tick.
+
+
 After loading the library use `MAKE-INOTIFY` to create a new event
-queue.  The `NONBLOCKING` argument currently determines if we use the
-standard `CL:LISTEN` function or `SB-UNIX:UNIX-READ` to check for
-available events.
+queue.  The `NONBLOCKING` argument sets the `SB-POSIX:O-NONBLOCK` bit on
+the stream so we don't block while reading.  Nevertheless,
+`EVENT-AVAILABLE-P` works either way (by using `CL:LISTEN`, or a custom
+function which works directly on the file descriptor).
 
 The result of `MAKE-INOTIFY` is used with `WATCH` and `UNWATCH`, the first
 being used to watch a file or directory, the second to stop watching
 it.  The `FLAGS` parameter of `WATCH` is described in the notify(7)
-manpage; you can use a combination of the flags (as keywords) to create
+man-page; you can use a combination of the flags (as keywords) to create
 a suitable bitmask.  The types `INOTIFY-ADD/READ-FLAG`,
 `INOTIFY-READ-FLAG` and `INOTIFY-ADD-FLAG` are also defined and can be
 examined.
@@ -61,19 +113,68 @@ The raw API, which doesn't register watched paths, consists of
 they're exported in case someone doesn't like the upper layers.
 
 
+# EVENT-BASED PROCESSING
+
 In case you want to use `epoll` or `select` on the event queue you can
 access the file descriptor yourself and then use the normal functions
-afterwards.  Currently no such functionality is integrated here.
-
-
-# EXAMPLE
-
-    > (use-package '#:cl-inotify)
-    > (defvar *tmp*)
-    > (setf *tmp* (make-notify))
-    > (watch *tmp* "/var/tmp/" :all-events)
-    > (next-events *tmp*)
-    > (close-inotify *tmp*)
+afterwards.  Currently no such functionality is integrated here, however
+the following sketch shows how something can be accomplished using
+implementation-specific and -internal functionality (since I couldn't
+yet get iolib to build again on my system).  So, given SBCL, we register
+ourselves for event notification on the inotify file descriptor:
+
+    > (with-unregistered-inotify (inotify T ("." :all-events))
+    >   (flet ((inotify-input (handler)
+    >            (declare (ignore handler))
+    >            (format T "~{~A~%~}" (next-events inotify))))
+    >     (sb-sys:with-fd-handler ((inotify-fd inotify) :input #'inotify-input)
+    >       (loop (sb-sys:serve-all-events 1)))))
+
+Note that we perform all inotify business only when something happens in
+that directory, so instead of that loop we could actually do useful
+work, e.g. communicating with a process:  This snippet was extracted
+from a function which uses behaviour to monitor a LaTeX process for
+written files to get the output file name without relying on heuristics
+about the generated filename.
+
+
+# REFERENCE
+
+Here follows a list of valid keywords for the `INOTIFY-FLAG` type:
+
+* `:ACCESS`
+* `:MODIFY`
+* `:ATTRIB`
+* `:CLOSE-WRITE`
+* `:CLOSE-NOWRITE`
+* `:CLOSE`
+* `:OPEN`
+* `:MOVED-FROM`
+* `:MOVED-TO`
+* `:MOVE`
+* `:CREATE`
+* `:DELETE`
+* `:DELETE-SELF`
+* `:MOVE-SELF`
+* `:UNMOUNT`
+* `:Q-OVERFLOW`
+* `:IGNORED`
+* `:ONLYDIR`
+* `:DONT-FOLLOW`
+* `:MASK-ADD`
+* `:ISDIR`
+* `:ONESHOT`
+* `:ALL-EVENTS`
+
+
+The `INOTIFY-EVENT` structure has the slots `WD`, `MASK`, `COOKIE` and
+`NAME` (with default `CONC-NAME`: `INOTIFY-EVENT-`).
+
+The `INOTIFY-INSTANCE` structure has the slots `FD`, `STREAM` and
+`NONBLOCKING` with `CONC-NAME` `INOTIFY-`.
+
+The `REGISTERED-INOTIFY-INSTANCE` includes the previous structure and
+only adds the `WATCHED` slot under the same `CONC-NAME`.
 
 
 # TODO
@@ -83,9 +184,9 @@ afterwards.  Currently no such functionality is integrated here.
 - make things more implementation independent
 - (maybe) don't use the libc for this, direct syscall
 - (maybe) add iolib replacement for io functions
-- easier interface for (e)poll/select maybe using iolib (done, using
-  CL:LISTEN and/or SB-UNIX:UNIX-READ)
-
+- easier interface for (e)poll/select maybe using iolib (done partly
+  using CL:LISTEN and/or SB-UNIX:UNIX-READ)
+- add some cleanup handler to close queue on garbage collection
 
 [1]: https://github.com/Ferada/binary-types
 [2]: http://www.cliki.net/Binary-types
index 79aaa1b..f35ddbf 100644 (file)
@@ -1,6 +1,6 @@
 ;;; -*- mode: lisp; syntax: common-lisp; coding: utf-8; package: cl-user; -*-
 
-;; Copyright (c) 2011, Olof-Joachim Frahm
+;; Copyright (c) 2011-12, Olof-Joachim Frahm
 ;; All rights reserved.
 
 ;; Redistribution and use in source and binary forms, with or without
index 9277300..3ff12d7 100644 (file)
@@ -1,6 +1,6 @@
 ;;; -*- mode: lisp; syntax: common-lisp; coding: utf-8; package: cl-inotify; -*-
 
-;; Copyright (c) 2011, Olof-Joachim Frahm
+;; Copyright (c) 2011-12, Olof-Joachim Frahm
 ;; All rights reserved.
 
 ;; Redistribution and use in source and binary forms, with or without
@@ -209,9 +209,9 @@ the file descriptor is set to non-blocking I/O."
          (sb-posix:close fd)))))
   inotify)
 
-(defun make-unregistered-inotify ()
+(defun make-unregistered-inotify (&optional (nonblocking T))
   "Creates a new unregistered INOTIFY instance."
-  (init-unregistered-inotify (make-inotify-instance)))
+  (init-unregistered-inotify (make-inotify-instance) nonblocking))
 
 (defun close-inotify (inotify)
   "Closes the inotify event queue."
@@ -377,3 +377,35 @@ terminates if no events are available."
   (loop
     while (event-available-p inotify)
     collect (read-event inotify)))
+
+;;; this has the longer name, because this way you actually have to read
+;;; about the differences, at least i hope so
+(defmacro with-unregistered-inotify ((&optional inotify (nonblocking T) &rest rest) &body body)
+  "Like WITH-INOTIFY, but uses MAKE-UNREGISTERED-INOTIFY and WATCH-RAW
+instead.  Useful if you need to monitor just a fixed set of paths."
+  `(let* ((,inotify (make-unregistered-inotify ,nonblocking)))
+     (unwind-protect
+         (progn
+           ,.(mapcar (lambda (specifier)
+                       `(watch-raw ,inotify ,.specifier))
+                     rest)
+           ,.body)
+       (close-inotify ,inotify))))
+
+(defmacro with-inotify ((&optional inotify (nonblocking T) &rest rest) &body body)
+  "Executes BODY with a newly created queue bound to INOTIFY if true.
+See MAKE-INOTIFY for more information about possible arguments.
+
+The REST is a list of argument forms for the WATCH function, i.e. one or
+more forms (PATHNAME FLAGS &KEY (REPLACE-P T)).
+
+Since the QUEUE is closed on unwinding, this macro doesn't bother with
+UNWATCH calls on all WATCHed paths."
+  `(let* ((,inotify (make-inotify ,nonblocking)))
+     (unwind-protect
+         (progn
+           ,.(mapcar (lambda (specifier)
+                       `(watch ,inotify ,.specifier))
+                     rest)
+           ,.body)
+       (close-inotify ,inotify))))
index 1d29263..1951899 100644 (file)
@@ -1,6 +1,6 @@
 ;;; -*- mode: lisp; syntax: common-lisp; coding: utf-8; package: cl-user; -*-
 
-;; Copyright (c) 2011, Olof-Joachim Frahm
+;; Copyright (c) 2011-12, Olof-Joachim Frahm
 ;; All rights reserved.
 
 ;; Redistribution and use in source and binary forms, with or without
           ;;; very raw
           #:read-raw-event-from-stream
 
+          ;;; basic stuff
           #:close-inotify
 
+          ;;; inotify accessors
+          #:inotify-fd
+          #:inotify-stream
+          #:inotify-nonblocking
+
           ;;; event parsing functions
           #:make-unregistered-inotify
           #:read-event-from-stream
           #:watch-raw
           #:unwatch-raw
 
+          ;;; event accessors
+          #:inotify-event-wd
+          #:inotify-event-mask
+          #:inotify-event-cookie
+          #:inotify-event-name
+
           ;;; enhanced functionality
           #:make-inotify
           #:pathname-handle/flags
@@ -59,5 +71,9 @@
           ;;; convenience functions
           #:list-watched
           #:do-events
-          #:next-events)
+          #:next-events
+
+          ;;; macros
+          #:with-inotify
+          #:with-unregistered-inotify)
   (:documentation "A binding (not only?) for the LINUX inotify(7) API."))