fs-4.1/0000755000232200023220000000000013226474060012340 5ustar debalancedebalancefs-4.1/README.md0000644000232200023220000000335313226474060013623 0ustar debalancedebalanceFS Listener =========== Backends -------- * Mac [fsevent](https://github.com/thibaudgg/rb-fsevent) * Linux [inotify](https://github.com/rvoicilas/inotify-tools/wiki) * Windows [inotify-win](https://github.com/thekid/inotify-win) NOTE: On Linux you need to install inotify-tools. ### Subscribe to Notifications ```erlang > fs:start_link(fs_watcher, "/Users/5HT/synrc/fs"). % need to start the fs watcher > fs:subscribe(fs_watcher). % the pid will receive events as messages > flush(). Shell got {<0.47.0>, {fs,file_event}, {"/Users/5HT/synrc/fs/src/README.md",[closed,modified]}} ``` ### List Events from Backend ```erlang > fs:known_events(fs_watcher). % returns events known by your backend [mustscansubdirs,userdropped,kerneldropped,eventidswrapped, historydone,rootchanged,mount,unmount,created,removed, inodemetamod,renamed,modified,finderinfomod,changeowner, xattrmod,isfile,isdir,issymlink,ownevent] ``` ### Sample Subscriber ```erlang > fs:start_looper(). % starts a sample process that logs events =INFO REPORT==== 28-Aug-2013::19:36:26 === file_event: "/tank/proger/erlfsmon/src/4913" [closed,modified] ``` ### API compatibility API is per default compatible to version before 1.10. By application start, `fs` will start fs watcher on specified per enviroment `path` or if enviroment is unsetted, than in `CWD`. That means you can still use it, like: ```erlang fs:subscribe() ``` If you do not want to use backwards_compatible mode, disable it by setting `fs` enviroment: ``` {backwards_compatible, false} ``` This option will lead, that default fs watcher willn't be started. ### Hex.pm publication ``` MIX_EXS=package.exs mix hex.publish package ``` Credits ------- * Vladimir Kirillov * Maxim Sokhatsky OM A HUM fs-4.1/c_src/0000755000232200023220000000000013226474060013431 5ustar debalancedebalancefs-4.1/c_src/mac/0000755000232200023220000000000013226474060014171 5ustar debalancedebalancefs-4.1/c_src/mac/compat.h0000644000232200023220000000362313226474060015631 0ustar debalancedebalance/** * @headerfile compat.h * FSEventStream flag compatibility shim * * In order to compile a binary against an older SDK yet still support the * features present in later OS releases, we need to define any missing enum * constants not present in the older SDK. This allows us to safely defer * feature detection to runtime (and avoid recompilation). */ #ifndef fsevent_watch_compat_h #define fsevent_watch_compat_h #ifndef __CORESERVICES__ #include #endif // __CORESERVICES__ #if MAC_OS_X_VERSION_MAX_ALLOWED < 1060 // ignoring events originating from the current process introduced in 10.6 extern FSEventStreamCreateFlags kFSEventStreamCreateFlagIgnoreSelf; #endif #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070 // file-level events introduced in 10.7 extern FSEventStreamCreateFlags kFSEventStreamCreateFlagFileEvents; extern FSEventStreamEventFlags kFSEventStreamEventFlagItemCreated, kFSEventStreamEventFlagItemRemoved, kFSEventStreamEventFlagItemInodeMetaMod, kFSEventStreamEventFlagItemRenamed, kFSEventStreamEventFlagItemModified, kFSEventStreamEventFlagItemFinderInfoMod, kFSEventStreamEventFlagItemChangeOwner, kFSEventStreamEventFlagItemXattrMod, kFSEventStreamEventFlagItemIsFile, kFSEventStreamEventFlagItemIsDir, kFSEventStreamEventFlagItemIsSymlink; #endif #if MAC_OS_X_VERSION_MAX_ALLOWED < 1090 // marking, rather than ignoring, events originating from the current process introduced in 10.9 extern FSEventStreamCreateFlags kFSEventStreamCreateFlagMarkSelf; extern FSEventStreamEventFlags kFSEventStreamEventFlagOwnEvent; #endif #endif // fsevent_watch_compat_h fs-4.1/c_src/mac/compat.c0000644000232200023220000000256213226474060015625 0ustar debalancedebalance#include "compat.h" #if MAC_OS_X_VERSION_MAX_ALLOWED < 1060 FSEventStreamCreateFlags kFSEventStreamCreateFlagIgnoreSelf = 0x00000008; #endif #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070 FSEventStreamCreateFlags kFSEventStreamCreateFlagFileEvents = 0x00000010; FSEventStreamEventFlags kFSEventStreamEventFlagItemCreated = 0x00000100; FSEventStreamEventFlags kFSEventStreamEventFlagItemRemoved = 0x00000200; FSEventStreamEventFlags kFSEventStreamEventFlagItemInodeMetaMod = 0x00000400; FSEventStreamEventFlags kFSEventStreamEventFlagItemRenamed = 0x00000800; FSEventStreamEventFlags kFSEventStreamEventFlagItemModified = 0x00001000; FSEventStreamEventFlags kFSEventStreamEventFlagItemFinderInfoMod = 0x00002000; FSEventStreamEventFlags kFSEventStreamEventFlagItemChangeOwner = 0x00004000; FSEventStreamEventFlags kFSEventStreamEventFlagItemXattrMod = 0x00008000; FSEventStreamEventFlags kFSEventStreamEventFlagItemIsFile = 0x00010000; FSEventStreamEventFlags kFSEventStreamEventFlagItemIsDir = 0x00020000; FSEventStreamEventFlags kFSEventStreamEventFlagItemIsSymlink = 0x00040000; #endif #if MAC_OS_X_VERSION_MAX_ALLOWED < 1090 FSEventStreamCreateFlags kFSEventStreamCreateFlagMarkSelf = 0x00000020; FSEventStreamEventFlags kFSEventStreamEventFlagOwnEvent = 0x00080000; #endif fs-4.1/c_src/mac/cli.c0000644000232200023220000001171613226474060015112 0ustar debalancedebalance#include #include "cli.h" const char* cli_info_purpose = "A flexible command-line interface for the FSEvents API"; const char* cli_info_usage = "Usage: fsevent_watch [OPTIONS]... [PATHS]..."; const char* cli_info_help[] = { " -h, --help you're looking at it", " -V, --version print version number and exit", " -p, --show-plist display the embedded Info.plist values", " -s, --since-when=EventID fire historical events since ID", " -l, --latency=seconds latency period (default='0.5')", " -n, --no-defer enable no-defer latency modifier", " -r, --watch-root watch for when the root path has changed", // " -i, --ignore-self ignore current process", " -F, --file-events provide file level event data", " -f, --format=name output format (ignored)", 0 }; static void default_args (struct cli_info* args_info) { args_info->since_when_arg = kFSEventStreamEventIdSinceNow; args_info->latency_arg = 0.5; args_info->no_defer_flag = false; args_info->watch_root_flag = false; args_info->ignore_self_flag = false; args_info->file_events_flag = false; args_info->mark_self_flag = false; args_info->format_arg = 0; } static void cli_parser_release (struct cli_info* args_info) { unsigned int i; for (i=0; i < args_info->inputs_num; ++i) { free(args_info->inputs[i]); } if (args_info->inputs_num) { free(args_info->inputs); } args_info->inputs_num = 0; } void cli_parser_init (struct cli_info* args_info) { default_args(args_info); args_info->inputs = 0; args_info->inputs_num = 0; } void cli_parser_free (struct cli_info* args_info) { cli_parser_release(args_info); } static void cli_print_info_dict (const void *key, const void *value, void *context) { CFStringRef entry = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@:\n %@"), key, value); if (entry) { CFShow(entry); CFRelease(entry); } } void cli_show_plist (void) { CFBundleRef mainBundle = CFBundleGetMainBundle(); CFRetain(mainBundle); CFDictionaryRef mainBundleDict = CFBundleGetInfoDictionary(mainBundle); if (mainBundleDict) { CFRetain(mainBundleDict); printf("Embedded Info.plist metadata:\n\n"); CFDictionaryApplyFunction(mainBundleDict, cli_print_info_dict, NULL); CFRelease(mainBundleDict); } CFRelease(mainBundle); printf("\n"); } void cli_print_version (void) { printf("%s %s\n\n", "VXZ", "1.0"); } void cli_print_help (void) { cli_print_version(); printf("\n%s\n", cli_info_purpose); printf("\n%s\n", cli_info_usage); printf("\n"); int i = 0; while (cli_info_help[i]) { printf("%s\n", cli_info_help[i++]); } } int cli_parser (int argc, const char** argv, struct cli_info* args_info) { static struct option longopts[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, { "show-plist", no_argument, NULL, 'p' }, { "since-when", required_argument, NULL, 's' }, { "latency", required_argument, NULL, 'l' }, { "no-defer", no_argument, NULL, 'n' }, { "watch-root", no_argument, NULL, 'r' }, { "ignore-self", no_argument, NULL, 'i' }, { "file-events", no_argument, NULL, 'F' }, { "mark-self", no_argument, NULL, 'm' }, { "format", required_argument, NULL, 'f' }, { 0, 0, 0, 0 } }; const char* shortopts = "hVps:l:nriFf:"; int c = -1; while ((c = getopt_long(argc, (char * const*)argv, shortopts, longopts, NULL)) != -1) { switch(c) { case 's': // since-when args_info->since_when_arg = strtoull(optarg, NULL, 0); break; case 'l': // latency args_info->latency_arg = strtod(optarg, NULL); break; case 'n': // no-defer args_info->no_defer_flag = true; break; case 'r': // watch-root args_info->watch_root_flag = true; break; case 'i': // ignore-self args_info->ignore_self_flag = true; break; case 'F': // file-events args_info->file_events_flag = true; break; case 'm': // mark-self args_info->mark_self_flag = true; break; case 'f': // format // XXX: ignored break; case 'V': // version cli_print_version(); exit(EXIT_SUCCESS); case 'p': // show-plist cli_show_plist(); exit(EXIT_SUCCESS); case 'h': // help case '?': // invalid option case ':': // missing argument cli_print_help(); exit((c == 'h') ? EXIT_SUCCESS : EXIT_FAILURE); } } if (optind < argc) { int i = 0; args_info->inputs_num = (unsigned int)(argc - optind); args_info->inputs = (char**)(malloc ((args_info->inputs_num)*sizeof(char*))); while (optind < argc) if (argv[optind++] != argv[0]) { args_info->inputs[i++] = strdup(argv[optind-1]); } } return EXIT_SUCCESS; } fs-4.1/c_src/mac/main.c0000644000232200023220000001702513226474060015266 0ustar debalancedebalance#include "common.h" #include "cli.h" // TODO: set on fire. cli.{h,c} handle both parsing and defaults, so there's // no need to set those here. also, in order to scope metadata by path, // each stream will need its own configuration... so this won't work as // a global any more. In the end the goal is to make the output format // able to declare not just that something happened and what flags were // attached, but what path it was watching that caused those events (so // that the path itself can be used for routing that information to the // relevant callback). // // Structure for storing metadata parsed from the commandline static struct { FSEventStreamEventId sinceWhen; CFTimeInterval latency; FSEventStreamCreateFlags flags; CFMutableArrayRef paths; int format; } config = { (UInt64) kFSEventStreamEventIdSinceNow, (double) 0.3, (CFOptionFlags) kFSEventStreamCreateFlagNone, NULL, 0 }; // Prototypes static void append_path(const char* path); static inline void parse_cli_settings(int argc, const char* argv[]); static void callback(FSEventStreamRef streamRef, void* clientCallBackInfo, size_t numEvents, void* eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[]); static void append_path(const char* path) { CFStringRef pathRef = CFStringCreateWithCString(kCFAllocatorDefault, path, kCFStringEncodingUTF8); CFArrayAppendValue(config.paths, pathRef); CFRelease(pathRef); } // Parse commandline settings static inline void parse_cli_settings(int argc, const char* argv[]) { // runtime os version detection SInt32 osMajorVersion, osMinorVersion; if (!(Gestalt(gestaltSystemVersionMajor, &osMajorVersion) == noErr)) { osMajorVersion = 0; } if (!(Gestalt(gestaltSystemVersionMinor, &osMinorVersion) == noErr)) { osMinorVersion = 0; } if ((osMajorVersion == 10) & (osMinorVersion < 5)) { fprintf(stderr, "The FSEvents API is unavailable on this version of macos!\n"); exit(EXIT_FAILURE); } struct cli_info args_info; cli_parser_init(&args_info); if (cli_parser(argc, argv, &args_info) != 0) { exit(EXIT_FAILURE); } config.paths = CFArrayCreateMutable(NULL, (CFIndex)0, &kCFTypeArrayCallBacks); config.sinceWhen = args_info.since_when_arg; config.latency = args_info.latency_arg; config.format = args_info.format_arg; if (args_info.no_defer_flag) { config.flags |= kFSEventStreamCreateFlagNoDefer; } if (args_info.watch_root_flag) { config.flags |= kFSEventStreamCreateFlagWatchRoot; } if (args_info.ignore_self_flag) { if ((osMajorVersion == 10) & (osMinorVersion >= 6)) { config.flags |= kFSEventStreamCreateFlagIgnoreSelf; } else { fprintf(stderr, "MacOSX 10.6 or later is required for --ignore-self\n"); exit(EXIT_FAILURE); } } if (args_info.file_events_flag) { if ((osMajorVersion == 10) & (osMinorVersion >= 7)) { config.flags |= kFSEventStreamCreateFlagFileEvents; } else { fprintf(stderr, "MacOSX 10.7 or later required for --file-events\n"); exit(EXIT_FAILURE); } } if (args_info.mark_self_flag) { if ((osMajorVersion == 10) & (osMinorVersion >= 9)) { config.flags |= kFSEventStreamCreateFlagMarkSelf; } else { fprintf(stderr, "MacOSX 10.9 or later required for --mark-self\n"); exit(EXIT_FAILURE); } } if (args_info.inputs_num == 0) { append_path("."); } else { for (unsigned int i=0; i < args_info.inputs_num; ++i) { append_path(args_info.inputs[i]); } } cli_parser_free(&args_info); #ifdef DEBUG fprintf(stderr, "config.sinceWhen %llu\n", config.sinceWhen); fprintf(stderr, "config.latency %f\n", config.latency); fprintf(stderr, "config.flags %#.8x\n", config.flags); FLAG_CHECK_STDERR(config.flags, kFSEventStreamCreateFlagUseCFTypes, " Using CF instead of C types"); FLAG_CHECK_STDERR(config.flags, kFSEventStreamCreateFlagNoDefer, " NoDefer latency modifier enabled"); FLAG_CHECK_STDERR(config.flags, kFSEventStreamCreateFlagWatchRoot, " WatchRoot notifications enabled"); FLAG_CHECK_STDERR(config.flags, kFSEventStreamCreateFlagIgnoreSelf, " IgnoreSelf enabled"); FLAG_CHECK_STDERR(config.flags, kFSEventStreamCreateFlagFileEvents, " FileEvents enabled"); fprintf(stderr, "config.paths\n"); long numpaths = CFArrayGetCount(config.paths); for (long i = 0; i < numpaths; i++) { char path[PATH_MAX]; CFStringGetCString(CFArrayGetValueAtIndex(config.paths, i), path, PATH_MAX, kCFStringEncodingUTF8); fprintf(stderr, " %s\n", path); } fprintf(stderr, "\n"); #endif } static void callback(__attribute__((unused)) FSEventStreamRef streamRef, __attribute__((unused)) void* clientCallBackInfo, size_t numEvents, void* eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[]) { char** paths = eventPaths; char *buf = calloc(sizeof(FSEVENTSBITS), sizeof(char)); for (size_t i = 0; i < numEvents; i++) { sprintb(buf, eventFlags[i], FSEVENTSBITS); printf("%llu\t%#.8x=[%s]\t%s\n", eventIds[i], eventFlags[i], buf, paths[i]); } fflush(stdout); free(buf); if (fcntl(STDIN_FILENO, F_GETFD) == -1) { CFRunLoopStop(CFRunLoopGetCurrent()); } } static void stdin_callback(CFFileDescriptorRef fdref, CFOptionFlags callBackTypes, void *info) { char buf[1024]; int nread; do { nread = read(STDIN_FILENO, buf, sizeof(buf)); if (nread == -1 && errno == EAGAIN) { CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack); return; } else if (nread == 0) { exit(1); return; } } while (nread > 0); } int main(int argc, const char* argv[]) { parse_cli_settings(argc, argv); FSEventStreamContext context = {0, NULL, NULL, NULL, NULL}; FSEventStreamRef stream; stream = FSEventStreamCreate(kCFAllocatorDefault, (FSEventStreamCallback)&callback, &context, config.paths, config.sinceWhen, config.latency, config.flags); #ifdef DEBUG FSEventStreamShow(stream); fprintf(stderr, "\n"); #endif fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); CFFileDescriptorRef fdref = CFFileDescriptorCreate(kCFAllocatorDefault, STDIN_FILENO, false, stdin_callback, NULL); CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack); CFRunLoopSourceRef source = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, fdref, 0); CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); CFRelease(source); FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); FSEventStreamStart(stream); CFRunLoopRun(); FSEventStreamFlushSync(stream); FSEventStreamStop(stream); return 0; } // vim: ts=2 sts=2 et sw=2 fs-4.1/c_src/mac/cli.h0000644000232200023220000000133613226474060015114 0ustar debalancedebalance#ifndef CLI_H #define CLI_H #include "common.h" #ifndef CLI_NAME #define CLI_NAME "fsevent_watch" #endif /* CLI_NAME */ struct cli_info { UInt64 since_when_arg; double latency_arg; bool no_defer_flag; bool watch_root_flag; bool ignore_self_flag; bool file_events_flag; bool mark_self_flag; int format_arg; char** inputs; unsigned inputs_num; }; extern const char* cli_info_purpose; extern const char* cli_info_usage; extern const char* cli_info_help[]; void cli_print_help(void); void cli_print_version(void); int cli_parser (int argc, const char** argv, struct cli_info* args_info); void cli_parser_init (struct cli_info* args_info); void cli_parser_free (struct cli_info* args_info); #endif /* CLI_H */ fs-4.1/c_src/mac/common.h0000644000232200023220000000273013226474060015634 0ustar debalancedebalance#ifndef fsevent_watch_common_h #define fsevent_watch_common_h #include #include #include #include #include "compat.h" #define _str(s) #s #define _xstr(s) _str(s) #define COMPILED_AT __DATE__ " " __TIME__ #define FPRINTF_FLAG_CHECK(flags, flag, msg, fd) \ do { \ if ((flags) & (flag)) { \ fprintf(fd, "%s\n", msg); } } \ while (0) #define FLAG_CHECK_STDERR(flags, flag, msg) \ FPRINTF_FLAG_CHECK(flags, flag, msg, stderr) /* * FSEVENTSBITS: * generated by `make printflags` (and pasted here) * flags MUST be ordered (bits ascending) and sorted * * idea from: http://www.openbsd.org/cgi-bin/cvsweb/src/sbin/ifconfig/ifconfig.c (see printb()) */ #define FSEVENTSBITS \ "\1mustscansubdirs\2userdropped\3kerneldropped\4eventidswrapped\5historydone\6rootchanged\7mount\10unmount\11created\12removed\13inodemetamod\14renamed\15modified\16finderinfomod\17changeowner\20xattrmod\21isfile\22isdir\23issymlink\24ownevent" static inline void sprintb(char *buf, unsigned short v, char *bits) { int i, any = 0; char c; char *bufp = buf; while ((i = *bits++)) { if (v & (1 << (i-1))) { if (any) *bufp++ = ','; any = 1; for (; (c = *bits) > 32; bits++) *bufp++ = c; } else for (; *bits > 32; bits++) ; } *bufp = '\0'; } #endif /* fsevent_watch_common_h */ fs-4.1/c_src/bsd/0000755000232200023220000000000013226474060014201 5ustar debalancedebalancefs-4.1/c_src/bsd/main.c0000644000232200023220000000243613226474060015276 0ustar debalancedebalance#include #include #include #include #include #include int main(int argc, char *argv[]) { struct kevent event; struct kevent change; int fd, kq, nev; if ((fd = open(argv[1], O_RDONLY)) == -1) return 1; EV_SET(&change, fd, EVFILT_VNODE , EV_ADD | EV_ENABLE | EV_DISABLE | EV_CLEAR | EV_DELETE | EV_EOF | EV_RECEIPT | EV_DISPATCH | EV_ONESHOT, NOTE_DELETE | NOTE_RENAME | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK | NOTE_REVOKE | NOTE_WRITE, 0, 0); if ((kq = kqueue()) == -1) return 1; nev = kevent(kq, &change, 1, &event, 1, NULL); if (nev < 0) { return 1; } else if (nev > 0) { if (event.flags & EV_ERROR) { return 1; } } close(kq); return 0; } fs-4.1/c_src/windows/0000755000232200023220000000000013226474060015123 5ustar debalancedebalancefs-4.1/c_src/windows/Arguments.cs0000644000232200023220000000145013226474060017417 0ustar debalancedebalanceusing System; using System.Collections.Generic; using System.Text.RegularExpressions; namespace De.Thekid.INotify { public class Arguments { // Default values private List _Events = new List(new string[] { "create", "modify", "delete", "move" }); private string[] _Format = new string[] { "w", " ", "e", " ", "f" }; private List _Paths = new List(); public bool Recursive { get; set; } public bool Monitor { get; set; } public bool Quiet { get; set; } public List Paths { get { return this._Paths; } } public string[] Format { get { return this._Format; } set { this._Format = value; } } public List Events { get { return this._Events; } set { this._Events = value; } } public Regex Exclude { get; set; } } } fs-4.1/c_src/windows/Makefile0000644000232200023220000000055013226474060016563 0ustar debalancedebalanceifeq ($(OS),Windows_NT) BASE=$(shell cd "$(WINDIR)";pwd -W) CSC?=$(shell ls -1d $(BASE)/Microsoft.NET/Framework/v*|sort -rn|head -1)/csc.exe else CSC?=csc endif MKDIR_P = mkdir -p inotifywait.exe: c_src/windows/*.cs ${MKDIR_P} priv $(CSC) //nologo //target:exe //out:priv\\$@ c_src\\windows\\*.cs clean: -rm priv\\inotifywait.exe fs-4.1/c_src/windows/AssemblyInfo.cs0000644000232200023220000000117313226474060020047 0ustar debalancedebalanceusing System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; [assembly: AssemblyTitle("https://github.com/thekid/inotify-win")] [assembly: AssemblyDescription("A port of the inotifywait tool for Windows")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Timm Friebe")] [assembly: AssemblyProduct("inotify-win")] [assembly: AssemblyCopyright("Copyright © 2012 - 2015 Timm Friebe")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: AssemblyVersion("1.5.1.0")] [assembly: ComVisible(false)] [assembly: Guid("4254314b-ae21-4e2f-ba52-d6f3d83a86b5")] fs-4.1/c_src/windows/Runner.cs0000644000232200023220000001325613226474060016732 0ustar debalancedebalanceusing System; using System.Threading; using System.IO; using System.Collections.Generic; namespace De.Thekid.INotify { // List of possible changes public enum Change { CREATE, MODIFY, DELETE, MOVED_FROM, MOVED_TO } /// Main class public class Runner { // Mappings protected static Dictionary Changes = new Dictionary(); private List _threads = new List(); private bool _stopMonitoring = false; private ManualResetEventSlim _stopMonitoringEvent; private object _notificationReactionLock = new object(); private Arguments _args = null; static Runner() { Changes[WatcherChangeTypes.Created]= Change.CREATE; Changes[WatcherChangeTypes.Changed]= Change.MODIFY; Changes[WatcherChangeTypes.Deleted]= Change.DELETE; } public Runner(Arguments args) { _args = args; } /// Callback for errors in watcher protected void OnWatcherError(object source, ErrorEventArgs e) { Console.Error.WriteLine("*** {0}", e.GetException()); } private void OnWatcherNotification(object sender, FileSystemEventArgs e) { FileSystemWatcher w = (FileSystemWatcher)sender; HandleNotification(w, e, () => Output(Console.Out, _args.Format, w, Changes[e.ChangeType], e.Name)); } private void OnRenameNotification(object sender, RenamedEventArgs e) { FileSystemWatcher w = (FileSystemWatcher)sender; HandleNotification(w, e, () => { Output(Console.Out, _args.Format, w, Change.MOVED_FROM, e.OldName); Output(Console.Out, _args.Format, w, Change.MOVED_TO, e.Name); }); } private void HandleNotification(FileSystemWatcher sender, FileSystemEventArgs e, Action outputAction) { FileSystemWatcher w = (FileSystemWatcher)sender; // Lock so we don't output more than one change if we were only supposed to watch for one. // And to serialize access to the console lock (_notificationReactionLock) { // if only looking for one change and another thread beat us to it, return if (!_args.Monitor && _stopMonitoring) { return; } if (null != _args.Exclude && _args.Exclude.IsMatch(e.FullPath)) { return; } outputAction(); // If only looking for one change, signal to stop if (!_args.Monitor) { _stopMonitoring = true; _stopMonitoringEvent.Set(); } } } /// Output method protected void Output(TextWriter writer, string[] tokens, FileSystemWatcher source, Change type, string name) { foreach (var token in tokens) { var path = Path.Combine(source.Path, name); switch (token[0]) { case 'e': writer.Write(type); if (Directory.Exists(path)) { writer.Write(",ISDIR"); } break; case 'f': writer.Write(Path.GetFileName(path)); break; case 'w': writer.Write(Path.Combine(source.Path, Path.GetDirectoryName(path))); break; case 'T': writer.Write(DateTime.Now); break; default: writer.Write(token); break; } } writer.WriteLine(); } public void Stop(object data) { string s = Console.ReadLine(); _stopMonitoring = true; _stopMonitoringEvent.Set(); } public void Processor(object data) { string path = (string)data; using (var w = new FileSystemWatcher { Path = path, IncludeSubdirectories = _args.Recursive, Filter = "*.*" }) { w.Error += new ErrorEventHandler(OnWatcherError); // Parse "events" argument WatcherChangeTypes changes = 0; if (_args.Events.Contains("create")) { changes |= WatcherChangeTypes.Created; w.Created += new FileSystemEventHandler(OnWatcherNotification); } if (_args.Events.Contains("modify")) { changes |= WatcherChangeTypes.Changed; w.Changed += new FileSystemEventHandler(OnWatcherNotification); } if (_args.Events.Contains("delete")) { changes |= WatcherChangeTypes.Deleted; w.Deleted += new FileSystemEventHandler(OnWatcherNotification); } if (_args.Events.Contains("move")) { changes |= WatcherChangeTypes.Renamed; w.Renamed += new RenamedEventHandler(OnRenameNotification); } // Main loop if (!_args.Quiet) { Console.Error.WriteLine( "===> {0} for {1} in {2}{3} for {4}", _args.Monitor ? "Monitoring" : "Watching", changes, path, _args.Recursive ? " -r" : "", String.Join(", ", _args.Events.ToArray()) ); } w.EnableRaisingEvents = true; _stopMonitoringEvent.Wait(); } } /// Entry point public int Run() { using (_stopMonitoringEvent = new ManualResetEventSlim(initialState: false)) { foreach (var path in _args.Paths) { Thread t = new Thread(new ParameterizedThreadStart(Processor)); t.Start(path); _threads.Add(t); } Thread stop = new Thread(new ParameterizedThreadStart(Stop)); stop.Start(""); _stopMonitoringEvent.Wait(); foreach (var thread in _threads) { if (thread.IsAlive) thread.Abort(); thread.Join(); } return 0; } } /// Entry point method public static int Main(string[] args) { var p = new ArgumentParser(); // Show usage if no args or standard "help" args are given if (0 == args.Length || args[0].Equals("-?") || args[0].Equals("--help")) { p.PrintUsage("inotifywait", Console.Error); return 1; } // Run! return new Runner(p.Parse(args)).Run(); } } } fs-4.1/c_src/windows/ArgumentParser.cs0000644000232200023220000001010413226474060020405 0ustar debalancedebalanceusing System; using System.IO; using System.Collections.Generic; using System.Text.RegularExpressions; namespace De.Thekid.INotify { /// See also inotifywait(1) - Linux man page public class ArgumentParser { /// Helper method for parser protected string Value(string[] args, int i, string name) { if (i > args.Length) { throw new ArgumentException("Argument " + name + " requires a value"); } return args[i]; } /// Tokenizes "printf" format string into an array of strings protected string[] TokenizeFormat(string arg) { var result = new List(); var tokens = arg.Split(new char[]{ '%' }); foreach (var token in tokens) { if (token.Length == 0) continue; if ("efwT".IndexOf(token[0]) != -1) { result.Add(token[0].ToString()); if (token.Length > 1) { result.Add(token.Substring(1)); } } else { result.Add(token); } } return result.ToArray(); } private void ParseArgument(string option, string[] args, ref int i, Arguments result) { if ("--recursive" == option || "-r" == option) { result.Recursive = true; } else if ("--monitor" == option || "-m" == option) { result.Monitor = true; } else if ("--quiet" == option || "-q" == option) { result.Quiet = true; } else if ("--event" == option || "-e" == option) { result.Events = new List(Value(args, ++i, "event").Split(',')); } else if ("--format" == option) { result.Format = TokenizeFormat(Value(args, ++i, "format")); } else if ("--exclude" == option) { result.Exclude = new Regex(Value(args, ++i, "exclude")); } else if ("--excludei" == option) { result.Exclude = new Regex(Value(args, ++i, "exclude"), RegexOptions.IgnoreCase); } else if (option.StartsWith("--event=")) { result.Events = new List(option.Split(new Char[]{'='}, 2)[1].Split(',')); } else if (option.StartsWith("--format=")) { result.Format = TokenizeFormat(option.Split(new Char[]{'='}, 2)[1]); } else if (option.StartsWith("--exclude=")) { result.Exclude = new Regex(option.Split(new Char[]{'='}, 2)[1]); } else if (option.StartsWith("--excludei=")) { result.Exclude = new Regex(option.Split(new Char[]{'='}, 2)[1], RegexOptions.IgnoreCase); } else if (Directory.Exists(option)) { result.Paths.Add(System.IO.Path.GetFullPath(option)); } } /// Creates a new argument parser and parses the arguments public Arguments Parse(string[] args) { var result = new Arguments(); for (var i = 0; i < args.Length; i++) { if (!args[i].StartsWith("--") && args[i].StartsWith("-") && args[i].Length > 2) { string options = args[i]; for (var j = 1; j < options.Length; j++) { ParseArgument("-" + options.Substring(j, 1), args, ref i, result); } } else { ParseArgument(args[i], args, ref i, result); } } return result; } /// Usage public void PrintUsage(string name, TextWriter writer) { writer.WriteLine("Usage: " + name + " [options] path [...]"); writer.WriteLine(); writer.WriteLine("Options:"); writer.WriteLine("-r/--recursive: Recursively watch all files and subdirectories inside path"); writer.WriteLine("-m/--monitor: Keep running until killed (e.g. via Ctrl+C)"); writer.WriteLine("-q/--quiet: Do not output information about actions"); writer.WriteLine("-e/--event list: Which events (create, modify, delete, move) to watch, comma-separated. Default: all"); writer.WriteLine("--format format: Format string for output."); writer.WriteLine("--exclude: Do not process any events whose filename matches the specified regex"); writer.WriteLine("--excludei: Ditto, case-insensitive"); writer.WriteLine(); writer.WriteLine("Formats:"); writer.WriteLine("%e : Event name"); writer.WriteLine("%f : File name"); writer.WriteLine("%w : Path name"); writer.WriteLine("%T : Current date and time"); } } } fs-4.1/priv/0000755000232200023220000000000013226474060013320 5ustar debalancedebalancefs-4.1/priv/inotifywait.exe0000644000232200023220000003500013226474060016367 0ustar debalancedebalanceMZ@ !L!This program cannot be run in DOS mode. $PELT . >M `@ @LK`P  H.textD- . `.rsrcP`0@@.reloc 8@B MH. 0-i -rprp( s z +*0s %o 8 o -+br9po o -=o ( o o -o o + o Xi:lo + *0rCp( -r[p( + -o8rap( -rup( + -o 8br{p( -rp( + -o 80rp( -rp( + -;%JX% Trp( ,o s o8rp(  -*%JX% Trp((o8rp(  -)%JX% Trp(s o8\rp(  -*%JX% Trp(s o8 r po  -< =o  ,o s o8rpo  -+ =o (o8r3po  -' =o s o+\rIpo  -( =o s o+"(  -o ( o *0s  8rapo -rgpo ,o +->  +#rgp o! (" ( X o -+(X i:h+*0rkpr{p( o# o$ rpo# rpo# rRpo# rpo# r>po# r po# r_po# r po# o$ r^po# rppo# rpo# rpo# rpo# *(% *0 { +*"}*0 { +*"}*0 { +*"}*0 { +*0 { +*&}*0 { +*&}*0 { +*"}*0| rbprppr~prps } rprprprprp}s }(% *s' ~o( ~o( ~o( *s) }}s% }}(% }*b(* rpo+ o, *(% *0H{(- {{o{~{o. o/ {o0 (*0B s  }}t }{{!s1 (*(% *0h{(- {{o{{o2 ({(- {{o{{o0 (*0B s" }}t }{{#s1 (*0   {% (3 {o - {+ -o{o,{oo4 o5 + -?o6 {o  -}{o7  -(8 *0   8 o9 (: o T.peYE5w.>+io; ( -rpo< +F(= o< +7o9 (> (: o< +(? 'o; + o< + X i:=o$ *0 (@ }{o7 *0t sA oB {ooC rpoD sE oF  {orbpoG -` sH oI {orppoG -` sH oJ {or~poG -` sH oK {orpoG -` sL oM {o :(* rp{o -rp+r$p{o-r:p+r<prDp{oo (N oO oP {oQ -oR *A60sS %}{o oT +1(U sV sW oX {oY (Z   -oR sV sW r:poX {oQ {o[  +' (\ o]   - o^ o_  (`   - oR   -oR *(,Bn80^s i,!rJpoa -rPpoa + -r^p(* o +os( +*BSJB v4.0.30319l #~| T #Stringsx#USH#GUIDX#BlobW %3-##a  c\j\ u{  % C m \I7`7}7777777U6i6ww\\\T buzw\\ \ 8 ] \ );)E) L) W"5<5pppsJVRVYV`VgVrpP o  ud!8$# $* %1 %@ %D %@ &D &+@#&5D,&?ID&IQ\&TVh&_I&j\&e&j&14'i''4(W(~0))*!+"-#d. #'1$'1$b(1$l(j1$ & -22yD1 1+ 2!7)< 2@6F @KHe k))uo} 61 1111!Q!.Y6EYN<q a<1Y<)}q1I11$i<91!Ai1i iDi-A iJI TPI itViViVQ i]dkiDI1Y1ID $ i AA, 2@$2 @, A; @AG 1A1,2 @M }$(,04.k.#*.c. ..$.+;.3L.;$.S$.[v33333333 3@3`33@3`3 Q)7Erw w(w.{4;{B         %S\inotifywait.exeArgumentParserDe.Thekid.INotifyArgumentsChangeRunnermscorlibSystemObjectEnumValueTokenizeFormatParseArgumentParseSystem.IOTextWriterPrintUsage.ctorSystem.Collections.GenericList`1_Events_Format_Pathsget_Recursiveset_Recursiveget_Monitorset_Monitorget_Quietset_Quietget_Pathsget_Formatset_Formatget_Eventsset_EventsSystem.Text.RegularExpressionsRegexget_Excludeset_Excludek__BackingFieldk__BackingFieldk__BackingFieldk__BackingFieldRecursiveMonitorQuietPathsFormatEventsExcludevalue__CREATEMODIFYDELETEMOVED_FROMMOVED_TODictionary`2WatcherChangeTypesChangesSystem.ThreadingThread_threads_stopMonitoringManualResetEventSlim_stopMonitoringEvent_notificationReactionLock_args.cctorErrorEventArgsOnWatcherErrorFileSystemEventArgsOnWatcherNotificationRenamedEventArgsOnRenameNotificationFileSystemWatcherActionHandleNotificationOutputStopProcessorRunMainargsinameargoptionresultwritervaluesourceesenderoutputActiontokenstypedataSystem.ReflectionAssemblyTitleAttributeAssemblyDescriptionAttributeAssemblyConfigurationAttributeAssemblyCompanyAttributeAssemblyProductAttributeAssemblyCopyrightAttributeAssemblyTrademarkAttributeAssemblyCultureAttributeAssemblyVersionAttributeSystem.Runtime.InteropServicesComVisibleAttributeGuidAttributeSystem.Runtime.CompilerServicesCompilationRelaxationsAttributeRuntimeCompatibilityAttributeinotifywaitStringConcatArgumentExceptionCharSplitget_Lengthget_CharsIndexOfToStringAddSubstringToArrayop_EqualityIEnumerable`1RegexOptionsStartsWithDirectoryExistsPathGetFullPathWriteLineCompilerGeneratedAttributeset_ItemConsoleget_ErrorExceptionGetException<>c__DisplayClass1w<>4__thisb__0get_Outget_ChangeTypeget_Itemget_Name<>c__DisplayClass4b__3get_OldNameEnterget_FullPathIsMatchInvokeSetExitget_PathCombineWriteGetFileNameGetDirectoryNameDateTimeget_NowReadLineset_Pathset_IncludeSubdirectoriesset_FilterErrorEventHandleradd_ErrorContainsFileSystemEventHandleradd_Createdadd_Changedadd_DeletedRenamedEventHandleradd_RenamedJoinset_EnableRaisingEventsWaitIDisposableDisposeEnumeratorGetEnumeratorget_CurrentParameterizedThreadStartStartMoveNextget_IsAliveAbortEqualsArgument # requires a value efwT--recursive-r--monitor-m--quiet-q--event-e event--format format--excludeexclude--excludei--event=--format=--exclude=--excludei=---Usage: + [options] path [...]Options:-r/--recursive: Recursively watch all files and subdirectories inside pathy-m/--monitor: Keep running until killed (e.g. via Ctrl+C)q-q/--quiet: Do not output information about actions-e/--event list: Which events (create, modify, delete, move) to watch, comma-separated. Default: allU--format format: Format string for output.--exclude: Do not process any events whose filename matches the specified regexQ--excludei: Ditto, case-insensitiveFormats:7%e : Event name5%f : File name5%w : Path nameM%T : Current date and time create modify delete movew ef*** {0} ,ISDIR*.*E===> {0} for {1} in {2}{3} for {4}WatchingMonitoring -r, -? --helpinotifywait{:F \z\V4                 (((( !%    ) - 1 5-9  5             }        !   5-   15       55   ! !!!%%!*%https://github.com/thekid/inotify-win/*A port of the inotifywait tool for Windows Timm Friebe inotify-win)$Copyright © 2012 - 2015 Timm Friebe)$4254314b-ae21-4e2f-ba52-d6f3d83a86b5TWrapNonExceptionThrowsM.M M_CorExeMainmscoree.dll% @ 8Ph``d4VS_VERSION_INFO?DVarFileInfo$Translation StringFileInfo000004b0p+CommentsA port of the inotifywait tool for Windows8 CompanyNameTimm Friebet&FileDescriptionhttps://github.com/thekid/inotify-win0FileVersion1.5.1.0@InternalNameinotifywait.exel$LegalCopyrightCopyright 2012 - 2015 Timm FriebeHOriginalFilenameinotifywait.exe8 ProductNameinotify-win4ProductVersion1.5.1.08Assembly Version1.5.1.0 @ @=fs-4.1/package.exs0000644000232200023220000000104113226474060014450 0ustar debalancedebalancedefmodule FS.Mixfile do use Mix.Project def project do [app: :fs, version: "3.4.0", description: "Erlang File System Listener", deps: deps, docs: [], package: package] end defp package do [name: :fs, files: ["include", "priv", "src", "c_src", "LICENSE", "README.md", "rebar.config"], maintainers: ["Vladimir Kirillov", "Namdak Tonpa"], licenses: ["MIT"], links: %{"GitHub" => "https://github.com/synrc/fs"}] end defp deps do [{:ex_doc, ">= 0.0.0", only: :dev}] end end fs-4.1/src/0000755000232200023220000000000013226474060013127 5ustar debalancedebalancefs-4.1/src/fs_sup.erl0000644000232200023220000000247313226474060015140 0ustar debalancedebalance-module(fs_sup). -behaviour(supervisor). -export([start_link/4]). -export([init/1]). -define(CHILD(I, Type, Args), {I, {I, start_link, Args}, permanent, 5000, Type, [I]}). start_link(SupName, EventHandler, FileHandler, Path) -> supervisor:start_link({local, SupName}, ?MODULE, [EventHandler, FileHandler, Path]). init([EventHandler, FileHandler, Path]) -> Backend = case os:type() of {unix, darwin} -> fsevents; {unix, linux} -> inotifywait; {unix, sunos} -> undefined; {unix, _} -> kqueue; {win32, nt} -> inotifywait_win32; _ -> undefined end, Children = case has_executable(Backend) of false -> []; true -> [?CHILD(fs_server, worker, [FileHandler, EventHandler, Backend, Path, Path])] end, {ok, { {one_for_one, 5, 10}, Children ++ [?CHILD(gen_event, worker, [{local, EventHandler}])]} }. has_executable(undefined) -> os_not_supported(), false; has_executable(Backend) -> case Backend:find_executable() of false -> backend_port_not_found(Backend), false; _ -> true end. os_not_supported() -> error_logger:warning_msg("fs does not support the current operating system: auto-reloading might not work~n",[]). backend_port_not_found(Backend) -> error_logger:error_msg("backend port not found: ~p~n",[Backend]). fs-4.1/src/fs.erl0000644000232200023220000000376413226474060014255 0ustar debalancedebalance-module(fs). -include_lib("kernel/include/file.hrl"). -export([start_link/1, start_link/2, subscribe/0, subscribe/1, known_events/0, known_events/1, start_looper/0, start_looper/1, find_executable/2, path/0]). % sample subscriber start_link(Name) -> start_link(Name, path()). start_link(Name, Path) -> SupName = name(Name, "sup"), FileHandler = name(Name, "file"), fs_sup:start_link(SupName, Name, FileHandler, Path). subscribe() -> subscribe(default_fs). subscribe(Name) -> gen_event:add_sup_handler(Name, {fs_event_bridge, self()}, [self()]). path() -> case application:get_env(fs, path) of {ok, P} -> filename:absname(P); undefined -> filename:absname("") end. known_events() -> known_events(default_fs). known_events(Name) -> gen_server:call(name(Name, "file"), known_events). start_looper() -> start_looper(default_fs). start_looper(Name) -> spawn(fun() -> subscribe(Name), loop() end). loop() -> receive {_Pid, {fs, file_event}, {Path, Flags}} -> error_logger:info_msg("file_event: ~p ~p", [Path, Flags]); _ -> ignore end, loop(). find_executable(Cmd, DepsPath) -> case priv_file(Cmd) of false -> mad_file(DepsPath); Priv -> Priv end. mad_file(DepsPath) -> case filelib:is_regular(DepsPath) of true -> path() ++ "/" ++ DepsPath; false -> case mad_repl:load_file(DepsPath) of {error,_} -> %% This path has been already checked in find_executable/2 false; {ok,ETSFile} -> filelib:ensure_dir(DepsPath), file:write_file(DepsPath, ETSFile), file:write_file_info(DepsPath, #file_info{mode=8#00555}) end end. priv_file(Cmd) -> case code:priv_dir(fs) of Priv when is_list(Priv) -> Path = filename:join(Priv, Cmd), case filelib:is_regular(Path) of true -> Path; false -> false end; _ -> false end. name(Name, Prefix) -> NameList = erlang:atom_to_list(Name), list_to_atom(NameList ++ Prefix). fs-4.1/src/fs_event_bridge.erl0000644000232200023220000000060313226474060016757 0ustar debalancedebalance-module(fs_event_bridge). -behaviour(gen_event). -export([init/1, handle_event/2, handle_call/2, handle_info/2, code_change/3, terminate/2]). init([Pid]) -> {ok, Pid}. handle_event(Event, Pid) -> Pid ! Event, {ok, Pid}. handle_call(_, State) -> {ok, ok, State}. handle_info(_, State) -> {ok, State}. code_change(_OldVsn, State, _Extra) -> {ok, State}. terminate(_Reason, _State) -> ok. fs-4.1/src/fs.app.src0000644000232200023220000000051713226474060015032 0ustar debalancedebalance{application, fs, [{description, "FS VXZ Listener"}, {vsn, "4.1.0"}, {registered, []}, {applications, [kernel,stdlib]}, {mod, { fs_app, []}}, {env, [ {backwards_compatible, true} ]}, {maintainers,["Maxim Sokhatsky", "Vladimir Kirillov"]}, {licenses, ["ISCL"]}, {links,[{"Github","https://github.com/synrc/fs"}]} ]}. fs-4.1/src/fs_server.erl0000644000232200023220000000271713226474060015640 0ustar debalancedebalance-module(fs_server). -behaviour(gen_server). -define(SERVER, ?MODULE). -export([start_link/5]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2,terminate/2, code_change/3]). -record(state, {event_handler, port, path, backend}). notify(EventHandler, file_event = A, Msg) -> Key = {fs, A}, gen_event:notify(EventHandler, {self(), Key, Msg}). start_link(Name, EventHandler, Backend, Path, Cwd) -> gen_server:start_link({local, Name}, ?MODULE, [EventHandler, Backend, Path, Cwd], []). init([EventHandler, Backend, Path, Cwd]) -> {ok, #state{event_handler=EventHandler,port=Backend:start_port(Path, Cwd),path=Path,backend=Backend}}. handle_call(known_events, _From, #state{backend=Backend} = State) -> {reply, Backend:known_events(), State}; handle_call(_Request, _From, State) -> {reply, ok, State}. handle_cast(_Msg, State) -> {noreply, State}. handle_info({_Port, {data, {eol, Line}}}, #state{event_handler=EventHandler,backend=Backend} = State) -> Event = Backend:line_to_event(Line), notify(EventHandler, file_event, Event), {noreply, State}; handle_info({_Port, {data, {noeol, Line}}}, State) -> error_logger:error_msg("~p line too long: ~p, ignoring~n", [?SERVER, Line]), {noreply, State}; handle_info({_Port, {exit_status, Status}}, State) -> {stop, {port_exit, Status}, State}; handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, #state{port=Port}) -> (catch port_close(Port)), ok. code_change(_OldVsn, State, _Extra) -> {ok, State}. fs-4.1/src/fs_app.erl0000644000232200023220000000042013226474060015077 0ustar debalancedebalance-module(fs_app). -behaviour(application). -export([start/2, stop/1]). start(_StartType, _StartArgs) -> case application:get_env(fs, backwards_compatible) of {ok, false} -> {ok, self()}; {ok, true} -> fs:start_link(default_fs) end. stop(_State) -> ok. fs-4.1/src/sys/0000755000232200023220000000000013226474060013745 5ustar debalancedebalancefs-4.1/src/sys/fsevents.erl0000644000232200023220000000144613226474060016313 0ustar debalancedebalance-module(fsevents). -include("api.hrl"). -export(?API). find_executable() -> fs:find_executable("mac_listener", "deps/fs/priv/mac_listener"). known_events() -> [mustscansubdirs,userdropped,kerneldropped,eventidswrapped,historydone,rootchanged, mount,unmount,created,removed,inodemetamod,renamed,modified,finderinfomod,changeowner, xattrmod,isfile,isdir,issymlink,ownevent]. start_port(Path, Cwd) -> erlang:open_port({spawn_executable, find_executable()}, [stream, exit_status, {line, 16384}, {args, ["-F", Path]}, {cd, Cwd}]). line_to_event(Line) -> [_EventId, Flags1, Path] = string:tokens(Line, [$\t]), [_, Flags2] = string:tokens(Flags1, [$=]), {ok, T, _} = erl_scan:string(Flags2 ++ "."), {ok, Flags} = erl_parse:parse_term(T), {Path, Flags}. fs-4.1/src/sys/kqueue.erl0000644000232200023220000000067513226474060015760 0ustar debalancedebalance-module(kqueue). -include("api.hrl"). -export(?API). known_events() -> [created, deleted, renamed, closed, modified, isdir, undefined]. line_to_event(Line) -> io:format("Line: ~p~n",[Line]), {".",Line}. find_executable() -> fs:find_executable("kqueue", "deps/fs/priv/kqueue"). start_port(Path, Cwd) -> erlang:open_port({spawn_executable, find_executable()}, [stream, exit_status, {line, 16384}, {args, [Path]}, {cd, Cwd}]). fs-4.1/src/sys/inotifywait.erl0000644000232200023220000000256013226474060017022 0ustar debalancedebalance-module(inotifywait). -include("api.hrl"). -export(?API). find_executable() -> os:find_executable("inotifywait"). known_events() -> [created, deleted, renamed, closed, modified, isdir, attribute, undefined]. start_port(Path, Cwd) -> Path1 = filename:absname(Path), Args = ["-c", "inotifywait $0 $@ & PID=$!; read a; kill $PID", "-m", "-e", "modify", "-e", "close_write", "-e", "moved_to", "-e", "create", "-e", "delete", "-e", "attrib", "--quiet", "-r", Path1], erlang:open_port({spawn_executable, os:find_executable("sh")}, [stream, exit_status, {line, 16384}, {args, Args}, {cd, Cwd}]). line_to_event(Line) -> {match, [Dir, Flags1, DirEntry]} = re:run(Line, re(), [{capture, all_but_first, list}]), Flags = [convert_flag(F) || F <- string:tokens(Flags1, ",")], Path = Dir ++ DirEntry, {Path, Flags}. convert_flag("CREATE") -> created; convert_flag("DELETE") -> deleted; convert_flag("ISDIR") -> isdir; convert_flag("MODIFY") -> modified; convert_flag("CLOSE_WRITE") -> modified; convert_flag("CLOSE") -> closed; convert_flag("MOVED_TO") -> renamed; convert_flag("ATTRIB") -> attribute; convert_flag(_) -> undefined. re() -> case get(inotifywait_re) of undefined -> {ok, R} = re:compile("^(.*/) ([A-Z_,]+) (.*)$", [unicode]), put(inotifywait_re, R), R; V -> V end. fs-4.1/src/sys/inotifywait_win32.erl0000644000232200023220000000210013226474060020032 0ustar debalancedebalance-module(inotifywait_win32). -include("api.hrl"). -export(?API). find_executable() -> fs:find_executable("inotifywait.exe", "deps/fs/priv/inotifywait.exe"). known_events() -> [created, modified, removed, renamed, undefined]. start_port(Path, Cwd) -> Path1 = filename:absname(Path), Args = ["-m", "-r", Path1], erlang:open_port({spawn_executable, find_executable()}, [stream, exit_status, {line, 16384}, {args, Args}, {cd, Cwd}]). line_to_event(Line) -> {match, [Dir, Flags1, DirEntry]} = re:run(Line, re(), [{capture, all_but_first, list}]), Flags = [convert_flag(F) || F <- string:tokens(Flags1, ",")], Path = filename:join(Dir,DirEntry), {Path, Flags}. convert_flag("CREATE") -> created; convert_flag("MODIFY") -> modified; convert_flag("DELETE") -> removed; convert_flag("MOVED_TO") -> renamed; convert_flag(_) -> undefined. re() -> case get(inotifywait_re) of undefined -> {ok, R} = re:compile("^(.*\\\\.*) ([A-Z_,]+) (.*)$", [unicode]), put(inotifywait_re, R), R; V -> V end. fs-4.1/rebar.config0000644000232200023220000000060113226474060014617 0ustar debalancedebalance{port_env, [{"darwin", "LDFLAGS", "-framework CoreFoundation -framework CoreServices"}, {"darwin", "CC", "clang"}, {"darwin", "CFLAGS", "-Wno-deprecated-declarations"}, {"freebsd", "LDFLAGS", ""}, {"freebsd", "CC", "cc"}, {"freebsd", "CFLAGS", ""}] }. {port_specs, [{"darwin", "priv/mac_listener", ["c_src/mac/*.c"]}, {"freebsd", "priv/kqueue", ["c_src/bsd/*.c"]} ]}. fs-4.1/include/0000755000232200023220000000000013226474060013763 5ustar debalancedebalancefs-4.1/include/api.hrl0000644000232200023220000000012213226474060015236 0ustar debalancedebalance-define(API, [find_executable/0, start_port/2, known_events/0, line_to_event/1]). fs-4.1/LICENSE0000644000232200023220000000136513226474060013352 0ustar debalancedebalanceCopyright (c) 2013 Vladimir Kirillov Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.