@@ -99,6 +99,8 @@ module Streamly.Internal.FileSystem.Event.Linux
99 , setAllEvents
99 , setAllEvents
101 -- ** Watch APIs
101 -- ** Watch APIs
102+ -- XXX watchPaths is redundant now because we can use watchTrees with
103+ -- setRecursiveMode False. Perhaps we can use a common "watch" API.
102 , watchPathsWith
104 , watchPathsWith
103 , watchPaths
105 , watchPaths
104 , watchTreesWith
106 , watchTreesWith
@@ -107,7 +109,7 @@ module Streamly.Internal.FileSystem.Event.Linux
107 , removeFromWatch
109 , removeFromWatch
109 -- * Handling Events
111 -- * Handling Events
110− , Event
112+ , Event(..)
111 , getRoot
113 , getRoot
112 , getRelPath
114 , getRelPath
113 , getCookie
115 , getCookie
@@ -142,19 +144,17 @@ module Streamly.Internal.FileSystem.Event.Linux
143 -- * Debugging
145 -- * Debugging
144 , showEvent
146 , showEvent
149import Control.Monad (void, when)
150import Control.Monad (void, when)
150import Control.Monad.IO.Class (MonadIO, liftIO)
151import Control.Monad.IO.Class (MonadIO, liftIO)
151−import Data.Bifunctor (bimap)
152import Data.Bits ((.|.), (.&.), complement)
152import Data.Bits ((.|.), (.&.), complement)
153+import Data.Char (ord)
153import Data.Foldable (foldlM)
154import Data.Foldable (foldlM)
154import Data.Functor.Identity (runIdentity)
155import Data.Functor.Identity (runIdentity)
155import Data.IntMap.Lazy (IntMap)
156import Data.IntMap.Lazy (IntMap)
156−import Data.IORef (IORef, newIORef, readIORef, writeIORef)
157+import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef)
157−import Data.Function ((&))
158import Data.List.NonEmpty (NonEmpty)
158import Data.List.NonEmpty (NonEmpty)
159import Data.Word (Word8, Word32)
159import Data.Word (Word8, Word32)
160import Foreign.C.Error (throwErrnoIfMinus1)
160import Foreign.C.Error (throwErrnoIfMinus1)
@@ -168,7 +168,6 @@ import GHC.IO.Handle.FD (mkHandleFromFD)
168import Streamly.Prelude (SerialT)
168import Streamly.Prelude (SerialT)
169import Streamly.Internal.Data.Parser (Parser)
169import Streamly.Internal.Data.Parser (Parser)
170import Streamly.Internal.Data.Array.Storable.Foreign.Types (Array(..))
170import Streamly.Internal.Data.Array.Storable.Foreign.Types (Array(..))
171−import System.FilePath ((</>))
172import System.IO (Handle, hClose, IOMode(ReadMode))
171import System.IO (Handle, hClose, IOMode(ReadMode))
173#if !MIN_VERSION_base(4,10,0)
172#if !MIN_VERSION_base(4,10,0)
174import Control.Concurrent.MVar (readMVar)
173import Control.Concurrent.MVar (readMVar)
@@ -201,7 +200,7 @@ import qualified Streamly.Internal.Unicode.Stream as U
202data Config = Config
201data Config = Config
203 { watchRec :: Bool
202 { watchRec :: Bool
204− , createFlags :: Word32
203+ , createFlags :: Word32
207-------------------------------------------------------------------------------
206-------------------------------------------------------------------------------
@@ -229,56 +228,19 @@ setFlag mask status cfg@Config{..} =
229-------------------------------------------------------------------------------
228-------------------------------------------------------------------------------
230-- Utilities
229-- Utilities
231-------------------------------------------------------------------------------
230-------------------------------------------------------------------------------
232−splitPath :: (MonadIO m) => FilePath -> FilePath -> m [FilePath]
233−splitPath pat xs = S.toList
234− $ S.splitOnSeq (A.fromList pat) (FL.toList) (S.fromList xs)
236−pathDiff :: (MonadIO m) => FilePath -> FilePath -> m FilePath
238− splits <- splitPath pat xs
239− let j = case length splits of
242− t = case length j of
247−dirListing :: String -> SerialT IO (Either String String)
250− $ S.iterateMapLeftsWith S.ahead listDir (S.yield $ (Left s))
252− where
232+-- XXX we really do not know the path encoding, all we know is that it is "/"
253−
233+-- separated bytes. So these may fail or convert the path in an unexpected
254− dirFilter x =
234+-- manner. We should ultimately remove all usage of these.
260− Dir.toEither dir -- SerialT IO (Either String String)
261− & S.map (bimap prefix prefix) -- SerialT IO (Either String String)
265− prefix x = dir ++ "/" ++ x
267−toPathList :: String -> IO [String]
269− S.toList $ S.map toStr $ dirListing root
278toUtf8 :: MonadIO m => String -> m (Array Word8)
236toUtf8 :: MonadIO m => String -> m (Array Word8)
279toUtf8 = A.fromStream . U.encodeUtf8 . S.fromList
237toUtf8 = A.fromStream . U.encodeUtf8 . S.fromList
239+utf8ToString :: Array Word8 -> String
240+utf8ToString = runIdentity . S.toList . U.decodeUtf8' . A.toStream
281−-- | Set watch event on directory recursively.
242+-- | Watch the whole directory tree recursively instead of watching just one
243+-- level of directory.
283-- /default: Off/
245-- /default: Off/
@@ -571,14 +533,23 @@ defaultConfig :: Config
571defaultConfig =
533defaultConfig =
572 setWhenExists AddIfExists
534 setWhenExists AddIfExists
573 $ setAllEvents On
535 $ setAllEvents On
574− $ Config {watchRec = False, createFlags = 0}
536+ $ Config {watchRec = True, createFlags = 0}
576-------------------------------------------------------------------------------
538-------------------------------------------------------------------------------
577-- Open an event stream
539-- Open an event stream
578-------------------------------------------------------------------------------
540-------------------------------------------------------------------------------
580-- | A handle for a watch.
542-- | A handle for a watch.
581−data Watch = Watch Handle (IORef (IntMap (Array Word8, Array Word8)))
543+data Watch =
545+ Handle -- File handle for the watch
547+ (IntMap -- Key is the watch descriptor
548+ ( Array Word8 -- Absolute path of the watch root
549+ , Array Word8 -- Path of subdir relative to watch root
583-- Instead of using the watch descriptor we can provide APIs that use the path
554-- Instead of using the watch descriptor we can provide APIs that use the path
584-- itself to identify the watch. That will require us to maintain a map from wd
555-- itself to identify the watch. That will require us to maintain a map from wd
@@ -615,15 +586,13 @@ createWatch = do
616 True -- use non-blocking IO
587 True -- use non-blocking IO
617 Nothing -- TextEncoding (binary)
588 Nothing -- TextEncoding (binary)
618− emptyMapRef <- newIORef Map.empty
589+ emptyMapRef <- newIORef Map.empty
619 return $ Watch h emptyMapRef
590 return $ Watch h emptyMapRef
621foreign import ccall unsafe
592foreign import ccall unsafe
622 "sys/inotify.h inotify_add_watch" c_inotify_add_watch
593 "sys/inotify.h inotify_add_watch" c_inotify_add_watch
623 :: CInt -> CString -> CUInt -> IO CInt
594 :: CInt -> CString -> CUInt -> IO CInt
625−utf8ToString :: Array Word8 -> String
626−utf8ToString = runIdentity . S.toList . U.decodeUtf8' . A.toStream
628#if !MIN_VERSION_base(4,10,0)
597#if !MIN_VERSION_base(4,10,0)
629-- | Turn an existing Handle into a file descriptor. This function throws an
598-- | Turn an existing Handle into a file descriptor. This function throws an
@@ -643,72 +612,105 @@ handleToFd h = case h of
643 InappropriateType "handleToFd" msg Nothing Nothing
612 InappropriateType "handleToFd" msg Nothing Nothing
646−-- | Given a 'Config' and a @path@ supplied as a UTF-8 encoded byte array, add
615+-- | Add a trailing "/" at the end of the path if there is none. Do not add a
647−-- the path to the list of paths being monitored by the given watch 'Handle'.
616+-- "/" if the path is empty.
648−-- Returns a "watch descriptor" which is used as an idenitfier in the events
617+--
649−-- generated for this particular path and also to remove the path from this
618+ensureTrailingSlash :: Array Word8 -> Array Word8
650−-- watch.
619+ensureTrailingSlash path =
620+ if A.length path /= 0
622+ let mx = A.readIndex path (A.length path - 1)
624+ Nothing -> error "ensureTrailingSlash: Bug: Invalid index"
626+ if x /= fromIntegral (ord '/')
627+ then path <> A.fromCString# "/"#
631+-- | @addToWatch cfg watch root subpath@ adds @subpath@ to the list of paths
632+-- being monitored under @root@ via the watch handle @watch@. @root@ must be
633+-- an absolute path and @subpath@ must be relative to @root@.
652-- /Internal/
635-- /Internal/
654−addToWatchExecutor ::
637+addToWatch :: Config -> Watch -> Array Word8 -> Array Word8 -> IO ()
659− -> IO (Config, Watch)
660−addToWatchExecutor cfg@Config{..} (Watch handle wdMap) pathArr root = do
638+addToWatch cfg@Config{..} watch@(Watch handle wdMap) root0 path0 = do
639+ -- XXX do not add if the path is already added
640+ -- XXX if the watch is added by the scan and not via an event we can
641+ -- generate a create event assuming that the create may have been lost. We
642+ -- can also mark in the map that this entry was added by the scan. So if an
643+ -- actual create event later comes and tries to add this again then we can
644+ -- ignore that and drop the create event to avoid duplicate create, because
645+ -- we have already emitted it.
647+ -- When a directory is added by the scan we should also emit create events
648+ -- for files that may have got added to the dir. However, such create
649+ -- events may get duplicated because of a race between the scan generated
650+ -- versus real events.
652+ -- Or we may distinguish between scan generated events and real events so
653+ -- that the application can assume that other events may been lost and
654+ -- handle it. For example, if it is a dir create the application can read
655+ -- the dir to scan the files in it.
657+ let root = ensureTrailingSlash root0
658+ path = ensureTrailingSlash path0
659+ absPath = root <> path
661 fd <- handleToFd handle
660 fd <- handleToFd handle
662− wd <- A.asCString pathArr $ \pathPtr ->
663− throwErrnoIfMinus1 ("addToWatch " ++ utf8ToString pathArr) $
664− c_inotify_add_watch (fdFD fd) pathPtr (CUInt createFlags)
665− km <- readIORef wdMap
666− let k = (Map.insert (fromIntegral wd) (root, pathArr) km)
668− return $ (cfg, Watch handle wdMap)
670−addToWatch :: Config -> Watch -> Array Word8 -> IO (Config, Watch)
662+ -- XXX we need to tolerate an error where we are adding a watch for a
671−addToWatch cfg@Config{..} (Watch handle wdMap) path = do
663+ -- non-existing file because the file may have got deleted by the time we
672− fd <- handleToFd handle
664+ -- added the watch. Perhaps we can have a flag in config for this and keep
665+ -- the default value to tolerate the error.
667+ -- XXX The file may have even got deleted and then recreated which we will
668+ -- never get to know, document this.
673− wd <- A.asCString path $ \pathPtr ->
669+ wd <- A.asCString absPath $ \pathPtr ->
674− throwErrnoIfMinus1 ("addToWatch " ++ utf8ToString path) $
670+ throwErrnoIfMinus1 ("addToWatch: " ++ utf8ToString absPath) $
675 c_inotify_add_watch (fdFD fd) pathPtr (CUInt createFlags)
671 c_inotify_add_watch (fdFD fd) pathPtr (CUInt createFlags)
676− km <- readIORef wdMap
672+
673+ -- We add the parent first so that we start getting events for any new
674+ -- creates and add the new subdirectories on creates while we are adding
677− let k = (Map.insert (fromIntegral wd) (path, path) km)
676+ modifyIORef wdMap (Map.insert (fromIntegral wd) (root, path))
678− writeIORef wdMap k
677+
679− return $ (cfg, Watch handle wdMap)
678+ -- Now add the children. If we missed any creates while we were adding the
680−
679+ -- parent, this will make sure they are added too.
681−addToWatchRec :: Config -> Watch -> FilePath -> FilePath -> IO (Config, Watch)
680+ --
682−addToWatchRec cfg watch path origRoot= do
681+ -- XXX Ensure that we generate events that we may have missed while we were
683− subdirs <- toPathList path
682+ -- adding the dirs.
684+ -- XXX toDirs currently uses paths as String, we need to convert it
685+ -- to "/" separated by byte arrays.
687+ S.mapM_ (\p -> addToWatch cfg watch root (path <> p))
684− pathArrList <- mapM toUtf8 subdirs
688+ $ S.mapM toUtf8
685− rootArr <- toUtf8 origRoot
689+ $ Dir.toDirs $ utf8ToString absPath
686− foldlM (\(cfg1, w1) pathArr -> addToWatchExecutor cfg1 w1 pathArr rootArr)
687− (cfg, watch) pathArrList
689foreign import ccall unsafe
691foreign import ccall unsafe
690 "sys/inotify.h inotify_rm_watch" c_inotify_rm_watch
692 "sys/inotify.h inotify_rm_watch" c_inotify_rm_watch
691 :: CInt -> CInt -> IO CInt
693 :: CInt -> CInt -> IO CInt
693−-- | Remove a path from a 'Watch', if a path was moved after adding you need to
695+-- | Remove an absolute root path from a 'Watch', if a path was moved after
694−-- need to provide the original path which was used to add the Watch.
696+-- adding you need to provide the original path which was used to add the
696-- /Internal/
699-- /Internal/
698−removeFromWatch :: Watch -> Array Word8 -> IO Watch
701+removeFromWatch :: Watch -> Array Word8 -> IO ()
699removeFromWatch (Watch handle wdMap) path = do
702removeFromWatch (Watch handle wdMap) path = do
700 fd <- handleToFd handle
703 fd <- handleToFd handle
701 km <- readIORef wdMap
704 km <- readIORef wdMap
702 wdMap1 <- foldlM (step fd) Map.empty (Map.toList km)
705 wdMap1 <- foldlM (step fd) Map.empty (Map.toList km)
703 writeIORef wdMap wdMap1
706 writeIORef wdMap wdMap1
704− return $ Watch handle wdMap
708 step fd newMap (wd, v) = do
710 step fd newMap (wd, v) = do
709 if (fst v) == path
711 if (fst v) == path
711− let err = "removeFromWatch " ++ show (utf8ToString path)
713+ let err = "removeFromWatch: " ++ show (utf8ToString path)
712 rm = c_inotify_rm_watch (fdFD fd) (fromIntegral wd)
714 rm = c_inotify_rm_watch (fdFD fd) (fromIntegral wd)
713 void $ throwErrnoIfMinus1 err rm
715 void $ throwErrnoIfMinus1 err rm
714 return newMap
716 return newMap
@@ -720,32 +722,18 @@ removeFromWatch (Watch handle wdMap) path = do
721-- /Internal/
723-- /Internal/
725− -> NonEmpty (Array Word8)
725+openWatch :: Config -> NonEmpty (Array Word8) -> IO Watch
726− -> IO (Config, Watch)
727openWatch cfg paths = do
726openWatch cfg paths = do
728− let pathList = NonEmpty.toList paths
730− foldlM (\(cfg1, w1) path -> addToWatch cfg1 w1 path)
735− -> NonEmpty (Array Word8)
736− -> IO (Config, Watch)
737−openWatchRec cfg paths = do
738− let pathList = map utf8ToString $ NonEmpty.toList paths
739 w <- createWatch
727 w <- createWatch
740− foldlM (\(cfg1, w1) path -> addToWatchRec cfg1 w1 path path)
728+ mapM_ (\p -> addToWatch cfg w p (A.fromList [])) $ NonEmpty.toList paths
741− (cfg, w) pathList
729+ return w
743-- | Close a 'Watch' handle.
731-- | Close a 'Watch' handle.
745-- /Internal/
733-- /Internal/
747−closeWatch :: (Config, Watch) -> IO ()
735+closeWatch :: Watch -> IO ()
748−closeWatch (_, Watch h _) = hClose h
736+closeWatch (Watch h _) = hClose h
750-------------------------------------------------------------------------------
738-------------------------------------------------------------------------------
751-- Raw events read from the watch file handle
739-- Raw events read from the watch file handle
@@ -788,8 +776,6 @@ readOneEvent cfg wt@(Watch _ wdMap) = do
788 -- XXX need the "initial" in parsers to return a step type so that "take 0"
776 -- XXX need the "initial" in parsers to return a step type so that "take 0"
789 -- can return without an input. otherwise if pathLen is 0 we will keep
777 -- can return without an input. otherwise if pathLen is 0 we will keep
790 -- waiting to read one more char before we return this event.
778 -- waiting to read one more char before we return this event.
791− -- PR.yieldM $ print ("Flag1 = " ++ show eflags)
792− -- PR.yieldM $ print ("pathLen = " ++ show pathLen)
794 if pathLen /= 0
780 if pathLen /= 0
@@ -798,30 +784,33 @@ readOneEvent cfg wt@(Watch _ wdMap) = do
799 pth <- PR.sliceSepByMax (== 0) pathLen (A.writeN pathLen)
785 pth <- PR.sliceSepByMax (== 0) pathLen (A.writeN pathLen)
800 let remaining = pathLen - A.length pth - 1
786 let remaining = pathLen - A.length pth - 1
801− -- PR.yieldM $ print ("remaining = " ++ show remaining)
802 when (remaining /= 0) $ PR.takeEQ remaining FL.drain
787 when (remaining /= 0) $ PR.takeEQ remaining FL.drain
803− -- PR.yieldM $ print ("takeEQ = " ++ show remaining)
804 return pth
788 return pth
805 else return $ A.fromList []
789 else return $ A.fromList []
806− -- PR.yieldM $ print ("Flag2 = " ++ show eflags)
807− xm <- PR.yieldM $ readIORef wdMap
790+ wdm <- PR.yieldM $ readIORef wdMap
808− let base = case Map.lookup (fromIntegral ewd) xm of
792+ case Map.lookup (fromIntegral ewd) wdm of
809− Just path1 -> path1
793+ Just pair -> pair
810− Nothing -> (path, path)
794+ Nothing ->
811− let xpath = utf8ToString (snd base) </> utf8ToString path
795+ error $ "readOneEvent: "
812− _ <- if eflags .&. iN_CREATE /= 0 && eflags .&. iN_ISDIR /= 0
796+ <> "Unknown watch descriptor: "
813− then
797+ <> show ewd
814− processEvent cfg wt xpath (utf8ToString $ fst base)
798+ let -- "sub" is guaranteed to have a trailing "/"
815− else return (cfg, wt)
799+ sub1 = sub <> path
816− rm2 <- PR.yieldM $ readIORef wdMap
800+ -- Check for "ISDIR" first because it is less likely
817− pdiff <- PR.yieldM $ pathDiff (utf8ToString $ fst base) xpath
801+ isDirCreate = eflags .&. iN_ISDIR /= 0 && eflags .&. iN_CREATE /= 0
818− relPath <- PR.yieldM $ toUtf8 (pdiff)
802+ when (watchRec cfg && isDirCreate)
803+ $ PR.yieldM $ addToWatch cfg wt root sub1
804+ -- XXX Handle IN_DELETE, IN_DELETE_SELF, IN_MOVE_SELF, IN_MOVED_FROM,
806+ -- What if a large dir tree gets moved in to our hierarchy? Do we get a
807+ -- single event for the top level dir in this case?
819 return $ Event
808 return $ Event
820 { eventWd = (fromIntegral ewd)
809 { eventWd = (fromIntegral ewd)
821 , eventFlags = eflags
810 , eventFlags = eflags
822 , eventCookie = cookie
811 , eventCookie = cookie
823− , eventRelPath = relPath
812+ , eventRelPath = sub1
824− , eventMap = rm2
813+ , eventMap = wdm
@@ -834,21 +823,8 @@ readOneEvent cfg wt@(Watch _ wdMap) = do
834 pathLen :: Word32 <- peekByteOff ptr (len + 8)
823 pathLen :: Word32 <- peekByteOff ptr (len + 8)
835 return (ewd, eflags, cookie, fromIntegral pathLen)
824 return (ewd, eflags, cookie, fromIntegral pathLen)
842− -> Parser IO Word8 (Config, Watch)
843− processEvent cfg1@Config{..} wt1 xpath root = do
846− PR.yieldM $ addToWatchRec cfg1 wt1 xpath root
848− PR.yieldM $ return (cfg1, wt1)
850−watchToStream :: (Config, Watch) -> SerialT IO Event
826+watchToStream :: Config -> Watch -> SerialT IO Event
851−watchToStream (cfg, wt@(Watch handle _)) = do
827+watchToStream cfg wt@(Watch handle _) = do
852 -- Do not use too small a buffer. As per inotify man page:
828 -- Do not use too small a buffer. As per inotify man page:
854 -- The behavior when the buffer given to read(2) is too small to return
830 -- The behavior when the buffer given to read(2) is too small to return
@@ -879,12 +855,7 @@ watchToStream (cfg, wt@(Watch handle _)) = do
880watchPathsWith ::
856watchPathsWith ::
881 (Config -> Config) -> NonEmpty (Array Word8) -> SerialT IO Event
857 (Config -> Config) -> NonEmpty (Array Word8) -> SerialT IO Event
882−watchPathsWith f paths = S.bracket before after watchToStream
858+watchPathsWith f = watchTreesWith (f . setRecursiveMode False)
886− before = liftIO $ openWatch (f defaultConfig) paths
887− after = liftIO . closeWatch
889-- | Like 'watchPathsWith' but uses the 'defaultConfig' options.
860-- | Like 'watchPathsWith' but uses the 'defaultConfig' options.
@@ -897,22 +868,40 @@ watchPathsWith f paths = S.bracket before after watchToStream
897watchPaths :: NonEmpty (Array Word8) -> SerialT IO Event
868watchPaths :: NonEmpty (Array Word8) -> SerialT IO Event
898watchPaths = watchPathsWith id
869watchPaths = watchPathsWith id
871+-- XXX We should not go across the mount points of network file systems or file
872+-- systems that are known to not generate any events.
900-- | Start monitoring a list of file system paths for file system events with
874-- | Start monitoring a list of file system paths for file system events with
901-- the supplied configuration operation over the 'defaultConfig'. The
875-- the supplied configuration operation over the 'defaultConfig'. The
902-- paths could be files or directories. When the path is a directory, the
876-- paths could be files or directories. When the path is a directory, the
903-- whole directory tree under it is watched recursively. Monitoring starts from
877-- whole directory tree under it is watched recursively. Monitoring starts from
904-- the current time onwards.
878-- the current time onwards.
880+-- Note that recrusive watch on a large directory tree could be expensive. When
881+-- starting a watch, the whole tree must be read and watches are started on
882+-- each directory in the tree. The initial time to start the watch as well as
883+-- the memory required is proportional to the number of directories in the
886+-- When new directories are created under the tree they are added to the watch
887+-- on receiving the directory create event. However, the creation of a dir and
888+-- adding a watch for it is not atomic. The implementation takes care of this
889+-- and makes sure that watches are added for all directories. However, In the
890+-- mean time, the directory may have received more events which may get lost.
891+-- Handling of any such lost events is yet to be implemented.
893+-- See the Linux __inotify__ man page for more details.
906-- /Internal/
895-- /Internal/
908watchTreesWith ::
897watchTreesWith ::
909 (Config -> Config) -> NonEmpty (Array Word8) -> SerialT IO Event
898 (Config -> Config) -> NonEmpty (Array Word8) -> SerialT IO Event
910−watchTreesWith f paths = S.bracket before after watchToStream
899+watchTreesWith f paths = S.bracket before after (watchToStream cfg)
903+ cfg = f defaultConfig
914− before = liftIO
904+ before = liftIO $ openWatch cfg paths
915− $ openWatchRec (f $ setRecursiveMode True defaultConfig) paths
916 after = liftIO . closeWatch
905 after = liftIO . closeWatch
918-- | Like 'watchTreesWith' but uses the 'defaultConfig' options.
907-- | Like 'watchTreesWith' but uses the 'defaultConfig' options.
@@ -928,12 +917,13 @@ watchTrees = watchTreesWith id
928-- Examine event stream
917-- Examine event stream
929-------------------------------------------------------------------------------
918-------------------------------------------------------------------------------
931−-- | Get the watch descriptor corresponding to the 'Event'. A watch descriptor
920+-- | Get the watch root corresponding to the 'Event'.
932−-- identifies which of the paths being monitored has generated this event.
934-- Note that if a path was moved after adding to the watch, this will give the
922-- Note that if a path was moved after adding to the watch, this will give the
935-- original path and not the new path after moving.
923-- original path and not the new path after moving.
925+-- TBD: we can possibly update the watch root on a move self event.
937-- /Internal/
927-- /Internal/
939getRoot :: Event -> Array Word8
929getRoot :: Event -> Array Word8
@@ -1188,12 +1178,6 @@ isDir = getFlag iN_ISDIR
1188-------------------------------------------------------------------------------
1178-------------------------------------------------------------------------------
1189-- Debugging
1179-- Debugging
1190-------------------------------------------------------------------------------
1180-------------------------------------------------------------------------------
1191−showEventShort :: Event -> String
1192−showEventShort ev@Event{..} = (utf8ToString $ getRelPath ev)
1193− ++ "_" ++ show eventFlags
1194− ++ showev isDir "Dir"
1196− where showev f str = if f ev then "_" ++ str else ""
1198-- | Convert an 'Event' record to a String representation.
1182-- | Convert an 'Event' record to a String representation.
1199showEvent :: Event -> String
1183showEvent :: Event -> String
@@ -1232,4 +1216,4 @@ showEvent ev@Event{..} =
1233#warning "Disabling module Streamly.Internal.FileSystem.Event.Linux. Does not support kernels older than 2.6.36."
1217#warning "Disabling module Streamly.Internal.FileSystem.Event.Linux. Does not support kernels older than 2.6.36."
1234module Streamly.Internal.FileSystem.Event.Linux () where
1218module Streamly.Internal.FileSystem.Event.Linux () where