packages feed

hinotify 0.2 → 0.3

raw patch · 19 files changed

+43/−949 lines, 19 filesdep ~basePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base

API changes (from Hackage documentation)

+ System.INotify: killINotify :: INotify -> IO ()
+ System.INotify: withINotify :: (INotify -> IO a) -> IO a

Files

− ANNOUNCE
@@ -1,58 +0,0 @@-Greetings!--I'm pleased to announce hinotify 0.1, a library to inotify[1] which has-been part of the Linux kernel since 2.6.13.--inotify provides file system event notification, simply add a watcher to-a file or directory and get an event when it is accessed or modified.--The API basically consists of:--inotify_init :: IO INotify-inotify_add_watch :: INotify-                  -> [EventVariety]   -- different events to listen on-                  -> FilePath         -- file/directory to watch-                  -> (Event -> IO ()) -- event handler-                  -> IO WatchDescriptor-inotify_rm_watch :: INotify -> WatchDescriptor -> IO ()--A sample program:--> import System.Directory-> import System.IO->-> import System.INotify->-> main :: IO ()-> main = do->     inotify <- inotify_init->     print inotify->     home <- getHomeDirectory->     wd <- inotify_add_watch inotify->                             [Open,Close,Access,Modify,Move]->                             home->                             print->     print wd->     putStrLn "Listens to your home directory. Hit enter to terminate."->     getLine->     inotify_rm_watch inotify wd--The code is available via www:--http://haskell.org/~kolmodin/code/hinotify/download/hinotify-0.1.tar.gz--and via darcs:--  darcs get http://haskell.org/~kolmodin/code/hinotify/--The API is available at:--  http://haskell.org/~kolmodin/code/hinotify/docs/api/--The library is very young and I'm most grateful for feedback on the API,-and what else you might have to suggest.--Cheers,-  Lennart Kolmodin--[1] http://www.kernel.org/pub/linux/kernel/people/rml/inotify/
− Makefile
@@ -1,7 +0,0 @@--.PHONY : docs--docs : README.html--%.html : %-	pandoc -s -S --toc -c hinotify.css $< -o $@
README view
@@ -1,6 +1,6 @@ % hinotify: inotify for Haskell % Lennart Kolmodin <kolmodin@gentoo.org>-% December 26, 2007+% December 26, 2007-2009  About -----@@ -11,7 +11,7 @@ inotify provides file system event notification, simply add a watcher to a file or directory and get an event when it is accessed or modified. -This module is named `inotify`.+This module is named `hinotify`.  See example code in the `examples` directory, distributed with the source code.@@ -20,6 +20,9 @@  News ----++**hinotify 0.3**+:   * Compiles with GHC 6.12, GHC 6.10.4, GHC 6.8.2 and GHC 6.6.1  **hinotify 0.2** :   * Updates to the API
− README.html
@@ -1,179 +0,0 @@-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">-<html xmlns="http://www.w3.org/1999/xhtml"-><head-  ><title-    >hinotify: inotify for Haskell</title-    ><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"-     /><meta name="generator" content="pandoc"-     /><meta name="author" content="Lennart Kolmodin &lt;kolmodin@gentoo.org&gt;"-     /><meta name="date" content="December 26, 2007"-     /><link rel="stylesheet" href="hinotify.css" type="text/css" media="all" />-</head-  ><body-  ><h1 class="title"-    >hinotify: inotify for Haskell</h1-    ><div id="toc"-    ><ul-      ><li-	><a href="#about" id="TOC-about"-	  >About</a-	  ></li-	><li-	><a href="#news" id="TOC-news"-	  >News</a-	  ></li-	><li-	><a href="#api" id="TOC-api"-	  >API</a-	  ></li-	><li-	><a href="#download" id="TOC-download"-	  >Download</a-	  ></li-	><li-	><a href="#author" id="TOC-author"-	  >Author</a-	  ></li-	><li-	><a href="#legal" id="TOC-legal"-	  >Legal</a-	  ></li-	></ul-      ></div-    ><h2 id="about"-    ><a href="#TOC-about"-      >About</a-      ></h2-    ><p-    >hinotify, a library to <a href="http://www.kernel.org/pub/linux/kernel/people/rml/inotify/"-      >inotify</a-      > which has been part of the Linux kernel since 2.6.13.</p-    ><p-    >inotify provides file system event notification, simply add a watcher to a file or directory and get an event when it is accessed or modified.</p-    ><p-    >This module is named <code-      >inotify</code-      >.</p-    ><p-    >See example code in the <code-      >examples</code-      > directory, distributed with the source code.</p-    ><h2 id="news"-    ><a href="#TOC-news"-      >News</a-      ></h2-    ><dl-    ><dt-      ><strong-	>hinotify 0.2</strong-	></dt-      ><dd-      ><ul-	><li-	  >Updates to the API<ul-	    ><li-	      >Function names is now in semiCamelCase</li-	      ><li-	      >Restructure event parameters to make it more consistent</li-	      ></ul-	    ></li-	  ><li-	  >Small test suit in <code-	    >tests/</code-	    ></li-	  ><li-	  >Compiles with GHC 6.8.2 and GHC 6.6.1</li-	  ><li-	  >Requires Cabal 1.2</li-	  ></ul-	></dd-      ><dt-      ><strong-	>hinotify 0.1</strong-	></dt-      ><dd-      >Initial release</dd-      ></dl-    ><h2 id="api"-    ><a href="#TOC-api"-      >API</a-      ></h2-    ><p-    >The API basically consists of:</p-    ><pre-    ><code-      >initINotify :: IO INotify-addWatch :: INotify-         -&gt; [EventVariety]   -- different events to listen on-         -&gt; FilePath         -- file/directory to watch-         -&gt; (Event -&gt; IO ()) -- event handler-         -&gt; IO WatchDescriptor-removeWatch :: INotify -&gt; WatchDescriptor -&gt; IO ()-</code-      ></pre-    ><p-    >A sample program:</p-    ><pre-    ><code-      >import System.Directory-import System.IO--import System.INotify--main :: IO ()-main = do-    inotify &lt;- initINotify-    print inotify-    home &lt;- getHomeDirectory-    wd &lt;- addWatch-            inotify-            [Open,Close,Access,Modify,Move]-            home-            print-    print wd-    putStrLn &quot;Listens to your home directory. Hit enter to terminate.&quot;-    getLine-    removeWatch inotify wd-</code-      ></pre-    ><h2 id="download"-    ><a href="#TOC-download"-      >Download</a-      ></h2-    ><p-    >The code is available via the <a href="http://haskell.org/~kolmodin/code/hinotify/"-      >homepage</a-      >, and via darcs:</p-    ><pre-    ><code-      >darcs get --partial http://haskell.org/~kolmodin/code/hinotify/-</code-      ></pre-    ><p-    >The <a href="http://haskell.org/~kolmodin/code/hinotify/docs/api/"-      >API</a-      > is available online.</p-    ><p-    >I&rsquo;m most grateful for feedback on the API, and what else you might have to suggest.</p-    ><h2 id="author"-    ><a href="#TOC-author"-      >Author</a-      ></h2-    ><p-    >Lennart Kolmodin</p-    ><p-    ><code-      >kolmodin at gentoo.org</code-      ></p-    ><h2 id="legal"-    ><a href="#TOC-legal"-      >Legal</a-      ></h2-    ><p-    >This software is released under a BSD-style license. See LICENSE for more details.</p-    ><p-    >Copyright © 2007 Lennart Kolmodin</p-    ></body-  ></html->-
− examples/DirTree/DirTree.hs
@@ -1,111 +0,0 @@--- Duncan Coutts 2006-2007--- Requires gtk2hs 0.9.11--import qualified Data.Map as Map-import System.Directory-import System.Environment-import Control.Concurrent--import Data.IORef-import Control.Monad (liftM)-import Ix (inRange)--import System.INotify--import Graphics.UI.Gtk hiding (TreeModelFlags(TreeModelListOnly), cellText)-import Graphics.UI.Gtk.ModelView.CellLayout-import Graphics.UI.Gtk.ModelView.Types (TypedTreeModelClass)-import Graphics.UI.Gtk.ModelView.TreeModel (TreeModelFlags(TreeModelListOnly))-import Graphics.UI.Gtk.ModelView.CellRendererText (cellText)-import Graphics.UI.Gtk.ModelView.CustomStore-import Graphics.UI.Gtk.TreeList.TreeIter--instance TypedTreeModelClass (CustomTreeModel a)--dirModelNew :: FilePath -> IO (CustomTreeModel () FilePath)-dirModelNew path = do-  -  dirContents <- getDirectoryContents path-  -  rows <- newIORef (Map.fromList (zip dirContents (repeat ())))--  model <- customTreeModelNew () CustomTreeModelImplementation {-      customTreeModelGetFlags      = return [TreeModelListOnly],-      customTreeModelGetIter       = \[n] -> return (Just (TreeIter 0 (fromIntegral n) 0 0)),-      customTreeModelGetPath       = \(TreeIter _ n _ _) -> return [fromIntegral n],-      customTreeModelGetRow        = \(TreeIter _ n _ _) ->-                                     readIORef rows >>= \rows -> -                                     if inRange (0, Map.size rows - 1) (fromIntegral n)-                                       then return (fst $ Map.elemAt (fromIntegral n) rows)-                                       else fail "DirModel.getRow: iter does not refer to a valid entry",--      customTreeModelIterNext      = \(TreeIter _ n _ _) ->-                                     readIORef rows >>= \rows ->-                                        if n >= fromIntegral (Map.size rows) - 1-                                          then return Nothing-                                          else return (Just (TreeIter 0 (n+1) 0 0)),-      customTreeModelIterChildren  = \_ -> return Nothing,-      customTreeModelIterHasChild  = \_ -> return False,-      customTreeModelIterNChildren = \index -> readIORef rows >>= \rows ->-                                           case index of-                                             Nothing -> return $! Map.size rows-                                             _       -> return 0,-      customTreeModelIterNthChild  = \index n -> case index of-                                               Nothing -> return (Just (TreeIter 0 (fromIntegral n) 0 0))-                                               _       -> return Nothing,-      customTreeModelIterParent    = \_ -> return Nothing,-      customTreeModelRefNode       = \_ -> return (),-      customTreeModelUnrefNode     = \_ -> return ()-    }--  notify <- initINotify-  watch <- addWatch notify [Move, Create, Delete] path $ \event -> -    let add file = do -          index <- atomicModifyIORef rows (\map ->-                     let map' = Map.insert file () map-                      in (map', Map.findIndex file map'))-          treeModelRowInserted model [index] (TreeIter 0 (fromIntegral index) 0 0)-        remove file = do-          index <- atomicModifyIORef rows (\map ->-                     let map' = Map.delete file map-                      in (map', Map.findIndex file map))-          treeModelRowDeleted model [index]--     in case event of-          MovedIn  _ file _ -> add file-          MovedOut _ file _ -> remove file-          Created  _ file   -> add file-          Deleted  _ file   -> remove file-          _ -> putStrLn $ "other event: " ++ show event--  -- TODO: on destroy model (INotify.removeWatch watch)-  -  return model--main = do-  initGUI--  win <- windowNew-  win `onDestroy` mainQuit--  args <- getArgs-  let dir = case args of-              [d] -> d-	      _   -> "."--  model <- dirModelNew dir--  tv <- treeViewNewWithModel model-  win `containerAdd` tv--  tvc <- treeViewColumnNew-  treeViewAppendColumn tv tvc--  text <- cellRendererTextNew-  cellLayoutPackStart tvc text True-  cellLayoutSetAttributes tvc text model-    (\file -> [cellText := file])--  widgetShowAll win-  timeoutAddFull (yield >> return True) priorityDefaultIdle 50-  mainGUI
− examples/simple/simple.hs
@@ -1,17 +0,0 @@-module Main where--import System.Directory-import System.IO--import System.INotify--main :: IO ()-main = do-    inotify <- initINotify-    print inotify-    home <- getHomeDirectory-    wd <- addWatch inotify [Open,Close,Access,Modify,Move] home print-    print wd-    putStrLn "Listens to your home directory. Hit enter to terminate."-    getLine-    removeWatch inotify wd
hinotify.cabal view
@@ -1,5 +1,6 @@ name:               hinotify-version:            0.2+version:            0.3+build-type:         Simple synopsis:           Haskell binding to INotify description:     .@@ -22,7 +23,7 @@ library     build-depends:  unix     if flag(split-base)-        build-depends:  base >= 3, containers, directory+        build-depends:  base >= 3 && < 5, containers, directory     else         build-depends:  base < 3     extensions:     ForeignFunctionInterface@@ -34,5 +35,4 @@      ghc-options:    -fvia-C -Wall -    include-dirs:   include     hs-source-dirs: src
− hinotify.css
@@ -1,89 +0,0 @@-body {-    margin: auto;-    padding-right: 1em;-    padding-left: 1em;-    max-width: 50em; -    border-left: 1px solid black;-    border-right: 1px solid black;-    color: black;-    font-family: Verdana, sans-serif;-    font-size: 100%;-    line-height: 140%;-    color: #333; -}-pre {-    border: 1px dotted gray;-    background-color: #9999ff;-    color: #111111;-    padding: 0.5em;-}-code {-    font-family: monospace;-    font-size: 110%;-}-h1 a, h2 a, h3 a, h4 a, h5 a { -    text-decoration: none;-    color: #000099; -}-h1, h2, h3, h4, h5 { font-family: verdana;-                     font-weight: bold;-                     border-bottom: 1px dotted black;-                     color: #000099; }-h1 {-        font-size: 130%;-}--h2 {-        font-size: 110%;-        border-bottom: 1px dotted black;-}--h3 {-        font-size: 95%;-}--h4 {-        font-size: 90%;-        font-style: italic;-}--h5 {-        font-size: 90%;-        font-style: italic;-}--h1.title {-        font-size: 150%;-        font-weight: bold;-        text-align: left;-        border: none;-}--dt code {-        font-weight: bold;-}-dd p {-        margin-top: 0;-}--a:link {-       color: #000099-       }--a:visited {-	  color: #666699-	  }-a:hover {-	color: #666699-	}-a:active {-	 color: #000099-	 }--#footer {-        padding-top: 1em;-        font-size: 70%;-        color: gray;-        text-align: center;-}-
− include/inotify-syscalls.h
@@ -1,61 +0,0 @@-#ifndef _LINUX_INOTIFY_SYSCALLS_H-#define _LINUX_INOTIFY_SYSCALLS_H--#include <sys/syscall.h>--#if defined(__i386__)-# define __NR_inotify_init	291-# define __NR_inotify_add_watch	292-# define __NR_inotify_rm_watch	293-#elif defined(__x86_64__)-# define __NR_inotify_init	253-# define __NR_inotify_add_watch	254-# define __NR_inotify_rm_watch	255-#elif defined(__powerpc__) || defined(__powerpc64__)-# define __NR_inotify_init	275-# define __NR_inotify_add_watch	276-# define __NR_inotify_rm_watch	277-#elif defined (__ia64__)-# define __NR_inotify_init	1277-# define __NR_inotify_add_watch	1278-# define __NR_inotify_rm_watch	1279-#elif defined (__s390__)-# define __NR_inotify_init	284-# define __NR_inotify_add_watch	285-# define __NR_inotify_rm_watch	286-#elif defined (__alpha__)-# define __NR_inotify_init	444-# define __NR_inotify_add_watch	445-# define __NR_inotify_rm_watch	446-#elif defined (__sparc__) || defined (__sparc64__)-# define __NR_inotify_init	151-# define __NR_inotify_add_watch	152-# define __NR_inotify_rm_watch	156-#elif defined (__arm__)-# define __NR_inotify_init	316-# define __NR_inotify_add_watch	317-# define __NR_inotify_rm_watch	318-#elif defined (__sh__)-# define __NR_inotify_init	290-# define __NR_inotify_add_watch	291-# define __NR_inotify_rm_watch	292-#else-# error "Unsupported architecture!"-#endif--static inline int inotify_init (void)-{-	return syscall (__NR_inotify_init);-}--static inline int inotify_add_watch (int fd, const char *name, __u32 mask)-{-	return syscall (__NR_inotify_add_watch, fd, name, mask);-}--static inline int inotify_rm_watch (int fd, __u32 wd)-{-	return syscall (__NR_inotify_rm_watch, fd, wd);-}--#endif /* _LINUX_INOTIFY_SYSCALLS_H */
− include/inotify.h
@@ -1,113 +0,0 @@-/*- * Inode based directory notification for Linux- *- * Copyright (C) 2005 John McCutchan- */--#ifndef _LINUX_INOTIFY_H-#define _LINUX_INOTIFY_H--#include <linux/types.h>--/*- * struct inotify_event - structure read from the inotify device for each event- *- * When you are watching a directory, you will receive the filename for events- * such as IN_CREATE, IN_DELETE, IN_OPEN, IN_CLOSE, ..., relative to the wd.- */-struct inotify_event {-	__s32		wd;		/* watch descriptor */-	__u32		mask;		/* watch mask */-	__u32		cookie;		/* cookie to synchronize two events */-	__u32		len;		/* length (including nulls) of name */-	char		name[0];	/* stub for possible name */-};--/* the following are legal, implemented events that user-space can watch for */-#define IN_ACCESS		0x00000001	/* File was accessed */-#define IN_MODIFY		0x00000002	/* File was modified */-#define IN_ATTRIB		0x00000004	/* Metadata changed */-#define IN_CLOSE_WRITE		0x00000008	/* Writtable file was closed */-#define IN_CLOSE_NOWRITE	0x00000010	/* Unwrittable file closed */-#define IN_OPEN			0x00000020	/* File was opened */-#define IN_MOVED_FROM		0x00000040	/* File was moved from X */-#define IN_MOVED_TO		0x00000080	/* File was moved to Y */-#define IN_CREATE		0x00000100	/* Subfile was created */-#define IN_DELETE		0x00000200	/* Subfile was deleted */-#define IN_DELETE_SELF		0x00000400	/* Self was deleted */-#define IN_MOVE_SELF		0x00000800	/* Self was moved */--/* the following are legal events.  they are sent as needed to any watch */-#define IN_UNMOUNT		0x00002000	/* Backing fs was unmounted */-#define IN_Q_OVERFLOW		0x00004000	/* Event queued overflowed */-#define IN_IGNORED		0x00008000	/* File was ignored */--/* helper events */-#define IN_CLOSE		(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) /* close */-#define IN_MOVE			(IN_MOVED_FROM | IN_MOVED_TO) /* moves */--/* special flags */-#define IN_ONLYDIR		0x01000000	/* only watch the path if it is a directory */-#define IN_DONT_FOLLOW		0x02000000	/* don't follow a sym link */-#define IN_MASK_ADD		0x20000000	/* add to the mask of an already existing watch */-#define IN_ISDIR		0x40000000	/* event occurred against dir */-#define IN_ONESHOT		0x80000000	/* only send event once */--/*- * All of the events - we build the list by hand so that we can add flags in- * the future and not break backward compatibility.  Apps will get only the- * events that they originally wanted.  Be sure to add new events here!- */-#define IN_ALL_EVENTS	(IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | \-			 IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM | \-			 IN_MOVED_TO | IN_DELETE | IN_CREATE | IN_DELETE_SELF | \-			 IN_MOVE_SELF)--#ifdef __KERNEL__--#include <linux/dcache.h>-#include <linux/fs.h>-#include <linux/config.h>--#ifdef CONFIG_INOTIFY--extern void inotify_inode_queue_event(struct inode *, __u32, __u32,-				      const char *);-extern void inotify_dentry_parent_queue_event(struct dentry *, __u32, __u32,-					      const char *);-extern void inotify_unmount_inodes(struct list_head *);-extern void inotify_inode_is_dead(struct inode *);-extern u32 inotify_get_cookie(void);--#else--static inline void inotify_inode_queue_event(struct inode *inode,-					     __u32 mask, __u32 cookie,-					     const char *filename)-{-}--static inline void inotify_dentry_parent_queue_event(struct dentry *dentry,-						     __u32 mask, __u32 cookie,-						     const char *filename)-{-}--static inline void inotify_unmount_inodes(struct list_head *list)-{-}--static inline void inotify_inode_is_dead(struct inode *inode)-{-}--static inline u32 inotify_get_cookie(void)-{-	return 0;-}--#endif	/* CONFIG_INOTIFY */--#endif	/* __KERNEL __ */--#endif	/* _LINUX_INOTIFY_H */
src/System/INotify.hsc view
@@ -2,7 +2,7 @@ -- | -- Module      :  System.INotify -- Copyright   :  (c) Lennart Kolmodin 2006--- License     :  GPL+-- License     :  BSD3 -- Maintainer  :  kolmodin@dtek.chalmers.se -- Stability   :  experimental -- Portability :  hc portable, linux only@@ -21,6 +21,8 @@  module System.INotify     ( initINotify+    , killINotify+    , withINotify     , addWatch     , removeWatch     , INotify@@ -30,16 +32,16 @@     , Cookie     ) where -#include "inotify.h"+#include "linux/inotify.h"  import Prelude hiding (init) import Control.Monad import Control.Concurrent import Control.Concurrent.MVar+import Control.Exception (bracket) import Data.Maybe import Data.Map (Map) import qualified Data.Map as Map-import GHC.Handle import Foreign.C import Foreign.Marshal import Foreign.Ptr@@ -47,7 +49,13 @@ import System.Directory import System.IO import System.IO.Error+#if __GLASGOW_HASKELL__ >= 612+import GHC.IO.Handle.FD (fdToHandle')+import GHC.IO.Device (IODeviceType(Stream))+#else+import GHC.Handle import System.Posix.Internals+#endif  import System.INotify.Masks @@ -58,7 +66,7 @@ type EventMap = Map WD (Event -> IO ()) type WDEvent = (WD, Event) -data INotify = INotify Handle FD (MVar EventMap)+data INotify = INotify Handle FD (MVar EventMap) ThreadId ThreadId data WatchDescriptor = WatchDescriptor Handle WD deriving Eq  newtype Cookie = Cookie CUInt deriving (Eq,Ord)@@ -151,7 +159,7 @@     deriving Eq  instance Show INotify where-    show (INotify _ fd _) =+    show (INotify _ fd _ _ _) =         showString "<inotify fd=" .          shows fd $ ">" @@ -164,19 +172,18 @@ initINotify :: IO INotify initINotify = do     fd <- throwErrnoIfMinus1 "initINotify" c_inotify_init-    em <- newMVar Map.empty     let desc = showString "<inotify handle, fd=" . shows fd $ ">" #if __GLASGOW_HASKELL__ < 608     h <-  openFd (fromIntegral fd) (Just Stream) False{-is_socket-} desc ReadMode True{-binary-} #else     h <-  fdToHandle' (fromIntegral fd) (Just Stream) False{-is_socket-} desc ReadMode True{-binary-} #endif-    -- h <- fdToHandle fd-    inotify_start_thread h em-    return (INotify h fd em)+    em <- newMVar Map.empty+    (tid1, tid2) <- inotify_start_thread h em+    return (INotify h fd em tid1 tid2)  addWatch :: INotify -> [EventVariety] -> FilePath -> (Event -> IO ()) -> IO WatchDescriptor-addWatch inotify@(INotify h fd em) masks fp cb = do+addWatch inotify@(INotify h fd em _ _) masks fp cb = do     is_dir <- doesDirectoryExist fp     when (not is_dir) $ do         file_exist <- doesFileExist fp@@ -227,20 +234,20 @@             AllEvents -> inAllEvents  removeWatch :: INotify -> WatchDescriptor -> IO ()-removeWatch (INotify _ fd _) (WatchDescriptor _ wd) = do-    throwErrnoIfMinus1 "removeWatch" $+removeWatch (INotify _ fd _ _ _) (WatchDescriptor _ wd) = do+    _ <- throwErrnoIfMinus1 "removeWatch" $       c_inotify_rm_watch (fromIntegral fd) wd     return ()  rm_watch :: INotify -> WD -> IO ()-rm_watch (INotify _ _ em) wd =+rm_watch (INotify _ _ em _ _) wd =     modifyMVar_ em (return . Map.delete wd)  read_events :: Handle -> IO [WDEvent] read_events h =      let maxRead = 16385 in     allocaBytes maxRead $ \buffer -> do-        hWaitForInput h (-1)  -- wait forever+        _ <- hWaitForInput h (-1)  -- wait forever         r <- hGetBufNonBlocking h buffer maxRead         read_events' buffer r     where@@ -283,12 +290,12 @@         isSet bits = maskIsSet bits mask         name = fromJust nameM        -inotify_start_thread :: Handle -> MVar EventMap -> IO ()+inotify_start_thread :: Handle -> MVar EventMap -> IO (ThreadId, ThreadId) inotify_start_thread h em = do     chan_events <- newChan-    forkIO (dispatcher chan_events)-    forkIO (start_thread chan_events)-    return ()+    tid1 <- forkIO (dispatcher chan_events)+    tid2 <- forkIO (start_thread chan_events)+    return (tid1,tid2)     where     start_thread :: Chan [WDEvent] -> IO ()     start_thread chan_events = do@@ -311,6 +318,15 @@         case handlerM of           Nothing -> putStrLn "runHandler: couldn't find handler" -- impossible?           Just handler -> catch (handler event) (\_ -> return ())++killINotify :: INotify -> IO ()+killINotify (INotify h _ _ tid1 tid2) =+    do killThread tid1+       killThread tid2+       hClose h++withINotify :: (INotify -> IO a) -> IO a+withINotify = bracket initINotify killINotify          foreign import ccall unsafe "inotify-syscalls.h inotify_init" c_inotify_init :: IO CInt foreign import ccall unsafe "inotify-syscalls.h inotify_add_watch" c_inotify_add_watch :: CInt -> CString -> CUInt -> IO CInt
src/System/INotify/Masks.hsc view
@@ -31,7 +31,7 @@ import Data.Maybe import Foreign.C.Types -#include "inotify.h"+#include "linux/inotify.h"  data Mask     = UserSpace CUInt@@ -91,35 +91,3 @@ #enum Mask, Special, IN_ONESHOT  #enum Mask, All, IN_ALL_EVENTS--{--/* the following are legal, implemented events that user-space can watch for */-#define IN_ACCESS               0x00000001      /* File was accessed */-#define IN_MODIFY               0x00000002      /* File was modified */-#define IN_ATTRIB               0x00000004      /* Metadata changed */-#define IN_CLOSE_WRITE          0x00000008      /* Writtable file was closed */-#define IN_CLOSE_NOWRITE        0x00000010      /* Unwrittable file closed */-#define IN_OPEN                 0x00000020      /* File was opened */-#define IN_MOVED_FROM           0x00000040      /* File was moved from X */-#define IN_MOVED_TO             0x00000080      /* File was moved to Y */-#define IN_CREATE               0x00000100      /* Subfile was created */-#define IN_DELETE               0x00000200      /* Subfile was deleted */-#define IN_DELETE_SELF          0x00000400      /* Self was deleted */-#define IN_MOVE_SELF            0x00000800      /* Self was moved */--/* the following are legal events.  they are sent as needed to any watch */-#define IN_UNMOUNT              0x00002000      /* Backing fs was unmounted */-#define IN_Q_OVERFLOW           0x00004000      /* Event queued overflowed */-#define IN_IGNORED              0x00008000      /* File was ignored */--/* helper events */-#define IN_CLOSE                (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) /* close */-#define IN_MOVE                 (IN_MOVED_FROM | IN_MOVED_TO) /* moves */--/* special flags */-#define IN_ONLYDIR              0x01000000      /* only watch the path if it is a directory */-#define IN_DONT_FOLLOW          0x02000000      /* don't follow a sym link */-#define IN_MASK_ADD             0x20000000      /* add to the mask of an already existing w atch */-#define IN_ISDIR                0x40000000      /* event occurred against dir */-#define IN_ONESHOT              0x80000000      /* only send event once */--}
− tests/Utils.hs
@@ -1,55 +0,0 @@-module Utils where--import Control.Concurrent.Chan-import Control.Exception--import System.Directory-import System.Environment-import System.Exit--import System.INotify--testName = do-    n <- getProgName-    return (n ++ "-playground")--withTempDir f = do-    path <- testName-    bracket-        ( createDirectory path >> return path )-        ( removeDirectoryRecursive )-        ( f )--withWatch inot events path action f =-    bracket-        ( addWatch inot events path action )-        ( removeWatch inot )-        ( const f )--inTestEnviron events action f = do-    withTempDir $ \testPath -> do-        inot <- initINotify-        chan <- newChan-        withWatch inot events testPath (writeChan chan) $ do-            action testPath-            events <- getChanContents chan-            f events--(~=) :: Eq a => [a] -> [a] -> Bool-[] ~= _ = True-(x:xs) ~= (y:ys) = x == y && xs ~= ys-_ ~= _ = False--asMany :: [a] -> [a] -> [a]-asMany xs ys = take (length xs) ys--explainFailure expected reality = do-    putStrLn "Expected:"-    mapM_ (\x -> putStr "> " >> print x) expected-    putStrLn "But got:"-    mapM_ (\x -> putStr "< " >> print x) (asMany expected reality)-    testFailure--testFailure = exitFailure --testSuccess = exitWith ExitSuccess
− tests/test-all
@@ -1,31 +0,0 @@-#!/bin/bash--GHC=ghc--if [[ -n $1 ]]; then-    GHC="$1"-    echo Using GHC: $GHC-fi--TESTS=( `ls test*.hs | cut -d. -f1` )--rm -rf *.o *.hi--for t in ${TESTS[@]}; do-    rm -rf $t{,.o,.hi}-    rm -rf $t-playground-    $GHC -v0 --make $t.hs -o $t-    echo -n Testing $t ...-    if [[ -x $t ]]; then-        ./$t-        exitCode=$?-        if [[ $exitCode == 0 ]]; then-            echo Success-            rm -rf $t{,.o,.hi}-        else-            echo Failure with exit code $exitCode-        fi-    else-        echo Compilation failed-    fi-done
− tests/test001-list-dir-contents.hs
@@ -1,20 +0,0 @@-module Main where--import Control.Monad--import System.Directory--import System.INotify as INotify--import Utils--main =-    inTestEnviron [Open, Close] getDirectoryContents $ \ events -> do-        when (expected ~= events)-            testSuccess-        explainFailure expected events--expected =-    [ Opened True Nothing-    , Closed True Nothing False-    ]
− tests/test002-writefile.hs
@@ -1,24 +0,0 @@-module Main where--import Control.Monad--import System.INotify as INotify--import Utils--write path = do-    writeFile (path ++ "/hello") ""-    -- actually writing any contents gives me two Modified-    -main =-    inTestEnviron [AllEvents] write $ \ events -> do-        when (expected ~= events)-            testSuccess-        explainFailure expected events--expected =-    [ Created   False "hello"-    , Opened    False (Just "hello")-    , Modified  False (Just "hello")-    , Closed    False (Just "hello") True-    ]
− tests/test003-removefile.hs
@@ -1,35 +0,0 @@-module Main where--import Control.Monad--import System.Directory--import System.INotify as INotify--import Utils--file = "hello"--write path = do-    writeFile (path ++ '/':file) ""--remove path = do-    removeFile (path ++ '/':file)--action path = do-    write path-    remove path-    -main =-    inTestEnviron [AllEvents] action $ \ events -> do-        when (expected ~= events)-            testSuccess-        explainFailure expected events--expected =-    [ Created   False file-    , Opened    False (Just file)-    , Modified  False (Just file)-    , Closed    False (Just file) True-    , Deleted   False file-    ]
− tests/test004-modify-file.hs
@@ -1,47 +0,0 @@-module Main where--import Control.Exception-import Control.Monad--import System.Directory-import System.IO--import System.INotify as INotify--import Utils--file = "hello"--write path = do-    writeFile (path ++ '/':file) ""--modify path = do-    bracket-        (openFile (path ++ '/':file) AppendMode)-        (hClose)-        (\h -> hPutStr h "yarr!")--remove path = do-    removeFile (path ++ '/':file)--action path = do-    write path-    modify path-    remove path-    -main =-    inTestEnviron [AllEvents] action $ \ events -> do-        when (expected ~= events)-            testSuccess-        explainFailure expected events--expected =-    [ Created   False file-    , Opened    False (Just file)-    , Modified  False (Just file)-    , Closed    False (Just file) True-    , Opened    False (Just file)-    , Modified  False (Just file)-    , Closed    False (Just file) True-    , Deleted   False file-    ]
− tests/test005-move-file.hs
@@ -1,46 +0,0 @@-module Main where--import Data.Maybe--import Control.Monad--import System.Directory-import System.IO--import System.INotify as INotify--import Utils--file = "hello"-file2 = file ++ "2"--write path = do-    writeFile (path ++ '/':file) ""--move path = do-    renameFile (path ++ '/':file) (path ++ '/':file2)--remove path = do-    removeFile (path ++ '/':file2)--action path = do-    write path-    move path-    remove path-    -main =-    inTestEnviron [AllEvents] action $ \ events -> do-        let cookie = head [ c | MovedOut _ _ c <- events ]-        when (expected cookie ~= events)-            testSuccess-        explainFailure (expected cookie) events--expected cookie =-    [ Created   False file-    , Opened    False (Just file)-    , Modified  False (Just file)-    , Closed    False (Just file) True-    , MovedOut  False file  cookie-    , MovedIn   False file2 cookie-    , Deleted   False file2-    ]