Created
December 14, 2013 00:00
-
-
Save jparishy/7953819 to your computer and use it in GitHub Desktop.
Watcher
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // main.m | |
| // FSEventsTest | |
| // | |
| // Created by Julius Parishy on 8/20/13. | |
| // Copyright (c) 2013 Fitocracy. All rights reserved. | |
| // | |
| #import <Foundation/Foundation.h> | |
| NSString *LEFSEventDescription(FSEventStreamEventFlags flags, NSString *path) | |
| { | |
| BOOL(^flags_has)(FSEventStreamEventFlags, FSEventStreamEventFlags) = ^BOOL(FSEventStreamEventFlags flags, FSEventStreamEventFlags flag){ | |
| return ((flags & flag) != 0); | |
| }; | |
| NSString *type = @"Unknown Type"; | |
| if(flags_has(flags,kFSEventStreamEventFlagItemIsFile)) | |
| { | |
| type = @"File"; | |
| } | |
| else if(flags_has(flags, kFSEventStreamEventFlagItemIsDir)) | |
| { | |
| type = @"Directory"; | |
| } | |
| else if(flags_has(flags, kFSEventStreamEventFlagItemIsSymlink)) | |
| { | |
| type = @"Symbolic Link"; | |
| } | |
| NSMutableString *action = [@"" mutableCopy]; | |
| NSDictionary *flagsMap = @{ | |
| @(kFSEventStreamEventFlagItemCreated) : @"ItemCreated", | |
| @(kFSEventStreamEventFlagItemRemoved) : @"ItemRemoved", | |
| @(kFSEventStreamEventFlagItemInodeMetaMod) : @"ItemInodeMetaMod", | |
| @(kFSEventStreamEventFlagItemRenamed) : @"ItemRenamed", | |
| @(kFSEventStreamEventFlagItemModified) : @"ItemModified", | |
| @(kFSEventStreamEventFlagItemFinderInfoMod) : @"ItemFinderInfoMod", | |
| @(kFSEventStreamEventFlagItemChangeOwner) : @"ItemChangeOwner", | |
| @(kFSEventStreamEventFlagItemXattrMod) : @"@ItemXattrMod", | |
| @(kFSEventStreamEventFlagItemIsFile) : @"ItemIsFile", | |
| @(kFSEventStreamEventFlagItemIsDir) : @"ItemIsDir", | |
| @(kFSEventStreamEventFlagItemIsSymlink) : @"ItemIsSymlink" | |
| }; | |
| [flagsMap enumerateKeysAndObjectsUsingBlock:^(NSNumber *flag, NSString *value, BOOL *stop) { | |
| if(flags_has(flags, flag.unsignedIntValue)) | |
| { | |
| [action appendFormat:@"%@,", value]; | |
| } | |
| }]; | |
| return [NSString stringWithFormat:@"%@ - %@: %@", type, action, path]; | |
| } | |
| @interface LEDirectoryWatcher : NSObject | |
| @property (nonatomic, assign, readonly) FSEventStreamRef stream; | |
| @property (nonatomic, strong, readonly) NSArray *directories; | |
| @property (nonatomic, assign) NSTimeInterval updateInterval; | |
| - (id)initWithDirectories:(NSArray *)directories; | |
| - (void)begin; | |
| - (void)end; | |
| - (void)handleCallbackWithEventPaths:(NSArray *)paths flags:(NSArray *)eventFlags; | |
| - (void)handlePath:(NSString *)path flags:(FSEventStreamEventFlags)flags; | |
| @end | |
| void LEFSEventCallback(ConstFSEventStreamRef streamRef, void *clientCallBackInfo, size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[]) | |
| { | |
| const char **castedPaths = eventPaths; | |
| NSMutableArray *paths = [NSMutableArray array]; | |
| NSMutableArray *flags = [NSMutableArray array]; | |
| for(NSInteger i = 0; i < numEvents; ++i) | |
| { | |
| const char *pathCString = (const char *)castedPaths[i]; | |
| NSString *path = [NSString stringWithCString:pathCString encoding:NSUTF8StringEncoding]; | |
| [paths addObject:path]; | |
| [flags addObject:@(eventFlags[i])]; | |
| } | |
| LEDirectoryWatcher *watcher = (__bridge LEDirectoryWatcher *)clientCallBackInfo; | |
| [watcher handleCallbackWithEventPaths:[paths copy] flags:[flags copy]]; | |
| } | |
| @implementation LEDirectoryWatcher | |
| - (id)initWithDirectories:(NSArray *)directories | |
| { | |
| if((self = [super init])) | |
| { | |
| _directories = [directories copy]; | |
| self.updateInterval = 1.0f; | |
| } | |
| return self; | |
| } | |
| - (void)begin | |
| { | |
| FSEventStreamEventId latestEventId = kFSEventStreamEventIdSinceNow; | |
| FSEventStreamContext context = { 0 }; | |
| context.info = (__bridge void *)self; | |
| CFArrayRef paths = (__bridge CFArrayRef)self.directories; | |
| _stream = FSEventStreamCreate(kCFAllocatorDefault, &LEFSEventCallback, &context, paths, latestEventId, self.updateInterval, kFSEventStreamCreateFlagFileEvents); | |
| FSEventStreamScheduleWithRunLoop(self.stream, CFRunLoopGetMain(), kCFRunLoopDefaultMode); | |
| FSEventStreamStart(self.stream); | |
| } | |
| - (void)end | |
| { | |
| FSEventStreamStop(self.stream); | |
| FSEventStreamInvalidate(self.stream); | |
| FSEventStreamRelease(self.stream); | |
| } | |
| - (void)handleCallbackWithEventPaths:(NSArray *)paths flags:(NSArray *)eventFlags | |
| { | |
| [paths enumerateObjectsUsingBlock:^(NSString *path, NSUInteger idx, BOOL *stop) { | |
| BOOL included = NO; | |
| for(NSString *directory in self.directories) | |
| { | |
| if([path hasPrefix:directory]) | |
| { | |
| included = YES; | |
| break; | |
| } | |
| } | |
| if(included) | |
| { | |
| FSEventStreamEventFlags flags = [eventFlags[idx] unsignedIntValue]; | |
| [self handlePath:path flags:flags]; | |
| } | |
| }]; | |
| } | |
| - (void)handlePath:(NSString *)path flags:(FSEventStreamEventFlags)flags | |
| { | |
| NSLog(@"%@", LEFSEventDescription(flags, path)); | |
| } | |
| @end | |
| @interface LEFileUpdater : LEDirectoryWatcher | |
| @property (nonatomic, strong) NSDictionary *commandsByFilename; | |
| @end | |
| @implementation LEFileUpdater | |
| - (void)handlePath:(NSString *)path flags:(FSEventStreamEventFlags)flags | |
| { | |
| [self.commandsByFilename enumerateKeysAndObjectsUsingBlock:^(NSString *filename, NSString *command, BOOL *stop) { | |
| if([path isEqualToString:filename]) | |
| { | |
| [self runCommand:command forFilename:filename]; | |
| } | |
| }]; | |
| } | |
| - (void)runCommand:(NSString *)command forFilename:(NSString *)filename | |
| { | |
| NSArray *components = [command componentsSeparatedByString:@" "]; | |
| NSTask *task = [[NSTask alloc] init]; | |
| task.launchPath = components[0]; | |
| task.arguments = [components subarrayWithRange:NSMakeRange(1, components.count - 1)]; | |
| //NSLog(@"%@", task.launchPath); | |
| // NSLog(@"%@", task.arguments); | |
| NSPipe *outputPipe = [[NSPipe alloc] init]; | |
| task.standardOutput = outputPipe; | |
| [task launch]; | |
| //NSLog(@"Performed '%@' for '%@'", components, filename); | |
| } | |
| @end | |
| BOOL running = YES; | |
| void sigint_callback(int signal) | |
| { | |
| running = NO; | |
| } | |
| int main(int argc, const char * argv[]) | |
| { | |
| @autoreleasepool { | |
| signal(SIGINT, sigint_callback); | |
| const char *directoryArg = argv[1]; | |
| NSString *directory = [NSString stringWithCString:directoryArg encoding:NSUTF8StringEncoding]; | |
| LEFileUpdater *watcher = [[LEFileUpdater alloc] initWithDirectories:@[ directory ]]; | |
| NSMutableDictionary *commandsByFilename = [NSMutableDictionary dictionary]; | |
| for(NSInteger i = 2; i < argc; i += 2) | |
| { | |
| const char *filenameArg = argv[i + 0]; | |
| const char *commandArg = argv[i + 1]; | |
| NSString *filename = [NSString stringWithCString:filenameArg encoding:NSUTF8StringEncoding]; | |
| NSString *command = [NSString stringWithCString:commandArg encoding:NSUTF8StringEncoding]; | |
| commandsByFilename[filename] = command; | |
| } | |
| watcher.commandsByFilename = [commandsByFilename copy]; | |
| [watcher begin]; | |
| while(running) | |
| { | |
| [[NSRunLoop currentRunLoop] runUntilDate:[NSDate.date dateByAddingTimeInterval:1.0f]]; | |
| } | |
| [watcher end]; | |
| printf("\n\nQuiting.\n"); | |
| } | |
| return 0; | |
| } | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ➜ Desktop clang fs.m -framework Foundation -framework CoreServices -o watch | |
| ➜ Desktop ./watch "/full/path/to/dir" "/full/path/to/dir/file/to/watch.txt" "/usr/bin/say changed" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment