Retain/release MYDirectoryWatcher's _standardizedPath, for non-GC compatibility.
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] copy];
42 _lastEventID = kFSEventStreamEventIdSinceNow;
51 [_standardizedPath release];
62 @synthesize path=_path, latency=_latency, lastEventID=_lastEventID;
64 - (NSString*) standardizedPath {
65 return _standardizedPath;
72 FSEventStreamContext context = {0,self,NULL,NULL,NULL};
73 _stream = FSEventStreamCreate(NULL,
74 &directoryWatcherCallback, &context,
75 (CFArrayRef)[NSArray arrayWithObject: _path],
78 kFSEventStreamCreateFlagUseCFTypes);
81 FSEventStreamScheduleWithRunLoop(_stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
82 if( ! FSEventStreamStart(_stream) ) {
86 _historyDone = (_lastEventID == kFSEventStreamEventIdSinceNow);
87 LogTo(MYDirectoryWatcher, @"Started on %@ (latency=%g, lastEvent=%llu)",_path,_latency,_lastEventID);
95 FSEventStreamStop(_stream);
96 FSEventStreamInvalidate(_stream);
97 FSEventStreamRelease(_stream);
99 LogTo(MYDirectoryWatcher, @"Stopped on %@ (lastEvent=%llu)",_path,_lastEventID);
106 _lastEventID = kFSEventStreamEventIdSinceNow; // so events from now till next start will be dropped
107 [NSObject cancelPreviousPerformRequestsWithTarget: self selector: @selector(start) object: nil];
110 - (void) stopTemporarily
114 [self performSelector: @selector(start) withObject: nil afterDelay: 0.0];
119 - (void) _notifyEvents: (size_t)numEvents
120 paths: (NSArray*)paths
121 flags: (const FSEventStreamEventFlags[])eventFlags
122 eventIDs: (const FSEventStreamEventId[])eventIDs
124 for (size_t i=0; i<numEvents; i++) {
125 NSString *path = [paths objectAtIndex: i];
126 FSEventStreamEventFlags flags = eventFlags[i];
127 FSEventStreamEventId eventID = eventIDs[i];
128 if( flags & (kFSEventStreamEventFlagMount | kFSEventStreamEventFlagUnmount) ) {
129 if( flags & kFSEventStreamEventFlagMount )
130 LogTo(MYDirectoryWatcher, @"Volume mounted: %@",path);
132 LogTo(MYDirectoryWatcher, @"Volume unmounted: %@",path);
133 } else if( flags & kFSEventStreamEventFlagHistoryDone ) {
134 LogTo(MYDirectoryWatcher, @"Event #%llu History done",eventID);
137 LogTo(MYDirectoryWatcher, @"Event #%llu flags=%02x path=%@",eventID,flags,path);
139 flags |= kFSEventStreamEventFlagHistoryDone;
141 MYDirectoryEvent *event = [[MYDirectoryEvent alloc] _initWithWatcher: self
145 [_target performSelector: _action withObject: event];
148 _lastEventID = eventIDs[i];
153 static void directoryWatcherCallback(ConstFSEventStreamRef streamRef,
157 const FSEventStreamEventFlags eventFlags[],
158 const FSEventStreamEventId eventIDs[])
160 [(MYDirectoryWatcher*)watcher _notifyEvents: numEvents
161 paths: (NSArray*)eventPaths
173 @implementation MYDirectoryEvent
175 - (id) _initWithWatcher: (MYDirectoryWatcher*)itsWatcher
176 path: (NSString*)itsPath
177 flags: (FSEventStreamEventFlags)itsFlags
178 eventID: (FSEventStreamEventId)itsEventID
182 watcher = itsWatcher;
185 eventID = itsEventID;
196 @synthesize watcher,path,flags,eventID;
198 - (NSString*) relativePath
200 NSString *base = watcher.standardizedPath;
201 NSString *standardizedPath = [path stringByStandardizingPath];
202 if( ! [standardizedPath hasPrefix: base] )
204 unsigned length = base.length;
205 while( length < standardizedPath.length && [standardizedPath characterAtIndex: length]=='/' )
207 return [standardizedPath substringFromIndex: length];
210 - (BOOL) mustScanSubdirectories {return (flags & kFSEventStreamEventFlagMustScanSubDirs) != 0;}
211 - (BOOL) eventsWereDropped {return (flags & (kFSEventStreamEventFlagUserDropped|kFSEventStreamEventFlagKernelDropped)) != 0;}
212 - (BOOL) isHistorical {return (flags & kFSEventStreamEventFlagHistoryDone)==0;}
213 - (BOOL) rootChanged {return (flags & kFSEventStreamEventFlagRootChanged)!=0;}