rev |
line source |
nuclear@22
|
1 /* file modification monitoring for windows */
|
nuclear@22
|
2 #ifdef WIN32
|
nuclear@22
|
3 #include <stdio.h>
|
nuclear@22
|
4 #include <assert.h>
|
nuclear@23
|
5 #include <malloc.h>
|
nuclear@22
|
6 #include "filewatch.h"
|
nuclear@22
|
7 #include "resman.h"
|
nuclear@22
|
8 #include "resman_impl.h"
|
nuclear@23
|
9 #include "dynarr.h"
|
nuclear@23
|
10
|
nuclear@23
|
11 struct watch_dir {
|
nuclear@23
|
12 HANDLE handle;
|
nuclear@23
|
13 int nref;
|
nuclear@23
|
14 };
|
nuclear@22
|
15
|
nuclear@22
|
16 static void reload_modified(struct rbnode *node, void *cls);
|
nuclear@22
|
17
|
nuclear@22
|
18 int resman_init_file_monitor(struct resman *rman)
|
nuclear@22
|
19 {
|
nuclear@23
|
20 if(!(rman->watch_handles = dynarr_alloc(0, sizeof *rman->watch_handles))) {
|
nuclear@22
|
21 return -1;
|
nuclear@22
|
22 }
|
nuclear@22
|
23
|
nuclear@23
|
24 /* create the handle->resource map */
|
nuclear@23
|
25 rman->nresmap = rb_create(RB_KEY_ADDR);
|
nuclear@23
|
26 /* create the watched dirs set */
|
nuclear@23
|
27 rman->watchdirs = rb_create(RB_KEY_STRING);
|
nuclear@22
|
28 return 0;
|
nuclear@22
|
29 }
|
nuclear@22
|
30
|
nuclear@22
|
31 void resman_destroy_file_monitor(struct resman *rman)
|
nuclear@22
|
32 {
|
nuclear@23
|
33 dynarr_free(rman->watch_handles);
|
nuclear@23
|
34
|
nuclear@22
|
35 rb_free(rman->nresmap);
|
nuclear@23
|
36 rb_free(rman->watchdirs);
|
nuclear@22
|
37 }
|
nuclear@22
|
38
|
nuclear@22
|
39 int resman_start_watch(struct resman *rman, struct resource *res)
|
nuclear@22
|
40 {
|
nuclear@23
|
41 char *path;
|
nuclear@23
|
42 HANDLE handle;
|
nuclear@23
|
43 struct watch_dir *wdir;
|
nuclear@22
|
44
|
nuclear@23
|
45 /* construct an absolute path for the directory containing this file */
|
nuclear@23
|
46 path = res->name;
|
nuclear@23
|
47 if(path[0] != '/' && path[1] != ':') { /* not an absolute path */
|
nuclear@23
|
48 char *src, *dest, *lastslash;
|
nuclear@23
|
49 int cwdsz = GetCurrentDirectory(0, 0);
|
nuclear@23
|
50 int pathsz = strlen(path) + cwdsz + 1;
|
nuclear@23
|
51
|
nuclear@23
|
52 path = malloc(pathsz + 1);
|
nuclear@23
|
53 GetCurrentDirectory(pathsz, path);
|
nuclear@23
|
54
|
nuclear@23
|
55 /* now copy the rest of the path, until the last slash, while converting path separators */
|
nuclear@23
|
56 src = res->name;
|
nuclear@23
|
57 dest = path + strlen(path);
|
nuclear@23
|
58
|
nuclear@23
|
59 lastslash = dest;
|
nuclear@23
|
60 *dest++ = '\\';
|
nuclear@23
|
61 while(*src) {
|
nuclear@23
|
62 if(src[-1] == '\\') {
|
nuclear@23
|
63 /* skip any /./ parts of the path */
|
nuclear@23
|
64 if(src[0] == '.' && (src[1] == '/' || src[1] == '\\')) {
|
nuclear@23
|
65 src += 2;
|
nuclear@23
|
66 continue;
|
nuclear@23
|
67 }
|
nuclear@23
|
68 /* normalize any /../ parts of the path */
|
nuclear@23
|
69 if(src[0] == '.' && src[1] == '.' && (src[2] == '/' || src[2] == '\\')) {
|
nuclear@23
|
70 src += 3;
|
nuclear@23
|
71 dest = strrchr(src - 2, '\\');
|
nuclear@23
|
72 assert(dest);
|
nuclear@23
|
73 dest++;
|
nuclear@23
|
74 continue;
|
nuclear@23
|
75 }
|
nuclear@23
|
76 }
|
nuclear@23
|
77
|
nuclear@23
|
78 if(*src == '/' || *src == '\\') {
|
nuclear@23
|
79 lastslash = dest;
|
nuclear@23
|
80 *dest++ = '\\';
|
nuclear@23
|
81 src++;
|
nuclear@23
|
82 } else {
|
nuclear@23
|
83 *dest++ = *src++;
|
nuclear@23
|
84 }
|
nuclear@23
|
85 }
|
nuclear@23
|
86
|
nuclear@23
|
87 *lastslash = 0;
|
nuclear@22
|
88 }
|
nuclear@22
|
89
|
nuclear@23
|
90 /* check to see if we already have a watch handle for this directory */
|
nuclear@23
|
91 if((wdir = rb_find(rman->watchdirs, path))) {
|
nuclear@23
|
92 handle = wdir->handle;
|
nuclear@23
|
93 wdir->nref++;
|
nuclear@23
|
94 } else {
|
nuclear@23
|
95 if(!(wdir = malloc(sizeof *wdir))) {
|
nuclear@23
|
96 perror("failed to allocate watchdir");
|
nuclear@23
|
97 free(path);
|
nuclear@23
|
98 return -1;
|
nuclear@23
|
99 }
|
nuclear@23
|
100
|
nuclear@23
|
101 /* otherwise start a new notification */
|
nuclear@23
|
102 if((handle = FindFirstChangeNotification(path, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE)) == INVALID_HANDLE_VALUE) {
|
nuclear@23
|
103 unsigned int err = GetLastError();
|
nuclear@23
|
104 fprintf(stderr, "failed to watch %s for modification (error: %u)\n", path, err);
|
nuclear@23
|
105 free(wdir);
|
nuclear@23
|
106 free(path);
|
nuclear@23
|
107 return -1;
|
nuclear@23
|
108 }
|
nuclear@23
|
109 wdir->handle = handle;
|
nuclear@23
|
110 wdir->nref = 1;
|
nuclear@23
|
111
|
nuclear@23
|
112 rb_insert(rman->watchdirs, path, wdir);
|
nuclear@23
|
113 dynarr_push(rman->watch_handles, &handle);
|
nuclear@23
|
114 }
|
nuclear@23
|
115
|
nuclear@23
|
116 rb_insert(rman->nresmap, handle, res);
|
nuclear@23
|
117 res->nhandle = handle;
|
nuclear@23
|
118 res->watch_path = path;
|
nuclear@22
|
119 return 0;
|
nuclear@22
|
120 }
|
nuclear@22
|
121
|
nuclear@22
|
122 void resman_stop_watch(struct resman *rman, struct resource *res)
|
nuclear@22
|
123 {
|
nuclear@23
|
124 int i, sz;
|
nuclear@23
|
125
|
nuclear@23
|
126 if(res->nhandle) {
|
nuclear@23
|
127 struct watch_dir *wdir = rb_find(rman->watchdirs, res->watch_path);
|
nuclear@23
|
128 if(wdir) {
|
nuclear@23
|
129 if(--wdir->nref <= 0) {
|
nuclear@23
|
130 FindCloseChangeNotification(res->nhandle);
|
nuclear@23
|
131
|
nuclear@23
|
132 /* find the handle in the watch_handles array and remove it */
|
nuclear@23
|
133 sz = dynarr_size(rman->watch_handles);
|
nuclear@23
|
134 for(i=0; i<sz; i++) {
|
nuclear@23
|
135 if(rman->watch_handles[i] == res->nhandle) {
|
nuclear@23
|
136 /* swap the end for it and pop */
|
nuclear@23
|
137 rman->watch_handles[i] = rman->watch_handles[sz - 1];
|
nuclear@23
|
138 rman->watch_handles[sz - 1] = 0;
|
nuclear@23
|
139 dynarr_pop(rman->watch_handles);
|
nuclear@23
|
140 break;
|
nuclear@23
|
141 }
|
nuclear@23
|
142 }
|
nuclear@23
|
143 }
|
nuclear@23
|
144 free(wdir);
|
nuclear@23
|
145 }
|
nuclear@23
|
146
|
nuclear@23
|
147 rb_delete(rman->nresmap, res->nhandle);
|
nuclear@23
|
148 res->nhandle = 0;
|
nuclear@22
|
149 }
|
nuclear@22
|
150 }
|
nuclear@22
|
151
|
nuclear@22
|
152 void resman_check_watch(struct resman *rman)
|
nuclear@22
|
153 {
|
nuclear@23
|
154 unsigned int num_handles = dynarr_size(rman->watch_handles);
|
nuclear@23
|
155 for(;;) {
|
nuclear@23
|
156 struct resource *res;
|
nuclear@23
|
157 unsigned int idx = WaitForMultipleObjects(num_handles, rman->watch_handles, FALSE, 0);
|
nuclear@23
|
158 if(idx < WAIT_OBJECT_0 || idx >= WAIT_OBJECT_0 + num_handles) {
|
nuclear@23
|
159 break;
|
nuclear@23
|
160 }
|
nuclear@22
|
161
|
nuclear@23
|
162 if(!(res = rb_find(rman->nresmap, rman->watch_handles[idx]))) {
|
nuclear@23
|
163 fprintf(stderr, "got modification event from unknown resource!\n");
|
nuclear@23
|
164 continue;
|
nuclear@23
|
165 }
|
nuclear@22
|
166
|
nuclear@23
|
167 printf("file \"%s\" modified\n", res->name);
|
nuclear@23
|
168 tpool_add_work(rman->tpool, res);
|
nuclear@22
|
169 }
|
nuclear@22
|
170 }
|
nuclear@22
|
171
|
nuclear@22
|
172 #else
|
nuclear@22
|
173 int resman_filewatch_win32_silence_empty_file_warning;
|
nuclear@22
|
174 #endif /* WIN32 */
|