* Optimized Olivier's MYDirectoryWatcher fix (by caching the watcher's standardized path)
* Added -[NSData my_UTF8ToString] to CollectionUtils.
2 // MYDirectoryWatcher.m
5 // Copyright 2008 Jens Alfke. All rights reserved.
8 #import "MYDirectoryWatcher.h"
11 #import <CoreServices/CoreServices.h>
14 static void directoryWatcherCallback(ConstFSEventStreamRef streamRef,
15 void *clientCallBackInfo,
18 const FSEventStreamEventFlags eventFlags[],
19 const FSEventStreamEventId eventIds[]);
21 @interface MYDirectoryEvent ()
22 - (id) _initWithWatcher: (MYDirectoryWatcher*)itsWatcher
23 path: (NSString*)itsPath
24 flags: (FSEventStreamEventFlags)itsFlags
25 eventID: (FSEventStreamEventId)itsEventID;
29 @implementation MYDirectoryWatcher
32 - (id) initWithDirectory: (NSString*)path target: (id)target action: (SEL)action
38 _standardizedPath = [_path stringByStandardizingPath];
42 _lastEventID = kFSEventStreamEventIdSinceNow;
61 @synthesize path=_path, latency=_latency, lastEventID=_lastEventID;
63 - (NSString*) standardizedPath {
64 return _standardizedPath;
71 FSEventStreamContext context = {0,self,NULL,NULL,NULL};
72 _stream = FSEventStreamCreate(NULL,
73 &directoryWatcherCallback, &context,
74 (CFArrayRef)[NSArray arrayWithObject: _path],
77 kFSEventStreamCreateFlagUseCFTypes);
80 FSEventStreamScheduleWithRunLoop(_stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
81 if( ! FSEventStreamStart(_stream) ) {
85 _historyDone = (_lastEventID == kFSEventStreamEventIdSinceNow);
86 LogTo(MYDirectoryWatcher, @"Started on %@ (latency=%g, lastEvent=%llu)",_path,_latency,_lastEventID);
94 FSEventStreamStop(_stream);
95 FSEventStreamInvalidate(_stream);
96 FSEventStreamRelease(_stream);
98 LogTo(MYDirectoryWatcher, @"Stopped on %@ (lastEvent=%llu)",_path,_lastEventID);
105 _lastEventID = kFSEventStreamEventIdSinceNow; // so events from now till next start will be dropped
106 [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(start) object: nil];
109 - (void) stopTemporarily
113 [self performSelector: @selector(start) withObject: nil afterDelay: 0.0];
118 - (void) _notifyEvents: (size_t)numEvents
119 paths: (NSArray*)paths
120 flags: (const FSEventStreamEventFlags[])eventFlags
121 eventIDs: (const FSEventStreamEventId[])eventIDs
123 for (size_t i=0; i<numEvents; i++) {
124 NSString *path = [paths objectAtIndex: i];
125 FSEventStreamEventFlags flags = eventFlags[i];
126 FSEventStreamEventId eventID = eventIDs[i];
127 if( flags & (kFSEventStreamEventFlagMount | kFSEventStreamEventFlagUnmount) ) {
128 if( flags & kFSEventStreamEventFlagMount )
129 LogTo(MYDirectoryWatcher, @"Volume mounted: %@",path);
131 LogTo(MYDirectoryWatcher, @"Volume unmounted: %@",path);
132 } else if( flags & kFSEventStreamEventFlagHistoryDone ) {
133 LogTo(MYDirectoryWatcher, @"Event #%llu History done",eventID);
136 LogTo(MYDirectoryWatcher, @"Event #%llu flags=%02x path=%@",eventID,flags,path);
138 flags |= kFSEventStreamEventFlagHistoryDone;
140 MYDirectoryEvent *event = [[MYDirectoryEvent alloc] _initWithWatcher: self
144 [_target performSelector: _action withObject: event];
147 _lastEventID = eventIDs[i];
152 static void directoryWatcherCallback(ConstFSEventStreamRef streamRef,
156 const FSEventStreamEventFlags eventFlags[],
157 const FSEventStreamEventId eventIDs[])
159 [(MYDirectoryWatcher*)watcher _notifyEvents: numEvents
160 paths: (NSArray*)eventPaths
172 @implementation MYDirectoryEvent
174 - (id) _initWithWatcher: (MYDirectoryWatcher*)itsWatcher
175 path: (NSString*)itsPath
176 flags: (FSEventStreamEventFlags)itsFlags
177 eventID: (FSEventStreamEventId)itsEventID
181 watcher = itsWatcher;
184 eventID = itsEventID;
195 @synthesize watcher,path,flags,eventID;
197 - (NSString*) relativePath
199 NSString *base = watcher.standardizedPath;
200 NSString *standardizedPath = [path stringByStandardizingPath];
201 if( ! [standardizedPath hasPrefix: base] )
203 unsigned length = base.length;
204 while( length < standardizedPath.length && [standardizedPath characterAtIndex: length]=='/' )
206 return [standardizedPath substringFromIndex: length];
209 - (BOOL) mustScanSubdirectories {return (flags & kFSEventStreamEventFlagMustScanSubDirs) != 0;}
210 - (BOOL) eventsWereDropped {return (flags & (kFSEventStreamEventFlagUserDropped|kFSEventStreamEventFlagKernelDropped)) != 0;}
211 - (BOOL) isHistorical {return (flags & kFSEventStreamEventFlagHistoryDone)==0;}
212 - (BOOL) rootChanged {return (flags & kFSEventStreamEventFlagRootChanged)!=0;}