rb-fsevent 0.9.7 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Gemfile +0 -3
- data/README.md +23 -5
- data/Rakefile +2 -1
- data/bin/fsevent_watch +0 -0
- data/ext/fsevent_watch/TSICTString.c +0 -21
- data/ext/fsevent_watch/cli.c +1 -2
- data/ext/fsevent_watch/compat.c +19 -3
- data/ext/fsevent_watch/compat.h +62 -9
- data/ext/fsevent_watch/defines.h +2 -0
- data/ext/fsevent_watch/main.c +47 -6
- data/ext/fsevent_watch/signal_handlers.c +66 -0
- data/ext/fsevent_watch/signal_handlers.h +16 -0
- data/ext/rakefile.rb +10 -3
- data/lib/otnetstring.rb +85 -0
- data/lib/rb-fsevent/fsevent.rb +38 -4
- data/lib/rb-fsevent/version.rb +1 -1
- data/rb-fsevent.gemspec +6 -2
- metadata +20 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8fa142a3dd92401ead21804759f41c08f1098b51e825e1568a16c831d116ab65
|
4
|
+
data.tar.gz: 6fdee72304339d8bab6b2407a1e5c2c281ad185435f7c11d34279225c9fb3406
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3e43118e4d444d59d0ad47ff692c7f90477bee895a56f0b26ba98cb5da1bf8ac7d21a4e382bf8605ec1ef166b3f549cf11b3d08bebbfa436400b2da909a1168
|
7
|
+
data.tar.gz: ea47ee332c8640d39ada051f9d06867e2c09ee1daff6915160615dd177be82f8d068954f236da62d2899d04ce355a045f162fdd3e07fb1fb26523cff1ad062af
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -5,17 +5,18 @@
|
|
5
5
|
|
6
6
|
Very simple & usable Mac OSX FSEvents API
|
7
7
|
|
8
|
-
* RubyCocoa not required!
|
9
8
|
* Signals are working (really)
|
10
|
-
* Tested on MRI 2.1, RBX
|
11
|
-
* Tested on 10.
|
9
|
+
* Tested on MRI 2.4.1, RBX 3.72, JRuby 1.7.26 and 9.1.8.0
|
10
|
+
* Tested on 10.8
|
12
11
|
|
13
12
|
## HFS+ filename corruption bug
|
14
13
|
|
15
|
-
There is a _very_ long-standing (since 2011) OSX bug where sometimes the filename metadata for HFS+ filesystems will get corrupted, resulting in some APIs returning one case for a file, and other APIs returning another.
|
14
|
+
There is a _very_ long-standing (since 2011) OSX bug where sometimes the filename metadata for HFS+ filesystems will get corrupted, resulting in some APIs returning one case for a file, and other APIs returning another. The result is that sometimes, _for no visible reason to the user_, fsevents would simply not work. As of rb-fsevent 0.9.5 this issue is properly detected and an insanely hacky (but effective) workaround is used that replaces the system `realpath()` with a custom implementation that should almost always return the same value as the kernel reporting (thus fixing fsevents). The major flaw in the workaround is that it may return the wrong path for hard links.
|
16
15
|
|
17
16
|
Please note that this doesn't repair the underlying issue on disk. Other apps and libraries using fsevents will continue to break with no warning. There may be other issues unrelated to fsevents.
|
18
17
|
|
18
|
+
__This bug is resolved in MacOS 10.12 and all users are strongly encouraged to upgrade.__
|
19
|
+
|
19
20
|
## Install
|
20
21
|
|
21
22
|
gem install rb-fsevent
|
@@ -131,6 +132,23 @@ end
|
|
131
132
|
fsevent.run
|
132
133
|
```
|
133
134
|
|
135
|
+
### Using _full_ event information
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
require 'rb-fsevent'
|
139
|
+
fsevent = FSEvent.new
|
140
|
+
fsevent.watch Dir.pwd do |paths, event_meta|
|
141
|
+
event_meta['events'].each do |event|
|
142
|
+
puts "event ID: #{event['id']}"
|
143
|
+
puts "path: #{event['path']}"
|
144
|
+
puts "c flags: #{event['cflags']}"
|
145
|
+
puts "named flags: #{event['flags'].join(', ')}"
|
146
|
+
# named flags will include strings such as `ItemInodeMetaMod` or `OwnEvent`
|
147
|
+
end
|
148
|
+
end
|
149
|
+
fsevent.run
|
150
|
+
```
|
151
|
+
|
134
152
|
## Options
|
135
153
|
|
136
154
|
When defining options using a hash or hash-like object, it gets checked for validity and converted to the appropriate fsevent\_watch commandline arguments array when the FSEvent class is instantiated. This is obviously the safest and preferred method of passing in options.
|
@@ -233,7 +251,7 @@ Pull requests are quite welcome! Please ensure that your commits are in a topic
|
|
233
251
|
|
234
252
|
The list of tested targets is currently:
|
235
253
|
|
236
|
-
%w[2.
|
254
|
+
%w[2.4.1 rbx-3.72 jruby-1.7.26 jruby-9.1.8.0]
|
237
255
|
|
238
256
|
## Authors
|
239
257
|
|
data/Rakefile
CHANGED
@@ -9,7 +9,7 @@ task :default => :spec
|
|
9
9
|
namespace(:spec) do
|
10
10
|
desc "Run all specs on multiple ruby versions"
|
11
11
|
task(:portability) do
|
12
|
-
versions = %w[2.
|
12
|
+
versions = %w[2.4.1 rbx-3.72 jruby-1.7.26 jruby-9.1.8.0]
|
13
13
|
versions.each do |version|
|
14
14
|
# system <<-BASH
|
15
15
|
# bash -c 'source ~/.rvm/scripts/rvm;
|
@@ -24,6 +24,7 @@ namespace(:spec) do
|
|
24
24
|
[[ ! -a $HOME/.rbenv/versions/#{version} ]] && rbenv install #{version};
|
25
25
|
rbenv shell #{version};
|
26
26
|
rbenv which bundle 2> /dev/null || gem install bundler;
|
27
|
+
rm Gemfile.lock;
|
27
28
|
bundle install;
|
28
29
|
rake spec;'
|
29
30
|
BASH
|
data/bin/fsevent_watch
CHANGED
Binary file
|
@@ -113,27 +113,6 @@ static inline CFStringRef TSICTStringCreateStringFromIntermediateRepresentation(
|
|
113
113
|
return string;
|
114
114
|
}
|
115
115
|
|
116
|
-
static inline CFDataRef TSICTStringCreateDataWithDataOfTypeAndFormat(CFDataRef data, TSITStringTag type, TSITStringFormat format)
|
117
|
-
{
|
118
|
-
CFRetain(data);
|
119
|
-
|
120
|
-
if (format == kTSITStringFormatDefault) {
|
121
|
-
format = TSICTStringGetDefaultFormat();
|
122
|
-
}
|
123
|
-
|
124
|
-
TStringIRep* rep = TSICTStringCreateWithDataOfTypeAndFormat(data, type, format);
|
125
|
-
if (rep == NULL) {
|
126
|
-
return NULL;
|
127
|
-
}
|
128
|
-
|
129
|
-
CFDataRef result = TSICTStringCreateDataFromIntermediateRepresentation(rep);
|
130
|
-
|
131
|
-
TSICTStringDestroy(rep);
|
132
|
-
CFRelease(data);
|
133
|
-
|
134
|
-
return result;
|
135
|
-
}
|
136
|
-
|
137
116
|
static inline void TSICTStringAppendObjectToMutableDataWithFormat(CFTypeRef object, CFMutableDataRef buffer, TSITStringFormat format)
|
138
117
|
{
|
139
118
|
if (object == NULL) {
|
data/ext/fsevent_watch/cli.c
CHANGED
@@ -27,7 +27,7 @@ static void default_args (struct cli_info* args_info)
|
|
27
27
|
args_info->ignore_self_flag = false;
|
28
28
|
args_info->file_events_flag = false;
|
29
29
|
args_info->mark_self_flag = false;
|
30
|
-
args_info->format_arg =
|
30
|
+
args_info->format_arg = kFSEventWatchOutputFormatOTNetstring;
|
31
31
|
}
|
32
32
|
|
33
33
|
static void cli_parser_release (struct cli_info* args_info)
|
@@ -199,4 +199,3 @@ int cli_parser (int argc, const char** argv, struct cli_info* args_info)
|
|
199
199
|
|
200
200
|
return EXIT_SUCCESS;
|
201
201
|
}
|
202
|
-
|
data/ext/fsevent_watch/compat.c
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
#include "compat.h"
|
2
2
|
|
3
|
-
|
3
|
+
|
4
|
+
#if (defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_6) || \
|
5
|
+
(defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_6_0)
|
4
6
|
FSEventStreamCreateFlags kFSEventStreamCreateFlagIgnoreSelf = 0x00000008;
|
5
7
|
#endif
|
6
8
|
|
7
|
-
#if MAC_OS_X_VERSION_MAX_ALLOWED <
|
9
|
+
#if (defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_7) || \
|
10
|
+
(defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_6_0)
|
8
11
|
FSEventStreamCreateFlags kFSEventStreamCreateFlagFileEvents = 0x00000010;
|
9
12
|
FSEventStreamEventFlags kFSEventStreamEventFlagItemCreated = 0x00000100;
|
10
13
|
FSEventStreamEventFlags kFSEventStreamEventFlagItemRemoved = 0x00000200;
|
@@ -19,7 +22,20 @@ FSEventStreamEventFlags kFSEventStreamEventFlagItemIsDir = 0x00020000
|
|
19
22
|
FSEventStreamEventFlags kFSEventStreamEventFlagItemIsSymlink = 0x00040000;
|
20
23
|
#endif
|
21
24
|
|
22
|
-
#if MAC_OS_X_VERSION_MAX_ALLOWED <
|
25
|
+
#if (defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_9) || \
|
26
|
+
(defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_7_0)
|
23
27
|
FSEventStreamCreateFlags kFSEventStreamCreateFlagMarkSelf = 0x00000020;
|
24
28
|
FSEventStreamEventFlags kFSEventStreamEventFlagOwnEvent = 0x00080000;
|
25
29
|
#endif
|
30
|
+
|
31
|
+
#if (defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_10) || \
|
32
|
+
(defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0)
|
33
|
+
FSEventStreamEventFlags kFSEventStreamEventFlagItemIsHardlink = 0x00100000;
|
34
|
+
FSEventStreamEventFlags kFSEventStreamEventFlagItemIsLastHardlink = 0x00200000;
|
35
|
+
#endif
|
36
|
+
|
37
|
+
#if (defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_13) || \
|
38
|
+
(defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_11_0)
|
39
|
+
FSEventStreamCreateFlags kFSEventStreamCreateFlagUseExtendedData = 0x00000040;
|
40
|
+
FSEventStreamEventFlags kFSEventStreamEventFlagItemCloned = 0x00400000;
|
41
|
+
#endif
|
data/ext/fsevent_watch/compat.h
CHANGED
@@ -9,20 +9,57 @@
|
|
9
9
|
*/
|
10
10
|
|
11
11
|
|
12
|
-
#ifndef
|
13
|
-
#define
|
12
|
+
#ifndef listen_fsevents_compat_h
|
13
|
+
#define listen_fsevents_compat_h
|
14
14
|
|
15
15
|
#ifndef __CORESERVICES__
|
16
16
|
#include <CoreServices/CoreServices.h>
|
17
17
|
#endif // __CORESERVICES__
|
18
18
|
|
19
|
-
#
|
20
|
-
|
19
|
+
#ifndef __AVAILABILITY__
|
20
|
+
#include <Availability.h>
|
21
|
+
#endif // __AVAILABILITY__
|
22
|
+
|
23
|
+
#ifndef __MAC_10_6
|
24
|
+
#define __MAC_10_6 1060
|
25
|
+
#endif
|
26
|
+
#ifndef __MAC_10_7
|
27
|
+
#define __MAC_10_7 1070
|
28
|
+
#endif
|
29
|
+
#ifndef __MAC_10_9
|
30
|
+
#define __MAC_10_9 1090
|
31
|
+
#endif
|
32
|
+
#ifndef __MAC_10_10
|
33
|
+
#define __MAC_10_10 101000
|
34
|
+
#endif
|
35
|
+
#ifndef __MAC_10_13
|
36
|
+
#define __MAC_10_13 101300
|
37
|
+
#endif
|
38
|
+
#ifndef __IPHONE_6_0
|
39
|
+
#define __IPHONE_6_0 60000
|
40
|
+
#endif
|
41
|
+
#ifndef __IPHONE_7_0
|
42
|
+
#define __IPHONE_7_0 70000
|
43
|
+
#endif
|
44
|
+
#ifndef __IPHONE_9_0
|
45
|
+
#define __IPHONE_9_0 90000
|
46
|
+
#endif
|
47
|
+
#ifndef __IPHONE_11_0
|
48
|
+
#define __IPHONE_11_0 110000
|
49
|
+
#endif
|
50
|
+
|
51
|
+
#ifdef __cplusplus
|
52
|
+
extern "C" {
|
53
|
+
#endif
|
54
|
+
|
55
|
+
|
56
|
+
#if (defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_6) || \
|
57
|
+
(defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_6_0)
|
21
58
|
extern FSEventStreamCreateFlags kFSEventStreamCreateFlagIgnoreSelf;
|
22
59
|
#endif
|
23
60
|
|
24
|
-
#if MAC_OS_X_VERSION_MAX_ALLOWED <
|
25
|
-
|
61
|
+
#if (defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_7) || \
|
62
|
+
(defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_6_0)
|
26
63
|
extern FSEventStreamCreateFlags kFSEventStreamCreateFlagFileEvents;
|
27
64
|
extern FSEventStreamEventFlags kFSEventStreamEventFlagItemCreated,
|
28
65
|
kFSEventStreamEventFlagItemRemoved,
|
@@ -37,11 +74,27 @@ extern FSEventStreamEventFlags kFSEventStreamEventFlagItemCreated,
|
|
37
74
|
kFSEventStreamEventFlagItemIsSymlink;
|
38
75
|
#endif
|
39
76
|
|
40
|
-
#if MAC_OS_X_VERSION_MAX_ALLOWED <
|
41
|
-
|
77
|
+
#if (defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_9) || \
|
78
|
+
(defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_7_0)
|
42
79
|
extern FSEventStreamCreateFlags kFSEventStreamCreateFlagMarkSelf;
|
43
80
|
extern FSEventStreamEventFlags kFSEventStreamEventFlagOwnEvent;
|
44
81
|
#endif
|
45
82
|
|
83
|
+
#if (defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_10) || \
|
84
|
+
(defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0)
|
85
|
+
extern FSEventStreamEventFlags kFSEventStreamEventFlagItemIsHardlink,
|
86
|
+
kFSEventStreamEventFlagItemIsLastHardlink;
|
87
|
+
#endif
|
88
|
+
|
89
|
+
#if (defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_13) || \
|
90
|
+
(defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_11_0)
|
91
|
+
extern FSEventStreamCreateFlags kFSEventStreamCreateFlagUseExtendedData;
|
92
|
+
extern FSEventStreamEventFlags kFSEventStreamEventFlagItemCloned;
|
93
|
+
#endif
|
94
|
+
|
95
|
+
|
96
|
+
#ifdef __cplusplus
|
97
|
+
}
|
98
|
+
#endif
|
46
99
|
|
47
|
-
#endif //
|
100
|
+
#endif // listen_fsevents_compat_h
|
data/ext/fsevent_watch/defines.h
CHANGED
data/ext/fsevent_watch/main.c
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#include "common.h"
|
2
|
+
#include "signal_handlers.h"
|
2
3
|
#include "cli.h"
|
3
4
|
#include "FSEventsFix.h"
|
4
5
|
|
@@ -23,7 +24,7 @@ static struct {
|
|
23
24
|
(double) 0.3,
|
24
25
|
(CFOptionFlags) kFSEventStreamCreateFlagNone,
|
25
26
|
NULL,
|
26
|
-
|
27
|
+
kFSEventWatchOutputFormatOTNetstring
|
27
28
|
};
|
28
29
|
|
29
30
|
// Prototypes
|
@@ -349,18 +350,54 @@ static void tstring_output_format(size_t numEvents,
|
|
349
350
|
false);
|
350
351
|
CFDictionarySetValue(event, CFSTR("path"), path);
|
351
352
|
|
352
|
-
CFNumberRef flags = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &eventFlags[i]);
|
353
|
-
CFDictionarySetValue(event, CFSTR("flags"), flags);
|
354
|
-
|
355
353
|
CFNumberRef ident = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &eventIds[i]);
|
356
354
|
CFDictionarySetValue(event, CFSTR("id"), ident);
|
357
355
|
|
356
|
+
CFNumberRef cflags = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &eventFlags[i]);
|
357
|
+
CFDictionarySetValue(event, CFSTR("cflags"), cflags);
|
358
|
+
|
359
|
+
CFMutableArrayRef flags = CFArrayCreateMutable(kCFAllocatorDefault,
|
360
|
+
0, &kCFTypeArrayCallBacks);
|
361
|
+
|
362
|
+
#define FLAG_ADD_NAME(flagsnum, flagnum, flagname, flagarray) \
|
363
|
+
do { \
|
364
|
+
if (FLAG_CHECK(flagsnum, flagnum)) { \
|
365
|
+
CFArrayAppendValue(flagarray, CFSTR(flagname)); } } \
|
366
|
+
while(0)
|
367
|
+
|
368
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagMustScanSubDirs, "MustScanSubDirs", flags);
|
369
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagUserDropped, "UserDropped", flags);
|
370
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagKernelDropped, "KernelDropped", flags);
|
371
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagEventIdsWrapped, "EventIdsWrapped", flags);
|
372
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagHistoryDone, "HistoryDone", flags);
|
373
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagRootChanged, "RootChanged", flags);
|
374
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagMount, "Mount", flags);
|
375
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagUnmount, "Unmount", flags);
|
376
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemCreated, "ItemCreated", flags);
|
377
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemRemoved, "ItemRemoved", flags);
|
378
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemInodeMetaMod, "ItemInodeMetaMod", flags);
|
379
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemRenamed, "ItemRenamed", flags);
|
380
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemModified, "ItemModified", flags);
|
381
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemFinderInfoMod, "ItemFinderInfoMod", flags);
|
382
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemChangeOwner, "ItemChangeOwner", flags);
|
383
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemXattrMod, "ItemXattrMod", flags);
|
384
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemIsFile, "ItemIsFile", flags);
|
385
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemIsDir, "ItemIsDir", flags);
|
386
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemIsSymlink, "ItemIsSymlink", flags);
|
387
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagOwnEvent, "OwnEvent", flags);
|
388
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemIsHardlink, "ItemIsHardLink", flags);
|
389
|
+
FLAG_ADD_NAME(eventFlags[i], kFSEventStreamEventFlagItemIsLastHardlink, "ItemIsLastHardLink", flags);
|
390
|
+
|
391
|
+
CFDictionarySetValue(event, CFSTR("flags"), flags);
|
392
|
+
|
393
|
+
|
358
394
|
CFArrayAppendValue(events, event);
|
359
395
|
|
360
396
|
CFRelease(event);
|
361
397
|
CFRelease(path);
|
362
|
-
CFRelease(flags);
|
363
398
|
CFRelease(ident);
|
399
|
+
CFRelease(cflags);
|
400
|
+
CFRelease(flags);
|
364
401
|
}
|
365
402
|
|
366
403
|
CFMutableDictionaryRef meta = CFDictionaryCreateMutable(kCFAllocatorDefault,
|
@@ -445,7 +482,10 @@ static void callback(__attribute__((unused)) FSEventStreamRef streamRef,
|
|
445
482
|
" Item is a directory");
|
446
483
|
FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagItemIsSymlink,
|
447
484
|
" Item is a symbolic link");
|
448
|
-
|
485
|
+
FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagItemIsHardlink,
|
486
|
+
" Item is a hard link");
|
487
|
+
FLAG_CHECK_STDERR(eventFlags[i], kFSEventStreamEventFlagItemIsLastHardlink,
|
488
|
+
" Item is the last hard link");
|
449
489
|
fprintf(stderr, " event path: %s\n", paths[i]);
|
450
490
|
fprintf(stderr, "\n");
|
451
491
|
}
|
@@ -470,6 +510,7 @@ static void callback(__attribute__((unused)) FSEventStreamRef streamRef,
|
|
470
510
|
|
471
511
|
int main(int argc, const char* argv[])
|
472
512
|
{
|
513
|
+
install_signal_handlers();
|
473
514
|
parse_cli_settings(argc, argv);
|
474
515
|
|
475
516
|
if (needs_fsevents_fix) {
|
@@ -0,0 +1,66 @@
|
|
1
|
+
#include "signal_handlers.h"
|
2
|
+
#include <fcntl.h>
|
3
|
+
#include <signal.h>
|
4
|
+
#include <stdio.h>
|
5
|
+
#include <stdlib.h>
|
6
|
+
#include <sys/time.h>
|
7
|
+
#include <unistd.h>
|
8
|
+
|
9
|
+
|
10
|
+
#define PPID_ALARM_INTERVAL 2 // send SIGALRM every this seconds
|
11
|
+
|
12
|
+
|
13
|
+
static pid_t orig_ppid;
|
14
|
+
|
15
|
+
|
16
|
+
static void signal_handler(int _) {
|
17
|
+
exit(EXIT_FAILURE);
|
18
|
+
}
|
19
|
+
|
20
|
+
static void check_ppid(void) {
|
21
|
+
if (getppid() != orig_ppid) {
|
22
|
+
exit(EXIT_FAILURE);
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
static void check_stdout_open(void) {
|
27
|
+
if (fcntl(STDOUT_FILENO, F_GETFD) < 0) {
|
28
|
+
exit(EXIT_FAILURE);
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
static void alarm_handler(int _) {
|
33
|
+
check_ppid();
|
34
|
+
check_stdout_open();
|
35
|
+
alarm(PPID_ALARM_INTERVAL);
|
36
|
+
signal(SIGALRM, alarm_handler);
|
37
|
+
}
|
38
|
+
|
39
|
+
static void die(const char *msg) {
|
40
|
+
fprintf(stderr, "\nFATAL: %s\n", msg);
|
41
|
+
abort();
|
42
|
+
}
|
43
|
+
|
44
|
+
static void install_signal_handler(int sig, void (*handler)(int)) {
|
45
|
+
if (signal(sig, handler) == SIG_ERR) {
|
46
|
+
die("Could not install signal handler");
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
void install_signal_handlers(void) {
|
51
|
+
// check pipe is still connected
|
52
|
+
check_stdout_open();
|
53
|
+
|
54
|
+
// watch getppid() every PPID_ALARM_INTERVAL seconds
|
55
|
+
orig_ppid = getppid();
|
56
|
+
if (orig_ppid <= 1) {
|
57
|
+
die("prematurely zombied");
|
58
|
+
}
|
59
|
+
install_signal_handler(SIGALRM, alarm_handler);
|
60
|
+
alarm(PPID_ALARM_INTERVAL);
|
61
|
+
|
62
|
+
// be sure to exit on SIGHUP, SIGPIPE
|
63
|
+
install_signal_handler(SIGHUP, signal_handler);
|
64
|
+
install_signal_handler(SIGPIPE, signal_handler);
|
65
|
+
}
|
66
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
/**
|
2
|
+
* @headerfile signal_handlers.h
|
3
|
+
* Signal handlers to stop the zombie hordes
|
4
|
+
*
|
5
|
+
* Catch and handle signals better so that we die faster like a good meat puppet.
|
6
|
+
*/
|
7
|
+
|
8
|
+
|
9
|
+
#ifndef fsevent_watch_signal_handlers_h
|
10
|
+
#define fsevent_watch_signal_handlers_h
|
11
|
+
|
12
|
+
|
13
|
+
void install_signal_handlers(void);
|
14
|
+
|
15
|
+
|
16
|
+
#endif // fsevent_watch_signal_handlers_h
|
data/ext/rakefile.rb
CHANGED
@@ -8,7 +8,7 @@ require 'rake/clean'
|
|
8
8
|
raise "unable to find xcodebuild" unless system('which', 'xcodebuild')
|
9
9
|
|
10
10
|
|
11
|
-
FSEVENT_WATCH_EXE_VERSION = '0.1.
|
11
|
+
FSEVENT_WATCH_EXE_VERSION = '0.1.5'
|
12
12
|
|
13
13
|
$this_dir = Pathname.new(__FILE__).dirname.expand_path
|
14
14
|
$final_exe = $this_dir.parent.join('bin/fsevent_watch')
|
@@ -99,6 +99,11 @@ task :x86 do
|
|
99
99
|
$ARCHFLAGS = '-arch i386'
|
100
100
|
end
|
101
101
|
|
102
|
+
desc 'set build arch to arm64'
|
103
|
+
task :arm64 do
|
104
|
+
$ARCHFLAGS = '-arch arm64'
|
105
|
+
end
|
106
|
+
|
102
107
|
task :setup_env => [:set_build_type, :sw_vers, :get_sdk_info]
|
103
108
|
|
104
109
|
directory $obj_dir.to_s
|
@@ -148,7 +153,7 @@ file $obj_dir.join('Info.plist').to_s => [$obj_dir.to_s, :setup_env] do
|
|
148
153
|
key['CFBundleDisplayName']
|
149
154
|
string['FSEvent Watch CLI']
|
150
155
|
key['NSHumanReadableCopyright']
|
151
|
-
string['Copyright (C) 2011-
|
156
|
+
string['Copyright (C) 2011-2017 Travis Tilley']
|
152
157
|
|
153
158
|
key['CFBundleVersion']
|
154
159
|
string["#{FSEVENT_WATCH_EXE_VERSION}"]
|
@@ -216,8 +221,10 @@ task :codesign => :build do
|
|
216
221
|
sh "codesign -s '#{$CODE_SIGN_IDENTITY}' #{$obj_dir.join('fsevent_watch')}"
|
217
222
|
end
|
218
223
|
|
224
|
+
directory $this_dir.parent.join('bin')
|
225
|
+
|
219
226
|
desc 'replace bundled fsevent_watch binary with build/fsevent_watch'
|
220
|
-
task :replace_exe => :build do
|
227
|
+
task :replace_exe => [$this_dir.parent.join('bin'), :build] do
|
221
228
|
sh "mv #{$obj_dir.join('fsevent_watch')} #{$final_exe}"
|
222
229
|
end
|
223
230
|
|
data/lib/otnetstring.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# Copyright (c) 2011 Konstantin Haase
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
|
22
|
+
require 'stringio'
|
23
|
+
|
24
|
+
module OTNetstring
|
25
|
+
class Error < StandardError; end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
def parse(io, encoding = 'internal', fallback_encoding = nil)
|
29
|
+
fallback_encoding = io.encoding if io.respond_to? :encoding
|
30
|
+
io = StringIO.new(io) if io.respond_to? :to_str
|
31
|
+
length, byte = "", nil
|
32
|
+
|
33
|
+
while byte.nil? || byte =~ /\d/
|
34
|
+
length << byte if byte
|
35
|
+
byte = io.read(1)
|
36
|
+
end
|
37
|
+
|
38
|
+
if length.size > 9
|
39
|
+
raise Error, "#{length} is longer than 9 digits"
|
40
|
+
elsif length !~ /\d+/
|
41
|
+
raise Error, "Expected '#{byte}' to be a digit"
|
42
|
+
end
|
43
|
+
length = Integer(length)
|
44
|
+
|
45
|
+
case byte
|
46
|
+
when '#' then Integer io.read(length)
|
47
|
+
when ',' then with_encoding io.read(length), encoding, fallback_encoding
|
48
|
+
when '~' then
|
49
|
+
raise Error, "nil has length of 0, #{length} given" unless length == 0
|
50
|
+
when '!' then io.read(length) == 'true'
|
51
|
+
when '[', '{'
|
52
|
+
array = []
|
53
|
+
start = io.pos
|
54
|
+
array << parse(io, encoding, fallback_encoding) while io.pos - start < length
|
55
|
+
raise Error, 'Nested element longer than container' if io.pos - start != length
|
56
|
+
byte == "{" ? Hash[*array] : array
|
57
|
+
else
|
58
|
+
raise Error, "Unknown type '#{byte}'"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def encode(obj, string_sep = ',')
|
63
|
+
case obj
|
64
|
+
when String then with_encoding "#{obj.bytesize}#{string_sep}#{obj}", "binary"
|
65
|
+
when Integer then encode(obj.inspect, '#')
|
66
|
+
when NilClass then "0~"
|
67
|
+
when Array then encode(obj.map { |e| encode(e) }.join, '[')
|
68
|
+
when Hash then encode(obj.map { |a,b| encode(a)+encode(b) }.join, '{')
|
69
|
+
when FalseClass, TrueClass then encode(obj.inspect, '!')
|
70
|
+
else raise Error, 'cannot encode %p' % obj
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def with_encoding(str, encoding, fallback = nil)
|
77
|
+
return str unless str.respond_to? :encode
|
78
|
+
encoding = Encoding.find encoding if encoding.respond_to? :to_str
|
79
|
+
encoding ||= fallback
|
80
|
+
encoding ? str.encode(encoding) : str
|
81
|
+
rescue EncodingError
|
82
|
+
str.force_encoding(encoding)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/rb-fsevent/fsevent.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
|
3
|
+
require 'otnetstring'
|
4
|
+
|
3
5
|
class FSEvent
|
4
6
|
class << self
|
5
7
|
class_eval <<-END
|
@@ -40,9 +42,41 @@ class FSEvent
|
|
40
42
|
# please note the use of IO::select() here, as it is used specifically to
|
41
43
|
# preserve correct signal handling behavior in ruby 1.8.
|
42
44
|
while @running && IO::select([@pipe], nil, nil, nil)
|
43
|
-
|
44
|
-
|
45
|
-
|
45
|
+
# managing the IO ourselves allows us to be careful and never pass an
|
46
|
+
# incomplete message to OTNetstring.parse()
|
47
|
+
message = ""
|
48
|
+
length = ""
|
49
|
+
byte = nil
|
50
|
+
|
51
|
+
reading_length = true
|
52
|
+
found_length = false
|
53
|
+
|
54
|
+
while reading_length
|
55
|
+
byte = @pipe.read_nonblock(1)
|
56
|
+
if "#{byte}" =~ /\d/
|
57
|
+
length << byte
|
58
|
+
found_length = true
|
59
|
+
elsif found_length == false
|
60
|
+
next
|
61
|
+
else
|
62
|
+
reading_length = false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
length = Integer(length, 10)
|
66
|
+
type = byte
|
67
|
+
|
68
|
+
message << "#{length}#{type}"
|
69
|
+
message << @pipe.read(length)
|
70
|
+
|
71
|
+
decoded = OTNetstring.parse(message)
|
72
|
+
modified_paths = decoded["events"].map {|event| event["path"]}
|
73
|
+
# passing the full info as a second block param feels icky, but such is
|
74
|
+
# the trap of backward compatibility.
|
75
|
+
case callback.arity
|
76
|
+
when 1
|
77
|
+
callback.call(modified_paths)
|
78
|
+
when 2
|
79
|
+
callback.call(modified_paths, decoded)
|
46
80
|
end
|
47
81
|
end
|
48
82
|
rescue Interrupt, IOError, Errno::EBADF
|
@@ -110,7 +144,7 @@ class FSEvent
|
|
110
144
|
private
|
111
145
|
|
112
146
|
def parse_options(options={})
|
113
|
-
opts = []
|
147
|
+
opts = ['--format=otnetstring']
|
114
148
|
opts.concat(['--since-when', options[:since_when]]) if options[:since_when]
|
115
149
|
opts.concat(['--latency', options[:latency]]) if options[:latency]
|
116
150
|
opts.push('--no-defer') if options[:no_defer]
|
data/lib/rb-fsevent/version.rb
CHANGED
data/rb-fsevent.gemspec
CHANGED
@@ -13,10 +13,14 @@ Gem::Specification.new do |s|
|
|
13
13
|
s.description = 'FSEvents API with Signals catching (without RubyCocoa)'
|
14
14
|
s.license = 'MIT'
|
15
15
|
|
16
|
+
s.metadata = {
|
17
|
+
'source_code_uri' => 'https://github.com/thibaudgg/rb-fsevent'
|
18
|
+
}
|
19
|
+
|
16
20
|
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^spec/}) }
|
17
21
|
s.require_path = 'lib'
|
18
22
|
|
19
|
-
s.add_development_dependency '
|
20
|
-
s.add_development_dependency 'rspec', '~> 2.11'
|
23
|
+
s.add_development_dependency 'rspec', '~> 3.6'
|
21
24
|
s.add_development_dependency 'guard-rspec', '~> 4.2'
|
25
|
+
s.add_development_dependency 'rake', '~> 12.0'
|
22
26
|
end
|
metadata
CHANGED
@@ -1,58 +1,58 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rb-fsevent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thibaud Guillaume-Gentil
|
8
8
|
- Travis Tilley
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2021-05-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
15
|
+
name: rspec
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
18
|
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '
|
20
|
+
version: '3.6'
|
21
21
|
type: :development
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: '
|
27
|
+
version: '3.6'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
|
-
name: rspec
|
29
|
+
name: guard-rspec
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
32
|
- - "~>"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '2
|
34
|
+
version: '4.2'
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: '2
|
41
|
+
version: '4.2'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
|
-
name:
|
43
|
+
name: rake
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
46
|
- - "~>"
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: '
|
48
|
+
version: '12.0'
|
49
49
|
type: :development
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
53
|
- - "~>"
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: '
|
55
|
+
version: '12.0'
|
56
56
|
description: FSEvents API with Signals catching (without RubyCocoa)
|
57
57
|
email:
|
58
58
|
- thibaud@thibaud.gg
|
@@ -80,7 +80,10 @@ files:
|
|
80
80
|
- ext/fsevent_watch/compat.h
|
81
81
|
- ext/fsevent_watch/defines.h
|
82
82
|
- ext/fsevent_watch/main.c
|
83
|
+
- ext/fsevent_watch/signal_handlers.c
|
84
|
+
- ext/fsevent_watch/signal_handlers.h
|
83
85
|
- ext/rakefile.rb
|
86
|
+
- lib/otnetstring.rb
|
84
87
|
- lib/rb-fsevent.rb
|
85
88
|
- lib/rb-fsevent/fsevent.rb
|
86
89
|
- lib/rb-fsevent/version.rb
|
@@ -88,8 +91,9 @@ files:
|
|
88
91
|
homepage: http://rubygems.org/gems/rb-fsevent
|
89
92
|
licenses:
|
90
93
|
- MIT
|
91
|
-
metadata:
|
92
|
-
|
94
|
+
metadata:
|
95
|
+
source_code_uri: https://github.com/thibaudgg/rb-fsevent
|
96
|
+
post_install_message:
|
93
97
|
rdoc_options: []
|
94
98
|
require_paths:
|
95
99
|
- lib
|
@@ -104,9 +108,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
104
108
|
- !ruby/object:Gem::Version
|
105
109
|
version: '0'
|
106
110
|
requirements: []
|
107
|
-
|
108
|
-
|
109
|
-
signing_key:
|
111
|
+
rubygems_version: 3.2.11
|
112
|
+
signing_key:
|
110
113
|
specification_version: 4
|
111
114
|
summary: Very simple & usable FSEvents API
|
112
115
|
test_files: []
|