libresman

view src/filewatch_win32.c @ 23:f8e5a1491275

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