#ifndef EL_PLATFORM_WIN_H #define EL_PLATFORM_WIN_H /* * el_platform_win.h — Windows OS-boundary shim for el_runtime.c. * * Branch: feat/windows-el-runtime. Included ONLY when _WIN32 is defined; the POSIX build is * untouched. Goal: let el_runtime.c (a BSD-sockets / dlfcn / fork host) compile and link with * mingw-w64 into a native neuron.exe, with no behavioural change to the Linux/macOS build. * * What it maps: * - sockets : winsock2 (same call names: socket/bind/listen/accept/recv/send/setsockopt). * Sockets close with closesocket() (see el_closesocket), and the stack must be * started once with WSAStartup — done automatically via a load-time constructor. * - dlsym : el_runtime.c uses dlsym(RTLD_DEFAULT, name) to resolve callback/tool symbols * exported by the main module. Windows equivalent: GetProcAddress on the process * module. Link the soul with -Wl,--export-all-symbols so the symbols are findable. * - popen : mapped to _popen/_pclose. * - threads : UNCHANGED. mingw-w64 ships winpthreads, so + -lpthread just work. */ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #include #include #include /* Portable headers mingw-w64 provides (verified present). */ #include #include #include #include #include #include /* strcasecmp */ #include #include #include #include /* mingw-w64 provides gettimeofday here */ #include #include #include #include #include #include /* ── socket close ─────────────────────────────────────────────────────────── */ /* Winsock closes sockets with closesocket(), not close() (close() is for file fds). The POSIX build defines the same helper as close() so the call sites are identical across platforms. */ static inline int el_closesocket(SOCKET s) { return closesocket(s); } /* ── setsockopt optval type ───────────────────────────────────────────────── */ /* Winsock's setsockopt takes optval as (const char*); POSIX takes (const void*), so el_runtime.c passes &int directly. GCC 14+ makes that an error under -Wincompatible-pointer-types. Wrap it so the runtime's POSIX-style call sites compile unchanged (defined before the macro so the wrapper itself resolves to the real winsock setsockopt). */ static inline int el_setsockopt(SOCKET s, int level, int optname, const void* optval, int optlen) { return setsockopt(s, level, optname, (const char*)optval, optlen); } #define setsockopt(s, l, o, v, n) el_setsockopt((s), (l), (o), (v), (int)(n)) /* ── winsock init (once, at load) ─────────────────────────────────────────── */ static void el__win_net_init(void) { static int inited = 0; if (!inited) { WSADATA w; WSAStartup(MAKEWORD(2, 2), &w); inited = 1; } } __attribute__((constructor)) static void el__win_ctor(void) { el__win_net_init(); } /* ── dlsym → GetProcAddress ───────────────────────────────────────────────── */ #ifndef RTLD_DEFAULT #define RTLD_DEFAULT ((void*)0) #endif static inline void* el_win_dlsym(void* handle, const char* name) { (void)handle; return (void*)(uintptr_t)GetProcAddress(GetModuleHandleA(NULL), name); } #define dlsym(h, n) el_win_dlsym((h), (n)) /* ── popen / pclose ───────────────────────────────────────────────────────── */ #define popen _popen #define pclose _pclose /* ── misc POSIX → Win32 shims ─────────────────────────────────────────────── */ #include /* _mkdir */ #define mkdir(path, mode) _mkdir(path) /* POSIX mkdir(path,mode) → _mkdir(path) */ #define timegm _mkgmtime /* UTC tm → time_t */ #define fsync(fd) _commit(fd) /* no fsync() on Windows; _commit() () is the equiv */ /* setenv/unsetenv: not in the Windows CRT; map to _putenv_s / SetEnvironmentVariable. */ static inline int setenv(const char* name, const char* value, int overwrite) { (void)overwrite; return _putenv_s(name, value ? value : ""); } static inline int unsetenv(const char* name) { /* _putenv_s(name, "") sets VAR="" rather than removing it. * SetEnvironmentVariableA(name, NULL) truly deletes it from the Win32 * env block; then we sync the CRT cache with _putenv("NAME="). */ SetEnvironmentVariableA(name, NULL); size_t len = strlen(name); char *buf = (char*)malloc(len + 2); if (!buf) return -1; memcpy(buf, name, len); buf[len] = '='; buf[len + 1] = '\0'; _putenv(buf); free(buf); return 0; } /* nanosleep — not available in MSVC/UCRT; approximate with Sleep(). */ static inline int el_nanosleep(const struct timespec *req, struct timespec *rem) { (void)rem; DWORD ms = (DWORD)((req->tv_sec * 1000ULL) + (req->tv_nsec / 1000000ULL)); Sleep(ms ? ms : 1); return 0; } #define nanosleep(req, rem) el_nanosleep((req), (rem)) /* localtime_r/gmtime_r: Windows offers localtime_s/gmtime_s with reversed arg order. */ static inline struct tm* localtime_r(const time_t* t, struct tm* out) { return localtime_s(out, t) == 0 ? out : (struct tm*)0; } static inline struct tm* gmtime_r(const time_t* t, struct tm* out) { return gmtime_s(out, t) == 0 ? out : (struct tm*)0; } /* ── libcurl: degradable stubs for the curl-less Windows build ─────────────── */ /* The curl-less validation build (WITH_CURL=0) links no libcurl. el_runtime.c uses libcurl * unconditionally for its HTTP client / LLM layer; these stubs let it compile and link so the * runtime, HTTP *server*, graph and memory work natively on Windows. Live outbound HTTP/LLM calls * degrade to a runtime error (curl_easy_perform returns an error) — matching the documented * curl-less contract. When HAVE_CURL is defined (WITH_CURL=1) the real is used and * this whole block is compiled out. POSIX never sees this header, so the POSIX build is untouched. */ #ifndef HAVE_CURL typedef void CURL; typedef int CURLcode; #define CURLE_OK 0 #define CURLE_HTTP_RETURNED_ERROR 22 #define CURL_ERROR_SIZE 256 /* Option ids: values are irrelevant to the no-op setopt below; kept distinct for readability. */ #define CURLOPT_URL 10002 #define CURLOPT_WRITEFUNCTION 20011 #define CURLOPT_WRITEDATA 10001 #define CURLOPT_POSTFIELDS 10015 #define CURLOPT_POSTFIELDSIZE 120 #define CURLOPT_POST 47 #define CURLOPT_HTTPHEADER 10023 #define CURLOPT_TIMEOUT_MS 155 #define CURLOPT_NOSIGNAL 99 #define CURLOPT_USERAGENT 10018 #define CURLOPT_FOLLOWLOCATION 52 #define CURLOPT_ERRORBUFFER 10010 #define CURLOPT_CUSTOMREQUEST 10036 #define CURLOPT_FAILONERROR 45 struct curl_slist { char* data; struct curl_slist* next; }; static inline struct curl_slist* curl_slist_append(struct curl_slist* list, const char* s) { struct curl_slist* node = (struct curl_slist*)malloc(sizeof(struct curl_slist)); if (!node) return list; node->data = s ? strdup(s) : NULL; node->next = NULL; if (!list) return node; struct curl_slist* p = list; while (p->next) p = p->next; p->next = node; return list; } static inline void curl_slist_free_all(struct curl_slist* list) { while (list) { struct curl_slist* n = list->next; free(list->data); free(list); list = n; } } static inline CURL* curl_easy_init(void) { return (CURL*)malloc(1); } static inline CURLcode curl_easy_setopt(CURL* h, int opt, ...) { (void)h; (void)opt; return CURLE_OK; } static inline CURLcode curl_easy_perform(CURL* h) { (void)h; return 7 /* CURLE_COULDNT_CONNECT */; } static inline void curl_easy_cleanup(CURL* h) { free(h); } static inline const char* curl_easy_strerror(CURLcode c) { (void)c; return "libcurl not built in (curl-less build)"; } #endif /* !HAVE_CURL */ #endif /* EL_PLATFORM_WIN_H */