libresman

annotate 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
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 */