forked from emcrisostomo/fswatch
-
Notifications
You must be signed in to change notification settings - Fork 6
/
fswatch.c
150 lines (136 loc) · 3.98 KB
/
fswatch.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <CoreServices/CoreServices.h>
/* fswatch.c
*
* usage: ./fswatch /some/directory[:/some/otherdirectory:...] [-r]
*
* r flag indicates that the returned paths should be relative paths. NOTE that
* the paths are relative to the cwd of fswatch itself, NOT relative to the
* path(s) given as argument. Note also that the path is not anywhere close to
* being an optimal relative path. If we produce any paths higher up in the tree
* than the cwd, they will be rendered as a full path because all we do is
* directly substitute the cwd as './' (should not be a big deal as the usual
* use case is just '.' for the dir to watch anyway)
*
* It is not clear how this would work for shortening multiple paths... maybe it
* will produce output that looks like a/.., b/.. etc.
*
* compile me with something like: gcc fswatch.c -framework CoreServices -o fswatch
*
* adapted from the FSEvents api PDF
*/
static inline int count_chars(const char* string, char ch)
{
int count = 0;
for(; *string; count += (*string++ == ch));
return count;
}
// write out some info when there's any change in watched files
void callback(
ConstFSEventStreamRef streamRef,
void *clientCallBackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[])
{
pid_t pid;
int status;
for (int i=0; i<numEvents; ++i) {
const char* path = ((char **)eventPaths)[i];
int extra = count_chars(path, ' ');
if (extra) { // produce escaped spaces in the paths
char * z = malloc(strlen(path)+1+extra);
int cur = 0, zcur = 0;
while (path[cur]) {
if (path[cur] == ' ')
z[zcur++] = '\\';
z[zcur++] = path[cur++];
}
printf("%x %s, ", eventFlags[i], z);
} else {
printf("%x %s, ", eventFlags[i], path);
}
}
printf("\n");
fflush(stdout);
}
char *cwd;
int cwdlen;
// making code wet eliminates conditional check inside callback
void callback_rel(
ConstFSEventStreamRef streamRef,
void *clientCallBackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[])
{
pid_t pid;
int status;
for (int i=0; i<numEvents; ++i) {
char* path = ((char **)eventPaths)[i];
// grab just the part that comes after the path
char* dot = "";
char* rel_abs = path;
if (!strncmp(path, cwd, cwdlen)) {
dot = ".";
rel_abs = path + cwdlen;
}
int extra = count_chars(rel_abs, ' ');
if (extra) { // produce escaped spaces in the paths
char * z = malloc(strlen(rel_abs)+1+extra);
int cur = 0, zcur = 0;
while (rel_abs[cur]) {
if (rel_abs[cur] == ' ')
z[zcur++] = '\\';
z[zcur++] = rel_abs[cur++];
}
printf("%x %s%s, ", eventFlags[i], dot, z);
} else {
printf("%x %s%s, ", eventFlags[i], dot, rel_abs);
}
}
printf("\n");
fflush(stdout);
}
//set up fsevents and callback
int main(int argc, char **argv) {
if ((cwd = getcwd(NULL, 0))) {
fprintf(stderr, "Current working dir: %s\n", cwd);
cwdlen = strlen(cwd);
}
else {
perror("getcwd() error");
return 2;
}
if(argc != 2 && argc != 3) {
fprintf(stderr, "You must specify a directory to watch, you only gave %d args\n", argc);
return 1;
}
int relative = 0;
if (argc == 3 && strncmp(argv[2], "-r", 2) == 0) {
fprintf(stderr, "You have specified relative path mode\n");
relative = 1;
}
CFStringRef mypath = CFStringCreateWithCString(NULL, argv[1], kCFStringEncodingUTF8);
CFArrayRef pathsToWatch = CFStringCreateArrayBySeparatingStrings (NULL, mypath, CFSTR(":"));
void *callbackInfo = NULL;
FSEventStreamRef stream;
CFAbsoluteTime latency = 1.0;
stream = FSEventStreamCreate(NULL,
relative ? &callback_rel : &callback,
callbackInfo,
pathsToWatch,
kFSEventStreamEventIdSinceNow,
latency,
kFSEventStreamCreateFlagFileEvents
);
FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
FSEventStreamStart(stream);
CFRunLoopRun();
}