guard 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/CHANGELOG.md +17 -0
  2. data/README.md +50 -30
  3. data/bin/fsevent_watch_guard_guard +0 -0
  4. data/lib/guard.rb +44 -26
  5. data/lib/guard/cli.rb +23 -6
  6. data/lib/guard/dsl.rb +33 -15
  7. data/lib/guard/listener.rb +1 -1
  8. data/lib/guard/listeners/darwin.rb +1 -1
  9. data/lib/guard/notifier.rb +3 -1
  10. data/lib/guard/notifiers/notifysend.rb +81 -0
  11. data/lib/guard/version.rb +1 -1
  12. data/lib/guard/version.rbc +180 -0
  13. data/lib/vendor/darwin/README.rdoc +3 -2
  14. data/lib/vendor/darwin/bin/fsevent_watch +0 -0
  15. data/lib/vendor/darwin/ext/fsevent_watch/Info.plist +38 -0
  16. data/lib/vendor/darwin/ext/fsevent_watch/LICENSE +21 -0
  17. data/lib/vendor/darwin/ext/fsevent_watch/fsevent_watch.xcodeproj/project.pbxproj +254 -0
  18. data/lib/vendor/darwin/ext/fsevent_watch/fsevent_watch/TSICTString.c +394 -0
  19. data/lib/vendor/darwin/ext/fsevent_watch/fsevent_watch/TSICTString.h +74 -0
  20. data/lib/vendor/darwin/ext/fsevent_watch/fsevent_watch/cli.c +160 -0
  21. data/lib/vendor/darwin/ext/fsevent_watch/fsevent_watch/cli.h +45 -0
  22. data/lib/vendor/darwin/ext/fsevent_watch/fsevent_watch/common.h +34 -0
  23. data/lib/vendor/darwin/ext/fsevent_watch/fsevent_watch/compat.c +20 -0
  24. data/lib/vendor/darwin/ext/fsevent_watch/fsevent_watch/compat.h +40 -0
  25. data/lib/vendor/darwin/ext/fsevent_watch/fsevent_watch/main.c +509 -0
  26. data/lib/vendor/darwin/ext/fsevent_watch/xcconfig/Common.xcconfig +82 -0
  27. data/lib/vendor/darwin/ext/fsevent_watch/xcconfig/Debug.xcconfig +19 -0
  28. data/lib/vendor/darwin/ext/fsevent_watch/xcconfig/Release.xcconfig +23 -0
  29. data/lib/vendor/darwin/ext/fsevent_watch/xcconfig/fsevent_watch.xcconfig +17 -0
  30. data/lib/vendor/darwin/ext/rakefile.rb +47 -0
  31. data/lib/vendor/darwin/ext/rb-fsevent.xcconfig +33 -0
  32. data/lib/vendor/darwin/lib/rb-fsevent/version.rb +1 -1
  33. data/lib/vendor/darwin/rb-fsevent.gemspec +3 -2
  34. data/lib/vendor/darwin/spec/spec_helper.rb +1 -2
  35. metadata +40 -25
  36. data/lib/vendor/darwin/ext/extconf.rb +0 -64
  37. data/lib/vendor/darwin/ext/fsevent/fsevent_watch.c +0 -226
@@ -0,0 +1,160 @@
1
+ #include <getopt.h>
2
+ #include "cli.h"
3
+
4
+ const char* cli_info_purpose = "A flexible command-line interface for the FSEvents API";
5
+ const char* cli_info_usage = "Usage: fsevent_watch [OPTIONS]... [PATHS]...";
6
+ const char* cli_info_help[] = {
7
+ " -h, --help you're looking at it",
8
+ " -V, --version print version number and exit",
9
+ " -s, --since-when=EventID fire historical events since ID",
10
+ " -l, --latency=seconds latency period (default='0.5')",
11
+ " -n, --no-defer enable no-defer latency modifier",
12
+ " -r, --watch-root watch for when the root path has changed",
13
+ // " -i, --ignore-self ignore current process",
14
+ " -F, --file-events provide file level event data",
15
+ " -f, --format=name output format (classic, niw, \n"
16
+ " tnetstring, otnetstring)",
17
+ 0
18
+ };
19
+
20
+ static void default_args (struct cli_info* args_info)
21
+ {
22
+ args_info->since_when_arg = kFSEventStreamEventIdSinceNow;
23
+ args_info->latency_arg = 0.5;
24
+ args_info->no_defer_flag = false;
25
+ args_info->watch_root_flag = false;
26
+ args_info->ignore_self_flag = false;
27
+ args_info->file_events_flag = false;
28
+ args_info->format_arg = kFSEventWatchOutputFormatClassic;
29
+ }
30
+
31
+ static void cli_parser_release (struct cli_info* args_info)
32
+ {
33
+ unsigned int i;
34
+
35
+ for (i=0; i < args_info->inputs_num; ++i) {
36
+ free(args_info->inputs[i]);
37
+ }
38
+
39
+ if (args_info->inputs_num) {
40
+ free(args_info->inputs);
41
+ }
42
+
43
+ args_info->inputs_num = 0;
44
+ }
45
+
46
+ void cli_parser_init (struct cli_info* args_info)
47
+ {
48
+ default_args(args_info);
49
+
50
+ args_info->inputs = 0;
51
+ args_info->inputs_num = 0;
52
+ }
53
+
54
+ void cli_parser_free (struct cli_info* args_info)
55
+ {
56
+ cli_parser_release(args_info);
57
+ }
58
+
59
+ void cli_print_version (void)
60
+ {
61
+ printf("%s %s\n", CLI_NAME, CLI_VERSION);
62
+ #ifdef COMPILED_AT
63
+ printf("Compiled %s\n", COMPILED_AT);
64
+ #endif
65
+ }
66
+
67
+ void cli_print_help (void)
68
+ {
69
+ cli_print_version();
70
+
71
+ printf("\n%s\n", cli_info_purpose);
72
+ printf("\n%s\n", cli_info_usage);
73
+ printf("\n");
74
+
75
+ int i = 0;
76
+ while (cli_info_help[i]) {
77
+ printf("%s\n", cli_info_help[i++]);
78
+ }
79
+ }
80
+
81
+ int cli_parser (int argc, const char** argv, struct cli_info* args_info)
82
+ {
83
+ static struct option longopts[] = {
84
+ { "help", no_argument, NULL, 'h' },
85
+ { "version", no_argument, NULL, 'V' },
86
+ { "since-when", required_argument, NULL, 's' },
87
+ { "latency", required_argument, NULL, 'l' },
88
+ { "no-defer", no_argument, NULL, 'n' },
89
+ { "watch-root", no_argument, NULL, 'r' },
90
+ { "ignore-self", no_argument, NULL, 'i' },
91
+ { "file-events", no_argument, NULL, 'F' },
92
+ { "format", required_argument, NULL, 'f' },
93
+ { 0, 0, 0, 0 }
94
+ };
95
+
96
+ const char* shortopts = "hVs:l:nriFf:";
97
+
98
+ int c = -1;
99
+
100
+ while ((c = getopt_long(argc, (char * const*)argv, shortopts, longopts, NULL)) != -1) {
101
+ switch(c) {
102
+ case 's': // since-when
103
+ args_info->since_when_arg = strtoull(optarg, NULL, 0);
104
+ break;
105
+ case 'l': // latency
106
+ args_info->latency_arg = strtod(optarg, NULL);
107
+ break;
108
+ case 'n': // no-defer
109
+ args_info->no_defer_flag = true;
110
+ break;
111
+ case 'r': // watch-root
112
+ args_info->watch_root_flag = true;
113
+ break;
114
+ case 'i': // ignore-self
115
+ args_info->ignore_self_flag = true;
116
+ break;
117
+ case 'F': // file-events
118
+ args_info->file_events_flag = true;
119
+ break;
120
+ case 'f': // format
121
+ if (strcmp(optarg, "classic") == 0) {
122
+ args_info->format_arg = kFSEventWatchOutputFormatClassic;
123
+ } else if (strcmp(optarg, "niw") == 0) {
124
+ args_info->format_arg = kFSEventWatchOutputFormatNIW;
125
+ } else if (strcmp(optarg, "tnetstring") == 0) {
126
+ args_info->format_arg = kFSEventWatchOutputFormatTNetstring;
127
+ } else if (strcmp(optarg, "otnetstring") == 0) {
128
+ args_info->format_arg = kFSEventWatchOutputFormatOTNetstring;
129
+ } else {
130
+ fprintf(stderr, "Unknown output format: %s\n", optarg);
131
+ exit(EXIT_FAILURE);
132
+ }
133
+ break;
134
+ case 'V': // version
135
+ cli_print_version();
136
+ exit(EXIT_SUCCESS);
137
+ break;
138
+ case 'h': // help
139
+ case '?': // invalid option
140
+ case ':': // missing argument
141
+ cli_print_help();
142
+ exit((c == 'h') ? EXIT_SUCCESS : EXIT_FAILURE);
143
+ break;
144
+ }
145
+ }
146
+
147
+ if (optind < argc) {
148
+ int i = 0;
149
+ args_info->inputs_num = (unsigned int)(argc - optind);
150
+ args_info->inputs =
151
+ (char**)(malloc ((args_info->inputs_num)*sizeof(char*)));
152
+ while (optind < argc)
153
+ if (argv[optind++] != argv[0]) {
154
+ args_info->inputs[i++] = strdup(argv[optind-1]);
155
+ }
156
+ }
157
+
158
+ return EXIT_SUCCESS;
159
+ }
160
+
@@ -0,0 +1,45 @@
1
+ #ifndef CLI_H
2
+ #define CLI_H
3
+
4
+ #ifndef CLI_NAME
5
+ #define CLI_NAME "fsevent_watch"
6
+ #endif /* CLI_NAME */
7
+
8
+ #ifndef PROJECT_VERSION
9
+ #error "PROJECT_VERSION not set"
10
+ #endif /* PROJECT_VERSION */
11
+
12
+ #ifndef CLI_VERSION
13
+ #define _str(s) #s
14
+ #define _xstr(s) _str(s)
15
+ #define CLI_VERSION _xstr(PROJECT_VERSION)
16
+ #endif /* CLI_VERSION */
17
+
18
+ #include "common.h"
19
+
20
+ struct cli_info {
21
+ UInt64 since_when_arg;
22
+ double latency_arg;
23
+ bool no_defer_flag;
24
+ bool watch_root_flag;
25
+ bool ignore_self_flag;
26
+ bool file_events_flag;
27
+ enum FSEventWatchOutputFormat format_arg;
28
+
29
+ char** inputs;
30
+ unsigned inputs_num;
31
+ };
32
+
33
+ extern const char* cli_info_purpose;
34
+ extern const char* cli_info_usage;
35
+ extern const char* cli_info_help[];
36
+
37
+ void cli_print_help(void);
38
+ void cli_print_version(void);
39
+
40
+ int cli_parser (int argc, const char** argv, struct cli_info* args_info);
41
+ void cli_parser_init (struct cli_info* args_info);
42
+ void cli_parser_free (struct cli_info* args_info);
43
+
44
+
45
+ #endif /* CLI_H */
@@ -0,0 +1,34 @@
1
+ #ifndef fsevent_watch_common_h
2
+ #define fsevent_watch_common_h
3
+
4
+ #include <CoreFoundation/CoreFoundation.h>
5
+ #ifdef __OBJC__
6
+ #import <Foundation/Foundation.h>
7
+ #endif
8
+
9
+ #include <CoreServices/CoreServices.h>
10
+ #include <unistd.h>
11
+ #include "compat.h"
12
+ #include "TSICTString.h"
13
+
14
+ #define COMPILED_AT __DATE__ " " __TIME__
15
+
16
+ #define FLAG_CHECK(flags, flag) ((flags) & (flag))
17
+
18
+ #define FPRINTF_FLAG_CHECK(flags, flag, msg, fd) \
19
+ do { \
20
+ if (FLAG_CHECK(flags, flag)) { \
21
+ fprintf(fd, "%s", msg "\n"); } } \
22
+ while (0)
23
+
24
+ #define FLAG_CHECK_STDERR(flags, flag, msg) \
25
+ FPRINTF_FLAG_CHECK(flags, flag, msg, stderr)
26
+
27
+ enum FSEventWatchOutputFormat {
28
+ kFSEventWatchOutputFormatClassic,
29
+ kFSEventWatchOutputFormatNIW,
30
+ kFSEventWatchOutputFormatTNetstring,
31
+ kFSEventWatchOutputFormatOTNetstring
32
+ };
33
+
34
+ #endif /* fsevent_watch_common_h */
@@ -0,0 +1,20 @@
1
+ #include "compat.h"
2
+
3
+ #if MAC_OS_X_VERSION_MAX_ALLOWED < 1060
4
+ FSEventStreamCreateFlags kFSEventStreamCreateFlagIgnoreSelf = 0x00000008;
5
+ #endif
6
+
7
+ #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
8
+ FSEventStreamCreateFlags kFSEventStreamCreateFlagFileEvents = 0x00000010;
9
+ FSEventStreamEventFlags kFSEventStreamEventFlagItemCreated = 0x00000100;
10
+ FSEventStreamEventFlags kFSEventStreamEventFlagItemRemoved = 0x00000200;
11
+ FSEventStreamEventFlags kFSEventStreamEventFlagItemInodeMetaMod = 0x00000400;
12
+ FSEventStreamEventFlags kFSEventStreamEventFlagItemRenamed = 0x00000800;
13
+ FSEventStreamEventFlags kFSEventStreamEventFlagItemModified = 0x00001000;
14
+ FSEventStreamEventFlags kFSEventStreamEventFlagItemFinderInfoMod = 0x00002000;
15
+ FSEventStreamEventFlags kFSEventStreamEventFlagItemChangeOwner = 0x00004000;
16
+ FSEventStreamEventFlags kFSEventStreamEventFlagItemXattrMod = 0x00008000;
17
+ FSEventStreamEventFlags kFSEventStreamEventFlagItemIsFile = 0x00010000;
18
+ FSEventStreamEventFlags kFSEventStreamEventFlagItemIsDir = 0x00020000;
19
+ FSEventStreamEventFlags kFSEventStreamEventFlagItemIsSymlink = 0x00040000;
20
+ #endif
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @headerfile compat.h
3
+ * FSEventStream flag compatibility shim
4
+ *
5
+ * In order to compile a binary against an older SDK yet still support the
6
+ * features present in later OS releases, we need to define any missing enum
7
+ * constants not present in the older SDK. This allows us to safely defer
8
+ * feature detection to runtime (and avoid recompilation).
9
+ */
10
+
11
+
12
+ #ifndef fsevent_watch_compat_h
13
+ #define fsevent_watch_compat_h
14
+
15
+ #ifndef __CORESERVICES__
16
+ #include <CoreServices/CoreServices.h>
17
+ #endif // __CORESERVICES__
18
+
19
+ #if MAC_OS_X_VERSION_MAX_ALLOWED < 1060
20
+ // ignoring events originating from the current process introduced in 10.6
21
+ extern FSEventStreamCreateFlags kFSEventStreamCreateFlagIgnoreSelf;
22
+ #endif
23
+
24
+ #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
25
+ // file-level events introduced in 10.7
26
+ extern FSEventStreamCreateFlags kFSEventStreamCreateFlagFileEvents;
27
+ extern FSEventStreamEventFlags kFSEventStreamEventFlagItemCreated,
28
+ kFSEventStreamEventFlagItemRemoved,
29
+ kFSEventStreamEventFlagItemInodeMetaMod,
30
+ kFSEventStreamEventFlagItemRenamed,
31
+ kFSEventStreamEventFlagItemModified,
32
+ kFSEventStreamEventFlagItemFinderInfoMod,
33
+ kFSEventStreamEventFlagItemChangeOwner,
34
+ kFSEventStreamEventFlagItemXattrMod,
35
+ kFSEventStreamEventFlagItemIsFile,
36
+ kFSEventStreamEventFlagItemIsDir,
37
+ kFSEventStreamEventFlagItemIsSymlink;
38
+ #endif
39
+
40
+ #endif // fsevent_watch_compat_h
@@ -0,0 +1,509 @@
1
+ #include "common.h"
2
+ #include "cli.h"
3
+
4
+ // TODO: set on fire. cli.{h,c} handle both parsing and defaults, so there's
5
+ // no need to set those here. also, in order to scope metadata by path,
6
+ // each stream will need its own configuration... so this won't work as
7
+ // a global any more. In the end the goal is to make the output format
8
+ // able to declare not just that something happened and what flags were
9
+ // attached, but what path it was watching that caused those events (so
10
+ // that the path itself can be used for routing that information to the
11
+ // relevant callback).
12
+ //
13
+ // Structure for storing metadata parsed from the commandline
14
+ static struct {
15
+ FSEventStreamEventId sinceWhen;
16
+ CFTimeInterval latency;
17
+ FSEventStreamCreateFlags flags;
18
+ CFMutableArrayRef paths;
19
+ enum FSEventWatchOutputFormat format;
20
+ } config = {
21
+ (UInt64) kFSEventStreamEventIdSinceNow,
22
+ (double) 0.3,
23
+ (CFOptionFlags) kFSEventStreamCreateFlagNone,
24
+ NULL,
25
+ kFSEventWatchOutputFormatClassic
26
+ };
27
+
28
+ // Prototypes
29
+ static void append_path(const char* path);
30
+ static inline void parse_cli_settings(int argc, const char* argv[]);
31
+ static void callback(FSEventStreamRef streamRef,
32
+ void* clientCallBackInfo,
33
+ size_t numEvents,
34
+ void* eventPaths,
35
+ const FSEventStreamEventFlags eventFlags[],
36
+ const FSEventStreamEventId eventIds[]);
37
+
38
+
39
+ // Resolve a path and append it to the CLI settings structure
40
+ // The FSEvents API will, internally, resolve paths using a similar scheme.
41
+ // Performing this ahead of time makes things less confusing, IMHO.
42
+ static void append_path(const char* path)
43
+ {
44
+ #ifdef DEBUG
45
+ fprintf(stderr, "\n");
46
+ fprintf(stderr, "append_path called for: %s\n", path);
47
+ #endif
48
+
49
+ #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
50
+
51
+ #ifdef DEBUG
52
+ fprintf(stderr, "compiled against 10.6+, using CFURLCreateFileReferenceURL\n");
53
+ #endif
54
+
55
+ CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8*)path, (CFIndex)strlen(path), false);
56
+ CFURLRef placeholder = CFURLCopyAbsoluteURL(url);
57
+ CFRelease(url);
58
+
59
+ CFMutableArrayRef imaginary = NULL;
60
+
61
+ // if we don't have an existing url, spin until we get to a parent that
62
+ // does exist, saving any imaginary components for appending back later
63
+ while(!CFURLResourceIsReachable(placeholder, NULL)) {
64
+ #ifdef DEBUG
65
+ fprintf(stderr, "path does not exist\n");
66
+ #endif
67
+
68
+ CFStringRef child;
69
+
70
+ if (imaginary == NULL) {
71
+ imaginary = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
72
+ }
73
+
74
+ child = CFURLCopyLastPathComponent(placeholder);
75
+ CFArrayInsertValueAtIndex(imaginary, 0, child);
76
+ CFRelease(child);
77
+
78
+ url = CFURLCreateCopyDeletingLastPathComponent(NULL, placeholder);
79
+ CFRelease(placeholder);
80
+ placeholder = url;
81
+
82
+ #ifdef DEBUG
83
+ fprintf(stderr, "parent: ");
84
+ CFShow(placeholder);
85
+ #endif
86
+ }
87
+
88
+ #ifdef DEBUG
89
+ fprintf(stderr, "path exists\n");
90
+ #endif
91
+
92
+ // realpath() doesn't always return the correct case for a path, so this
93
+ // is a funky workaround that converts a path into a (volId/inodeId) pair
94
+ // and asks what the path should be for that. since it looks at the actual
95
+ // inode instead of returning the same case passed in like realpath()
96
+ // appears to do for HFS+, it should always be correct.
97
+ url = CFURLCreateFileReferenceURL(NULL, placeholder, NULL);
98
+ CFRelease(placeholder);
99
+ placeholder = CFURLCreateFilePathURL(NULL, url, NULL);
100
+ CFRelease(url);
101
+
102
+ #ifdef DEBUG
103
+ fprintf(stderr, "path resolved to: ");
104
+ CFShow(placeholder);
105
+ #endif
106
+
107
+ // if we stripped off any imaginary path components, append them back on
108
+ if (imaginary != NULL) {
109
+ CFIndex count = CFArrayGetCount(imaginary);
110
+ for (CFIndex i = 0; i<count; i++) {
111
+ CFStringRef component = CFArrayGetValueAtIndex(imaginary, i);
112
+ #ifdef DEBUG
113
+ fprintf(stderr, "appending component: ");
114
+ CFShow(component);
115
+ #endif
116
+ url = CFURLCreateCopyAppendingPathComponent(NULL, placeholder, component, false);
117
+ CFRelease(placeholder);
118
+ placeholder = url;
119
+ }
120
+ CFRelease(imaginary);
121
+ }
122
+
123
+ #ifdef DEBUG
124
+ fprintf(stderr, "result: ");
125
+ CFShow(placeholder);
126
+ #endif
127
+
128
+ CFStringRef cfPath = CFURLCopyFileSystemPath(placeholder, kCFURLPOSIXPathStyle);
129
+ CFArrayAppendValue(config.paths, cfPath);
130
+ CFRelease(cfPath);
131
+ CFRelease(placeholder);
132
+
133
+ #else
134
+
135
+ #ifdef DEBUG
136
+ fprintf(stderr, "compiled against 10.5, using realpath()\n");
137
+ #endif
138
+
139
+ char fullPath[PATH_MAX + 1];
140
+
141
+ if (realpath(path, fullPath) == NULL) {
142
+ #ifdef DEBUG
143
+ fprintf(stderr, " realpath not directly resolvable from path\n");
144
+ #endif
145
+
146
+ if (path[0] != '/') {
147
+ #ifdef DEBUG
148
+ fprintf(stderr, " passed path is not absolute\n");
149
+ #endif
150
+ size_t len;
151
+ getcwd(fullPath, sizeof(fullPath));
152
+ #ifdef DEBUG
153
+ fprintf(stderr, " result of getcwd: %s\n", fullPath);
154
+ #endif
155
+ len = strlen(fullPath);
156
+ fullPath[len] = '/';
157
+ strlcpy(&fullPath[len + 1], path, sizeof(fullPath) - (len + 1));
158
+ } else {
159
+ #ifdef DEBUG
160
+ fprintf(stderr, " assuming path does not YET exist\n");
161
+ #endif
162
+ strlcpy(fullPath, path, sizeof(fullPath));
163
+ }
164
+ }
165
+
166
+ #ifdef DEBUG
167
+ fprintf(stderr, " resolved path to: %s\n", fullPath);
168
+ fprintf(stderr, "\n");
169
+ #endif
170
+
171
+ CFStringRef pathRef = CFStringCreateWithCString(kCFAllocatorDefault,
172
+ fullPath,
173
+ kCFStringEncodingUTF8);
174
+ CFArrayAppendValue(config.paths, pathRef);
175
+ CFRelease(pathRef);
176
+
177
+ #endif
178
+ }
179
+
180
+ // Parse commandline settings
181
+ static inline void parse_cli_settings(int argc, const char* argv[])
182
+ {
183
+ // runtime os version detection
184
+ SInt32 osMajorVersion, osMinorVersion;
185
+ if (!(Gestalt(gestaltSystemVersionMajor, &osMajorVersion) == noErr)) {
186
+ osMajorVersion = 0;
187
+ }
188
+ if (!(Gestalt(gestaltSystemVersionMinor, &osMinorVersion) == noErr)) {
189
+ osMinorVersion = 0;
190
+ }
191
+
192
+ if ((osMajorVersion == 10) & (osMinorVersion < 5)) {
193
+ fprintf(stderr, "The FSEvents API is unavailable on this version of macos!\n");
194
+ exit(EXIT_FAILURE);
195
+ }
196
+
197
+ struct cli_info args_info;
198
+ cli_parser_init(&args_info);
199
+
200
+ if (cli_parser(argc, argv, &args_info) != 0) {
201
+ exit(EXIT_FAILURE);
202
+ }
203
+
204
+ config.paths = CFArrayCreateMutable(NULL,
205
+ (CFIndex)0,
206
+ &kCFTypeArrayCallBacks);
207
+
208
+ config.sinceWhen = args_info.since_when_arg;
209
+ config.latency = args_info.latency_arg;
210
+ config.format = args_info.format_arg;
211
+
212
+ if (args_info.no_defer_flag) {
213
+ config.flags |= kFSEventStreamCreateFlagNoDefer;
214
+ }
215
+ if (args_info.watch_root_flag) {
216
+ config.flags |= kFSEventStreamCreateFlagWatchRoot;
217
+ }
218
+
219
+ if (args_info.ignore_self_flag) {
220
+ if ((osMajorVersion == 10) & (osMinorVersion >= 6)) {
221
+ config.flags |= kFSEventStreamCreateFlagIgnoreSelf;
222
+ } else {
223
+ fprintf(stderr, "MacOSX 10.6 or later is required for --ignore-self\n");
224
+ exit(EXIT_FAILURE);
225
+ }
226
+ }
227
+
228
+ if (args_info.file_events_flag) {
229
+ if ((osMajorVersion == 10) & (osMinorVersion >= 7)) {
230
+ config.flags |= kFSEventStreamCreateFlagFileEvents;
231
+ } else {
232
+ fprintf(stderr, "MacOSX 10.7 or later required for --file-events\n");
233
+ exit(EXIT_FAILURE);
234
+ }
235
+ }
236
+
237
+ if (args_info.inputs_num == 0) {
238
+ append_path(".");
239
+ } else {
240
+ for (unsigned int i=0; i < args_info.inputs_num; ++i) {
241
+ append_path(args_info.inputs[i]);
242
+ }
243
+ }
244
+
245
+ cli_parser_free(&args_info);
246
+
247
+ #ifdef DEBUG
248
+ fprintf(stderr, "config.sinceWhen %llu\n", config.sinceWhen);
249
+ fprintf(stderr, "config.latency %f\n", config.latency);
250
+
251
+ // STFU clang
252
+ #if __LP64__
253
+ fprintf(stderr, "config.flags %#.8x\n", config.flags);
254
+ #else
255
+ fprintf(stderr, "config.flags %#.8lx\n", config.flags);
256
+ #endif
257
+
258
+ FLAG_CHECK_STDERR(config.flags, kFSEventStreamCreateFlagUseCFTypes,
259
+ " Using CF instead of C types");
260
+ FLAG_CHECK_STDERR(config.flags, kFSEventStreamCreateFlagNoDefer,
261
+ " NoDefer latency modifier enabled");
262
+ FLAG_CHECK_STDERR(config.flags, kFSEventStreamCreateFlagWatchRoot,
263
+ " WatchRoot notifications enabled");
264
+ FLAG_CHECK_STDERR(config.flags, kFSEventStreamCreateFlagIgnoreSelf,
265
+ " IgnoreSelf enabled");
266
+ FLAG_CHECK_STDERR(config.flags, kFSEventStreamCreateFlagFileEvents,
267
+ " FileEvents enabled");
268
+
269
+ fprintf(stderr, "config.paths\n");
270
+
271
+ long numpaths = CFArrayGetCount(config.paths);
272
+
273
+ for (long i = 0; i < numpaths; i++) {
274
+ char path[PATH_MAX];
275
+ CFStringGetCString(CFArrayGetValueAtIndex(config.paths, i),
276
+ path,
277
+ PATH_MAX,
278
+ kCFStringEncodingUTF8);
279
+ fprintf(stderr, " %s\n", path);
280
+ }
281
+
282
+ fprintf(stderr, "\n");
283
+ #endif
284
+ }
285
+
286
+ // original output format for rb-fsevent
287
+ static void classic_output_format(size_t numEvents,
288
+ char** paths)
289
+ {
290
+ for (size_t i = 0; i < numEvents; i++) {
291
+ fprintf(stdout, "%s:", paths[i]);
292
+ }
293
+ fprintf(stdout, "\n");
294
+ }
295
+
296
+ // output format used in the Yoshimasa Niwa branch of rb-fsevent
297
+ static void niw_output_format(size_t numEvents,
298
+ char** paths,
299
+ const FSEventStreamEventFlags eventFlags[],
300
+ const FSEventStreamEventId eventIds[])
301
+ {
302
+ for (size_t i = 0; i < numEvents; i++) {
303
+ fprintf(stdout, "%lu:%llu:%s\n",
304
+ (unsigned long)eventFlags[i],
305
+ (unsigned long long)eventIds[i],
306
+ paths[i]);
307
+ }
308
+ fprintf(stdout, "\n");
309
+ }
310
+
311
+ static void tstring_output_format(size_t numEvents,
312
+ char** paths,
313
+ const FSEventStreamEventFlags eventFlags[],
314
+ const FSEventStreamEventId eventIds[],
315
+ TSITStringFormat format)
316
+ {
317
+ CFMutableArrayRef events = CFArrayCreateMutable(kCFAllocatorDefault,
318
+ 0, &kCFTypeArrayCallBacks);
319
+
320
+ for (size_t i = 0; i < numEvents; i++) {
321
+ CFMutableDictionaryRef event = CFDictionaryCreateMutable(kCFAllocatorDefault,
322
+ 0,
323
+ &kCFTypeDictionaryKeyCallBacks,
324
+ &kCFTypeDictionaryValueCallBacks);
325
+
326
+ CFStringRef path = CFStringCreateWithBytes(kCFAllocatorDefault,
327
+ (const UInt8*)paths[i],
328
+ (CFIndex)strlen(paths[i]),
329
+ kCFStringEncodingUTF8,
330
+ false);
331
+ CFDictionarySetValue(event, CFSTR("path"), path);
332
+
333
+ CFNumberRef flags = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &eventFlags[i]);
334
+ CFDictionarySetValue(event, CFSTR("flags"), flags);
335
+
336
+ CFNumberRef ident = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &eventIds[i]);
337
+ CFDictionarySetValue(event, CFSTR("id"), ident);
338
+
339
+ CFArrayAppendValue(events, event);
340
+
341
+ CFRelease(event);
342
+ CFRelease(path);
343
+ CFRelease(flags);
344
+ CFRelease(ident);
345
+ }
346
+
347
+ CFMutableDictionaryRef meta = CFDictionaryCreateMutable(kCFAllocatorDefault,
348
+ 0,
349
+ &kCFTypeDictionaryKeyCallBacks,
350
+ &kCFTypeDictionaryValueCallBacks);
351
+ CFDictionarySetValue(meta, CFSTR("events"), events);
352
+
353
+ CFNumberRef num = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &numEvents);
354
+ CFDictionarySetValue(meta, CFSTR("numEvents"), num);
355
+
356
+ CFDataRef data = TSICTStringCreateRenderedDataFromObjectWithFormat(meta, format);
357
+ fprintf(stdout, "%s", CFDataGetBytePtr(data));
358
+
359
+ CFRelease(events);
360
+ CFRelease(num);
361
+ CFRelease(meta);
362
+ CFRelease(data);
363
+ }
364
+
365
+ static void callback(__attribute__((unused)) FSEventStreamRef streamRef,
366
+ __attribute__((unused)) void* clientCallBackInfo,
367
+ size_t numEvents,
368
+ void* eventPaths,
369
+ const FSEventStreamEventFlags eventFlags[],
370
+ const FSEventStreamEventId eventIds[])
371
+ {
372
+ char** paths = eventPaths;
373
+
374
+
375
+ #ifdef DEBUG
376
+ fprintf(stderr, "\n");
377
+ fprintf(stderr, "FSEventStreamCallback fired!\n");
378
+ fprintf(stderr, " numEvents: %lu\n", numEvents);
379
+
380
+ for (size_t i = 0; i < numEvents; i++) {
381
+ fprintf(stderr, "\n");
382
+ fprintf(stderr, " event ID: %llu\n", eventIds[i]);
383
+
384
+ // STFU clang
385
+ #if __LP64__
386
+ fprintf(stderr, " event flags: %#.8x\n", eventFlags[i]);
387
+ #else
388
+ fprintf(stderr, " event flags: %#.8lx\n", eventFlags[i]);
389
+ #endif
390
+
391
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagMustScanSubDirs,
392
+ " Recursive scanning of directory required");
393
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagUserDropped,
394
+ " Buffering problem: events dropped user-side");
395
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagKernelDropped,
396
+ " Buffering problem: events dropped kernel-side");
397
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagEventIdsWrapped,
398
+ " Event IDs have wrapped");
399
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagHistoryDone,
400
+ " All historical events have been processed");
401
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagRootChanged,
402
+ " Root path has changed");
403
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagMount,
404
+ " A new volume was mounted at this path");
405
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagUnmount,
406
+ " A volume was unmounted from this path");
407
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagItemCreated,
408
+ " Item created");
409
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagItemRemoved,
410
+ " Item removed");
411
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagItemInodeMetaMod,
412
+ " Item metadata modified");
413
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagItemRenamed,
414
+ " Item renamed");
415
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagItemModified,
416
+ " Item modified");
417
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagItemFinderInfoMod,
418
+ " Item Finder Info modified");
419
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagItemChangeOwner,
420
+ " Item changed ownership");
421
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagItemXattrMod,
422
+ " Item extended attributes modified");
423
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagItemIsFile,
424
+ " Item is a file");
425
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagItemIsDir,
426
+ " Item is a directory");
427
+ FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagItemIsSymlink,
428
+ " Item is a symbolic link");
429
+
430
+ fprintf(stderr, " event path: %s\n", paths[i]);
431
+ fprintf(stderr, "\n");
432
+ }
433
+
434
+ fprintf(stderr, "\n");
435
+ #endif
436
+
437
+ if (config.format == kFSEventWatchOutputFormatClassic) {
438
+ classic_output_format(numEvents, paths);
439
+ } else if (config.format == kFSEventWatchOutputFormatNIW) {
440
+ niw_output_format(numEvents, paths, eventFlags, eventIds);
441
+ } else if (config.format == kFSEventWatchOutputFormatTNetstring) {
442
+ tstring_output_format(numEvents, paths, eventFlags, eventIds,
443
+ kTSITStringFormatTNetstring);
444
+ } else if (config.format == kFSEventWatchOutputFormatOTNetstring) {
445
+ tstring_output_format(numEvents, paths, eventFlags, eventIds,
446
+ kTSITStringFormatOTNetstring);
447
+ }
448
+
449
+ fflush(stdout);
450
+ }
451
+
452
+ int main(int argc, const char* argv[])
453
+ {
454
+ /*
455
+ * a subprocess will initially inherit the process group of its parent. the
456
+ * process group may have a control terminal associated with it, which would
457
+ * be the first tty device opened by the group leader. typically the group
458
+ * leader is your shell and the control terminal is your login device. a
459
+ * subset of signals triggered on the control terminal are sent to all members
460
+ * of the process group, in large part to facilitate sane and consistent
461
+ * cleanup (ex: control terminal was closed).
462
+ *
463
+ * so why the overly descriptive lecture style comment?
464
+ * 1. SIGINT and SIGQUIT are among the signals with this behavior
465
+ * 2. a number of applications gank the above for their own use
466
+ * 3. ruby's insanely useful "guard" is one of these applications
467
+ * 4. despite having some level of understanding of POSIX signals and a few
468
+ * of the scenarios that might cause problems, i learned this one only
469
+ * after reading ruby 1.9's process.c
470
+ * 5. if left completely undocumented, even slightly obscure bugfixes
471
+ * may be removed as cruft by a future maintainer
472
+ *
473
+ * hindsight is 20/20 addition: if you're single-threaded and blocking on IO
474
+ * with a subprocess, then handlers for deferrable signals might not get run
475
+ * when you expect them to. In the case of Ruby 1.8, that means making use of
476
+ * IO::select, which will preserve correct signal handling behavior.
477
+ */
478
+ if (setpgid(0,0) < 0) {
479
+ fprintf(stderr, "Unable to set new process group.\n");
480
+ return 1;
481
+ }
482
+
483
+ parse_cli_settings(argc, argv);
484
+
485
+ FSEventStreamContext context = {0, NULL, NULL, NULL, NULL};
486
+ FSEventStreamRef stream;
487
+ stream = FSEventStreamCreate(kCFAllocatorDefault,
488
+ (FSEventStreamCallback)&callback,
489
+ &context,
490
+ config.paths,
491
+ config.sinceWhen,
492
+ config.latency,
493
+ config.flags);
494
+
495
+ #ifdef DEBUG
496
+ FSEventStreamShow(stream);
497
+ fprintf(stderr, "\n");
498
+ #endif
499
+
500
+ FSEventStreamScheduleWithRunLoop(stream,
501
+ CFRunLoopGetCurrent(),
502
+ kCFRunLoopDefaultMode);
503
+ FSEventStreamStart(stream);
504
+ CFRunLoopRun();
505
+ FSEventStreamFlushSync(stream);
506
+ FSEventStreamStop(stream);
507
+
508
+ return 0;
509
+ }