From eec93f78e54ab2ea6eaa29abbc2f2fcfb3c22a62 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Fri, 5 Dec 2025 15:37:22 -0600 Subject: [PATCH] initial add --- .cell/cell.toml | 2 + dmon.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 .cell/cell.toml create mode 100644 dmon.c diff --git a/.cell/cell.toml b/.cell/cell.toml new file mode 100644 index 0000000..e3bc8e7 --- /dev/null +++ b/.cell/cell.toml @@ -0,0 +1,2 @@ +[compilation.macOS] +LDFLAGS = "-framework CoreFoundation -framework CoreServices" diff --git a/dmon.c b/dmon.c new file mode 100644 index 0000000..3b6e7db --- /dev/null +++ b/dmon.c @@ -0,0 +1,132 @@ +#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, 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(js, 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; +)