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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6ec2a04c1e09fcc2b15f66a283c02222b102fef8
4
- data.tar.gz: 1a63e413a9d9cbf3fb71558322652d4d6ecc2f18
2
+ SHA256:
3
+ metadata.gz: 8fa142a3dd92401ead21804759f41c08f1098b51e825e1568a16c831d116ab65
4
+ data.tar.gz: 6fdee72304339d8bab6b2407a1e5c2c281ad185435f7c11d34279225c9fb3406
5
5
  SHA512:
6
- metadata.gz: b9d7321a62fbd19b4b25464a19b5ca23adbd79f7e1adcb015b70512c58814d782969654c99dd6e8863a496e81a8a63b2c950f1fe93409591fc6904d190b5ad6e
7
- data.tar.gz: 449890feca668e4827d2c679caf4b46a1ae11173b4a73064da0c09f8b267006adadd06029bea97a5ef85983d1641724209518e1179a27681d0330daff576229d
6
+ metadata.gz: c3e43118e4d444d59d0ad47ff692c7f90477bee895a56f0b26ba98cb5da1bf8ac7d21a4e382bf8605ec1ef166b3f549cf11b3d08bebbfa436400b2da909a1168
7
+ data.tar.gz: ea47ee332c8640d39ada051f9d06867e2c09ee1daff6915160615dd177be82f8d068954f236da62d2899d04ce355a045f162fdd3e07fb1fb26523cff1ad062af
data/Gemfile CHANGED
@@ -1,6 +1,3 @@
1
1
  source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
-
5
- gem 'rake'
6
-
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 2.5, JRuby
11
- * Tested on 10.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. This corruption is not currently fixed by their tools, though Apple has been made aware of the issue and are working on it (as of may 2015). 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 always return the same value as the kernel reporting (thus fixing fsevents).
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.2.2 2.3.0-dev rbx-2.5.5 jruby-1.7.9]
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.2.2 2.3.0-dev rbx-2.5.5 jruby-1.7.9]
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) {
@@ -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 = kFSEventWatchOutputFormatClassic;
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
-
@@ -1,10 +1,13 @@
1
1
  #include "compat.h"
2
2
 
3
- #if MAC_OS_X_VERSION_MAX_ALLOWED < 1060
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 < 1070
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 < 1090
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
@@ -9,20 +9,57 @@
9
9
  */
10
10
 
11
11
 
12
- #ifndef fsevent_watch_compat_h
13
- #define fsevent_watch_compat_h
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
- #if MAC_OS_X_VERSION_MAX_ALLOWED < 1060
20
- // ignoring events originating from the current process introduced in 10.6
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 < 1070
25
- // file-level events introduced in 10.7
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 < 1090
41
- // marking, rather than ignoring, events originating from the current process introduced in 10.9
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 // fsevent_watch_compat_h
100
+ #endif // listen_fsevents_compat_h
@@ -22,6 +22,8 @@
22
22
  #define TARGET_CPU "i386"
23
23
  #elif defined(__x86_64__)
24
24
  #define TARGET_CPU "x86_64"
25
+ #elif defined(__arm64__)
26
+ #define TARGET_CPU "arm64"
25
27
  #else
26
28
  #define TARGET_CPU "unknown"
27
29
  #endif
@@ -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
- kFSEventWatchOutputFormatClassic
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.4'
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-2015 Travis Tilley']
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
 
@@ -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
@@ -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
- if line = @pipe.readline
44
- modified_dir_paths = line.split(':').select { |dir| dir != "\n" }
45
- callback.call(modified_dir_paths)
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]
@@ -1,5 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
3
  class FSEvent
4
- VERSION = '0.9.7'
4
+ VERSION = '0.11.0'
5
5
  end
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 'bundler', '~> 1.0'
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.9.7
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: 2015-12-29 00:00:00.000000000 Z
12
+ date: 2021-05-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: bundler
15
+ name: rspec
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
18
  - - "~>"
19
19
  - !ruby/object:Gem::Version
20
- version: '1.0'
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: '1.0'
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.11'
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.11'
41
+ version: '4.2'
42
42
  - !ruby/object:Gem::Dependency
43
- name: guard-rspec
43
+ name: rake
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - "~>"
47
47
  - !ruby/object:Gem::Version
48
- version: '4.2'
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: '4.2'
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
- post_install_message:
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
- rubyforge_project:
108
- rubygems_version: 2.5.1
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: []