diff --git a/c_src/bsd/main.c b/c_src/bsd/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..72bcb2b4f2a94cb21532807226c97a1848edbf2b
--- /dev/null
+++ b/c_src/bsd/main.c
@@ -0,0 +1,34 @@
+#include <sys/time.h>
+#include <sys/event.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+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;
+}
diff --git a/c_src/mac/cli.c b/c_src/mac/cli.c
new file mode 100644
index 0000000000000000000000000000000000000000..84b0c861aff84c429a61585b99ce4e27b068a672
--- /dev/null
+++ b/c_src/mac/cli.c
@@ -0,0 +1,180 @@
+#include <getopt.h>
+#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;
+}
+
diff --git a/c_src/mac/cli.h b/c_src/mac/cli.h
new file mode 100644
index 0000000000000000000000000000000000000000..f176cf0027f033ebf82e1b76d4973feb3c331108
--- /dev/null
+++ b/c_src/mac/cli.h
@@ -0,0 +1,36 @@
+#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 */
diff --git a/c_src/mac/cli.h.gch b/c_src/mac/cli.h.gch
new file mode 100644
index 0000000000000000000000000000000000000000..1759022172e368b87231c340e27bc560b2f621c3
Binary files /dev/null and b/c_src/mac/cli.h.gch differ
diff --git a/c_src/mac/common.h b/c_src/mac/common.h
new file mode 100644
index 0000000000000000000000000000000000000000..70bd64865d8423a51b9b95cf9b7cec564ed8abe3
--- /dev/null
+++ b/c_src/mac/common.h
@@ -0,0 +1,55 @@
+#ifndef fsevent_watch_common_h
+#define fsevent_watch_common_h
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreServices/CoreServices.h>
+#include <unistd.h>
+#include <fcntl.h>
+#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 */
diff --git a/c_src/mac/common.h.gch b/c_src/mac/common.h.gch
new file mode 100644
index 0000000000000000000000000000000000000000..6d5370128513b2a20009a1a0c060073633b1bc88
Binary files /dev/null and b/c_src/mac/common.h.gch differ
diff --git a/c_src/mac/compat.c b/c_src/mac/compat.c
new file mode 100644
index 0000000000000000000000000000000000000000..ab84dfd41c202df72af5a9380a27e9a659adbf3e
--- /dev/null
+++ b/c_src/mac/compat.c
@@ -0,0 +1,25 @@
+#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
diff --git a/c_src/mac/compat.h b/c_src/mac/compat.h
new file mode 100644
index 0000000000000000000000000000000000000000..d44c0c88506f0967cd593dd8bd9bbeadfdc85f7d
--- /dev/null
+++ b/c_src/mac/compat.h
@@ -0,0 +1,47 @@
+/**
+ * @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 <CoreServices/CoreServices.h>
+#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
diff --git a/c_src/mac/compat.h.gch b/c_src/mac/compat.h.gch
new file mode 100644
index 0000000000000000000000000000000000000000..e792a5ea56ba8ec205919cc9f183543db7e8ee04
Binary files /dev/null and b/c_src/mac/compat.h.gch differ
diff --git a/c_src/mac/main.c b/c_src/mac/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..7b31f27be00815186a52ec4a913083c6dde7d234
--- /dev/null
+++ b/c_src/mac/main.c
@@ -0,0 +1,234 @@
+#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
diff --git a/c_src/windows/ArgumentParser.cs b/c_src/windows/ArgumentParser.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b7dc8fcbbea461a4a11f2815c396243421065118
--- /dev/null
+++ b/c_src/windows/ArgumentParser.cs
@@ -0,0 +1,143 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+namespace De.Thekid.INotify
+{
+
+	/// See also <a href="http://linux.die.net/man/1/inotifywait">inotifywait(1) - Linux man page</a>
+	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<string>();
+			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<string>(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<string>(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");
+		}
+	}
+}
diff --git a/c_src/windows/Arguments.cs b/c_src/windows/Arguments.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b6efd64d8c71a28be51b51a3e5f88208b114e9c6
--- /dev/null
+++ b/c_src/windows/Arguments.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+namespace De.Thekid.INotify
+{
+
+	public class Arguments
+	{
+		// Default values
+		private List<string> _Events = new List<string>(new string[] { "create", "modify", "delete", "move" });
+		private string[] _Format = new string[] { "w", " ", "e", " ", "f" };
+		private List<string> _Paths = new List<string>();
+
+		public bool Recursive { get; set; }
+		public bool Monitor { get; set; }
+		public bool Quiet { get; set; }
+		public List<string> Paths {
+			get { return this._Paths; }
+		}
+		public string[] Format {
+			get { return this._Format; }
+			set { this._Format = value; }
+		}
+		public List<string> Events
+		{
+			get { return this._Events; }
+			set { this._Events = value; }
+		}
+		public Regex Exclude { get; set; }
+	}
+}
diff --git a/c_src/windows/AssemblyInfo.cs b/c_src/windows/AssemblyInfo.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6a90948862bbbbc4aa8fd001cbb584483fce3464
--- /dev/null
+++ b/c_src/windows/AssemblyInfo.cs
@@ -0,0 +1,16 @@
+using 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")]
diff --git a/c_src/windows/Makefile b/c_src/windows/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..861eaa509c7ad19c8dbf2fac66d08e58a03521ab
--- /dev/null
+++ b/c_src/windows/Makefile
@@ -0,0 +1,15 @@
+ifeq ($(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
diff --git a/c_src/windows/Runner.cs b/c_src/windows/Runner.cs
new file mode 100644
index 0000000000000000000000000000000000000000..15adbf86df6c16f283a2863c74c33c476b287321
--- /dev/null
+++ b/c_src/windows/Runner.cs
@@ -0,0 +1,209 @@
+using 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<WatcherChangeTypes, Change> Changes = new Dictionary<WatcherChangeTypes, Change>();
+
+		private List<Thread> _threads = new List<Thread>();
+		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();
+		}
+	}
+}
diff --git a/lib/exfswatch.ex b/lib/exfswatch.ex
index 89196466aaea6ba98e403450c299b120fe7bd8d8..1026ed2a72551395a5e764f96fc7a168b81c883e 100644
--- a/lib/exfswatch.ex
+++ b/lib/exfswatch.ex
@@ -18,10 +18,11 @@ defmodule ExFSWatch do
   end
 
   @backend (case :os.type() do
-    {:unix,    :darwin} -> :fsevents
-    {:unix,    :linux}  -> :inotifywait
-    {:"win32", :nt}     -> :"inotifywait_win32"
-     _                  -> nil
+    {:unix,  :darwin}  -> ExFSWatch.Backends.Fsevents
+    {:unix,  :freebsd} -> ExFSWatch.Backends.Kqueue
+    {:unix,  :linux}   -> ExFSWatch.Backends.InotifyWait
+    {:win32, :nt}      -> ExFSWatch.Backends.InotifyWaitWin32
+    _                  -> nil
   end)
 
   def start(_, _) do
diff --git a/lib/exfswatch/backends/fsevents.ex b/lib/exfswatch/backends/fsevents.ex
new file mode 100644
index 0000000000000000000000000000000000000000..314870992d48b225af2038c220cc6f7d38885784
--- /dev/null
+++ b/lib/exfswatch/backends/fsevents.ex
@@ -0,0 +1,33 @@
+alias ExFSWatch.Utils
+
+defmodule ExFSWatch.Backends.Fsevents do
+
+  def find_executable do
+    :code.priv_dir(:exfswatch) ++ '/mac_listener'
+  end
+
+  def start_port(path, listener_extra_args) do
+    path = path |> Utils.format_path()
+    args = listener_extra_args ++ ['-F' | path]
+    Port.open(
+      {:spawn_executable, find_executable()},
+      [:stream, :exit_status, {:line, 16384}, {:args, args}, {:cd, System.tmp_dir!()}]
+    )
+  end
+
+  def known_events do
+    [ :mustscansubdirs, :userdropped, :kerneldropped, :eventidswrapped, :historydone,
+      :rootchanged, :mount, :unmount, :created, :removed, :inodemetamod, :renamed, :modified,
+      :finderinfomod, :changeowner, :xattrmod, :isfile, :isdir, :issymlink, :ownevent,
+    ]
+  end
+
+  def line_to_event(line) do
+    [_event_id, flags, path] = :string.tokens(line, [?\t])
+    [_, flags] = :string.tokens(flags, [?=])
+    {:ok, t, _} = :erl_scan.string(flags ++ '.')
+    {:ok, flags} = :erl_parse.parse_term(t)
+    {path, flags}
+  end
+
+end
diff --git a/lib/exfswatch/backends/inotify_wait.ex b/lib/exfswatch/backends/inotify_wait.ex
new file mode 100644
index 0000000000000000000000000000000000000000..9309c1096591702f6e812b51ecac27db2c713096
--- /dev/null
+++ b/lib/exfswatch/backends/inotify_wait.ex
@@ -0,0 +1,65 @@
+alias ExFSWatch.Utils
+
+defmodule ExFSWatch.Backends.InotifyWait do
+
+  def find_executable do
+    System.find_executable("sh") |> to_charlist
+  end
+
+  def start_port(path, listener_extra_args) do
+    path = path |> Utils.format_path()
+    args = listener_extra_args ++ [
+      '-c', 'inotifywait $0 $@ & PID=$!; read a; kill $PID',
+      '-m', '-e', 'modify', '-e', 'close_write', '-e', 'moved_to', '-e', 'create',
+      '-r' | path]
+    Port.open(
+      {:spawn_executable, find_executable()},
+      [:stream, :exit_status, {:line, 16384}, {:args, args}, {:cd, System.tmp_dir!()}]
+    )
+  end
+
+  def known_events do
+    [:created, :deleted, :renamed, :closed, :modified, :isdir, :attribute, :undefined]
+  end
+
+  def line_to_event(line) do
+    line
+    |> to_string
+    |> scan1
+    |> scan2(line)
+  end
+
+  def scan1(line) do
+    re = ~r/^(.*) ([A-Z_,]+) (.*)$/
+    case Regex.scan re, line do
+      [] -> {:error, :unknown}
+      [[_, path, events, file]] ->
+        {Path.join(path, file), parse_events(events)}
+    end
+
+  end
+  def scan2({:error, :unknown}, line) do
+    re = ~r/^(.*) ([A-Z_,]+)$/
+    case Regex.scan re, line do
+      [] -> {:error, :unknown}
+      [[_, path, events]] ->
+        {path, parse_events(events)}
+    end
+  end
+  def scan2(res, _), do: res
+
+  def parse_events(events) do
+    String.split(events, ",")
+    |> Enum.map(&(convert_flag &1))
+  end
+
+  def convert_flag("CREATE"),      do: :created
+  def convert_flag("DELETE"),      do: :deleted
+  def convert_flag("ISDIR"),       do: :isdir
+  def convert_flag("MODIFY"),      do: :modified
+  def convert_flag("CLOSE_WRITE"), do: :modified
+  def convert_flag("CLOSE"),       do: :closed
+  def convert_flag("MOVED_TO"),    do: :renamed
+  def convert_flag("ATTRIB"),      do: :attribute
+  def convert_flag(_),             do: :undefined
+end
diff --git a/lib/exfswatch/backends/inotify_wait_win32.ex b/lib/exfswatch/backends/inotify_wait_win32.ex
new file mode 100644
index 0000000000000000000000000000000000000000..acfadc248651263efad7d3079eebd00ec385f381
--- /dev/null
+++ b/lib/exfswatch/backends/inotify_wait_win32.ex
@@ -0,0 +1,37 @@
+alias ExFSWatch.Utils
+
+defmodule ExFSWatch.Backends.InotifyWaitWin32 do
+
+  @re :re.compile('^(.*\\\\.*) ([A-Z_,]+) (.*)$', [:unicode]) |> elem(1)
+
+  def find_executable do
+    :code.priv_dir(:exfswatch) ++ '/inotifywait.exe'
+  end
+
+  def start_port(path, listener_extra_args) do
+    path = path |> Utils.format_path()
+    args = listener_extra_args ++ ['-m', '-r' | path]
+    Port.open(
+      {:spawn_executable, find_executable()},
+      [:stream, :exit_status, {:line, 16384}, {:args, args}, {:cd, System.tmp_dir!()}]
+    )
+  end
+
+  def known_events do
+    [:created, :modified, :removed, :renamed, :undefined]
+  end
+
+  def line_to_event(line) do
+    {:match, [dir, flags, dir_entry]} = :re.run(line, @re, [{:capture, :all_but_first, :list}])
+    flags = for f <- :string.tokens(flags, ','), do: convert_flag(f)
+    path = :filename.join(dir, dir_entry)
+    {path, flags}
+  end
+
+  defp convert_flag('CREATE'),   do: :created
+  defp convert_flag('MODIFY'),   do: :modified
+  defp convert_flag('DELETE'),   do: :removed
+  defp convert_flag('MOVED_TO'), do: :renamed
+  defp convert_flag(_),          do: :undefined
+
+end
diff --git a/lib/exfswatch/backends/kqueue.ex b/lib/exfswatch/backends/kqueue.ex
new file mode 100644
index 0000000000000000000000000000000000000000..81adba2f1fe98fa3595e15237aa7ca91a69be539
--- /dev/null
+++ b/lib/exfswatch/backends/kqueue.ex
@@ -0,0 +1,26 @@
+alias ExFSWatch.Utils
+
+defmodule ExFSWatch.Backends.Kqueue do
+
+  def known_events do
+    [:created, :deleted, :renamed, :closed, :modified, :isdir, :undefined]
+  end
+
+  def find_executable do
+    :code.priv_dir(:exfswatch) ++ '/kqueue'
+  end
+
+  def start_port(path, listener_extra_args) do
+    path = path |> Utils.format_path()
+    args = listener_extra_args ++ [path]
+    Port.open(
+      {:spawn_executable, find_executable()},
+      [:stream, :exit_status, {:line, 16384}, {:args, args}, {:cd, System.tmp_dir!()}]
+    )
+  end
+
+  def line_to_event(line) do
+    {'.', line}
+  end
+
+end
diff --git a/lib/exfswatch/sys/inotifywait.ex b/lib/exfswatch/sys/inotifywait.ex
deleted file mode 100644
index 30ca0bf8307d4741db8b5365a92588c93491386c..0000000000000000000000000000000000000000
--- a/lib/exfswatch/sys/inotifywait.ex
+++ /dev/null
@@ -1,43 +0,0 @@
-defmodule ExFSWatch.Sys.InotifyWait do
-
-  def line_to_event(line) do
-    line
-    |> to_string
-    |> scan1
-    |> scan2(line)
-  end
-
-  def scan1(line) do
-    re = ~r/^(.*) ([A-Z_,]+) (.*)$/
-    case Regex.scan re, line do
-      [] -> {:error, :unknown}
-      [[_, path, events, file]] ->
-        {Path.join(path, file), parse_events(events)}
-    end
-
-  end
-  def scan2({:error, :unknown}, line) do
-    re = ~r/^(.*) ([A-Z_,]+)$/
-    case Regex.scan re, line do
-      [] -> {:error, :unknown}
-      [[_, path, events]] ->
-        {path, parse_events(events)}
-    end
-  end
-  def scan2(res, _), do: res
-
-  def parse_events(events) do
-    String.split(events, ",")
-    |> Enum.map(&(convert_flag &1))
-  end
-
-  def convert_flag("CLOSE_WRITE"), do: :modified
-  def convert_flag("CLOSE"), do: :closed
-  def convert_flag("CREATE"), do: :create
-  def convert_flag("MOVED_TO"), do: :renamed
-  def convert_flag("ISDIR"), do: :isdir
-  def convert_flag("DELETE_SELF"), do: :delete_self
-  def convert_flag("DELETE"), do: :deleted
-
-  def convert_flag(_), do: :unknown
-end
diff --git a/lib/exfswatch/utils.ex b/lib/exfswatch/utils.ex
new file mode 100644
index 0000000000000000000000000000000000000000..218a8db03b642636361b7a86ab89db93b071bcc5
--- /dev/null
+++ b/lib/exfswatch/utils.ex
@@ -0,0 +1,13 @@
+defmodule ExFSWatch.Utils do
+
+  def format_path(path) when is_list(path) do
+    for i <- path do
+      i |> Path.absname |> to_char_list
+    end
+  end
+
+  def format_path(path) do
+    [path] |> format_path
+  end
+
+end
diff --git a/lib/exfswatch/worker.ex b/lib/exfswatch/worker.ex
index bacd172138d789e450d9935d8290c21527903d01..f02fe6d4ba232ec5dd358ab9fd674744a5a2471e 100644
--- a/lib/exfswatch/worker.ex
+++ b/lib/exfswatch/worker.ex
@@ -9,12 +9,12 @@ defmodule ExFSWatch.Worker do
 
   def init(module) do
     backend = ExFSWatch.backend
-    port = start_port(backend, module.__dirs__, module.__listener_extra_args__)
+    port = backend.start_port(module.__dirs__, module.__listener_extra_args__)
     {:ok, %__MODULE__{port: port, backend: backend, module: module}}
   end
 
   def handle_info({port, {:data, {:eol, line}}}, %__MODULE__{port: port, backend: backend, module: module}=sd) do
-    {file_path, events} = backend(backend).line_to_event(line)
+    {file_path, events} = backend.line_to_event(line)
     module.callback(file_path |> to_string, events)
     {:noreply, sd}
   end
@@ -27,42 +27,4 @@ defmodule ExFSWatch.Worker do
   def handle_info(_, sd) do
     {:noreply, sd}
   end
-
-
-  defp start_port(:fsevents, path, listener_extra_args) do
-    path = path |> format_path |> IO.inspect
-    args = listener_extra_args ++ ['-F' | path] |> IO.inspect
-    Port.open({:spawn_executable, :fsevents.find_executable()},
-              [:stream, :exit_status, {:line, 16384}, {:args, args}, {:cd, System.tmp_dir!}]
-    )
-  end
-  defp start_port(:inotifywait, path, listener_extra_args) do
-    path = path |> format_path
-    args = listener_extra_args ++ ['-c', 'inotifywait $0 $@ & PID=$!; read a; kill $PID',
-             '-m', '-e', 'close_write', '-e', 'moved_to', '-e', 'create', '-e',
-             'delete_self', '-e', 'delete', '-r' | path
-           ]
-    Port.open({:spawn_executable, :os.find_executable('sh')},
-              [:stream, :exit_status, {:line, 16384}, {:args, args}, {:cd, System.tmp_dir!}]
-    )
-  end
-  defp start_port(:"inotifywait_win32", path, listener_extra_args) do
-    path = path |> format_path
-    args = listener_extra_args ++ ['-m', '-r' | path]
-    Port.open({:spawn_executable, :"inotifywait_win32".find_executable()},
-              [:stream, :exit_status, {:line, 16384}, {:args, args}, {:cd, System.tmp_dir!}]
-    )
-  end
-
-  defp format_path(path) when is_list(path) do
-    for i <- path do
-      i |> Path.absname |> to_char_list
-    end
-  end
-  defp format_path(path) do
-    [path] |> format_path
-  end
-
-  defp backend(:inotifywait), do: ExFSWatch.Sys.InotifyWait
-  defp backend(be), do: be
 end
diff --git a/mix.exs b/mix.exs
index 23ebc0d067424c0ad44b94f3fea86f199877c77c..7dea1ee1ce476652fc5c752478bc5e78342850e6 100644
--- a/mix.exs
+++ b/mix.exs
@@ -1,3 +1,16 @@
+defmodule Mix.Tasks.Compile.Src do
+  def run(_) do
+    priv_dir = :code.priv_dir(:exfswatch)
+    case :os.type() do
+      {:unix, :darwin} ->
+        Mix.shell.cmd("clang -framework CoreFoundation -framework CoreServices -Wno-deprecated-declarations c_src/mac/*.c -o #{priv_dir}/mac_listener")
+      {:unix, :freebsd} ->
+        Mix.shell.cmd("cc c_src/bsd/*.c -o #{priv_dir}/kqueue")
+      _ -> nil
+    end
+  end
+end
+
 defmodule ExFSWatch.Mixfile do
   use Mix.Project
 
@@ -5,6 +18,7 @@ defmodule ExFSWatch.Mixfile do
     [ app: :exfswatch,
       version: "0.3.2",
       elixir: "~> 1.0",
+      compilers: [ :src, :elixir, :app ],
       deps: deps(),
       description: "A file change watcher wrapper based on [fs](https://github.com/synrc/fs)",
       source_url: "https://github.com/falood/exfswatch",
@@ -18,14 +32,12 @@ defmodule ExFSWatch.Mixfile do
 
   def application do
     [ mod: { ExFSWatch, [] },
-      applications: [:logger],
-      included_applications: [:fs],
+      included_applications: [:logger],
     ]
   end
 
   defp deps do
-    [ { :fs,     "~> 2.12" },
-      { :ex_doc, "~> 0.14", only: :docs },
+    [ { :ex_doc, "~> 0.14", only: :docs },
     ]
   end
 
diff --git a/priv/inotifywait.exe b/priv/inotifywait.exe
new file mode 100644
index 0000000000000000000000000000000000000000..8b6a86ecc6dd6e5096aeaec450b76cee1504e44b
Binary files /dev/null and b/priv/inotifywait.exe differ
diff --git a/test/exfswatch_test.exs b/test/backends/fsevents.exs
similarity index 70%
rename from test/exfswatch_test.exs
rename to test/backends/fsevents.exs
index d1662723d96d468f754fdf0c091e7e0c8b1820b3..d50a0c95b5f9fff9776a3eb46b8298e19bea9a2e 100644
--- a/test/exfswatch_test.exs
+++ b/test/backends/fsevents.exs
@@ -1,6 +1,6 @@
-defmodule ExfswatchTest do
+defmodule ExFSWatch.Backends.FseventsTest do
   use ExUnit.Case
-  import :fsevents
+  import ExFSWatch.Backends.Fsevents
 
   test "file modified" do
     assert line_to_event('37425557\t0x00011400=[inodemetamod,modified]\t/one/two/file') ==
diff --git a/test/inotifywait_test.exs b/test/backends/inotify_wait_test.exs
similarity index 90%
rename from test/inotifywait_test.exs
rename to test/backends/inotify_wait_test.exs
index 84f9842c43409dac448f31eb480bdc78cb61b368..22a5f9d4dde5712e51603069b69128925960275a 100644
--- a/test/inotifywait_test.exs
+++ b/test/backends/inotify_wait_test.exs
@@ -1,6 +1,6 @@
-defmodule Exfswatch.Sys.InotifyWaitTest do
+defmodule ExFSWatch.Backends.InotifyWaitTest do
   use ExUnit.Case
-  import ExFSWatch.Sys.InotifyWait
+  import ExFSWatch.Backends.InotifyWait
 
   test "dir write close" do
     assert line_to_event("/one/two/ CLOSE_WRITE,CLOSE file") ==