#include "cell.h" #define DMON_IMPL #include "dmon.h" // Define the file event structure and completion queue typedef struct { dmon_action action; char rootdir[256]; char filepath[256]; char oldfilepath[256]; } FileEvent; typedef struct EventNode { FileEvent event; struct EventNode *next; } EventNode; typedef struct { EventNode *head; EventNode *tail; } CompletionQueue; CompletionQueue completionQueue = { NULL, NULL }; // Helper functions for the completion queue void enqueue_event(FileEvent event) { EventNode *node = malloc(sizeof(EventNode)); node->event = event; node->next = NULL; if (completionQueue.tail) { completionQueue.tail->next = node; } else { completionQueue.head = node; } completionQueue.tail = node; } int dequeue_event(FileEvent *event) { if (!completionQueue.head) { return 0; // No event } EventNode *node = completionQueue.head; *event = node->event; completionQueue.head = node->next; if (!completionQueue.head) { completionQueue.tail = NULL; } free(node); return 1; } void watch_cb(dmon_watch_id id, dmon_action action, const char *rootdir, const char *filepath, const char *oldfilepath, void *user) { FileEvent event; event.action = action; strncpy(event.rootdir, rootdir, sizeof(event.rootdir) - 1); strncpy(event.filepath, filepath, sizeof(event.filepath) - 1); if (oldfilepath) { strncpy(event.oldfilepath, oldfilepath, sizeof(event.oldfilepath) - 1); } else { event.oldfilepath[0] = '\0'; } enqueue_event(event); // Add event to completion queue } static dmon_watch_id watched = {0}; JSValue js_dmon_watch(JSContext *js, JSValue self, int argc, JSValue *argv) { if (watched.id) return JS_ThrowReferenceError(js, "Already watching a directory."); watched = dmon_watch(".", watch_cb, DMON_WATCHFLAGS_RECURSIVE | DMON_WATCHFLAGS_FOLLOW_SYMLINKS, NULL); return JS_NULL; } JSValue js_dmon_unwatch(JSContext *js, JSValue self, int argc, JSValue *argv) { if (!watched.id) return JS_ThrowReferenceError(js, "Not watching a directory."); dmon_unwatch(watched); watched.id = 0; return JS_NULL; } JSValue js_dmon_poll(JSContext *js, JSValueConst this_val, int argc, JSValueConst *argv) { FileEvent event; while (dequeue_event(&event)) { if (!JS_IsFunction(argv[0])) continue; JSValue jsevent = JS_NewObject(js); JSValue action; switch(event.action) { case DMON_ACTION_CREATE: action = JS_NewAtomString(js, "create"); break; case DMON_ACTION_DELETE: action = JS_NewAtomString(js, "delete"); break; case DMON_ACTION_MODIFY: action = JS_NewAtomString(js, "modify"); break; case DMON_ACTION_MOVE: action = JS_NewAtomString(js, "move"); break; } JS_SetPropertyStr(js, jsevent, "action", action); JS_SetPropertyStr(js, jsevent, "root", JS_NewString(js, event.rootdir)); JS_SetPropertyStr(js, jsevent, "file", JS_NewString(js, event.filepath)); JS_SetPropertyStr(js, jsevent, "old", JS_NewString(js, event.oldfilepath)); JS_Call(js, argv[0], JS_NULL, 1, &jsevent); JS_FreeValue(js, jsevent); } return JS_NULL; } static const JSCFunctionListEntry js_dmon_funcs[] = { JS_CFUNC_DEF("watch", 0, js_dmon_watch), JS_CFUNC_DEF("unwatch", 0, js_dmon_unwatch), JS_CFUNC_DEF("poll", 1, js_dmon_poll) }; CELL_USE_INIT( JSValue export = JS_NewObject(js); JS_SetPropertyFunctionList(js, export, js_dmon_funcs, sizeof(js_dmon_funcs)/sizeof(JSCFunctionListEntry)); dmon_init(); return export; )