Merge origin/dev into engram-tiered-storage
El SDK CI - dev / build-and-test (pull_request) Failing after 10m51s
El SDK CI - dev / build-and-test (pull_request) Failing after 10m51s
Resolve 3 conflicts: - lang/el-compiler/runtime/el_runtime.c: keep deletion (deprecated runtime fork; single-source-of-truth is lang/runtime/, enforced by scripts/check-single-runtime.sh). - lang/releases/v1.0.0-20260501/el_runtime.h: keep deletion (releases/ is a generated artifact folder, not a source path; a release is a git tag, not a folder). - lang/runtime/el_platform_win.h: union of dev's Windows port (#80: setsockopt optval wrapper + curl-less libcurl stubs) and our fsync(->_commit) shim needed by engram_store WAL. Nothing in dev's build consumes the deprecated fork or releases/ folder.
This commit is contained in:
@@ -2727,6 +2727,7 @@ fn builtin_arity(name: String) -> Int {
|
||||
if str_eq(name, "engram_store_checkpoint") { return 0 }
|
||||
if str_eq(name, "engram_store_close") { return 0 }
|
||||
if str_eq(name, "engram_get_node_json") { return 1 }
|
||||
if str_eq(name, "engram_get_node_by_label") { return 1 }
|
||||
if str_eq(name, "engram_search_json") { return 2 }
|
||||
if str_eq(name, "engram_scan_nodes_json") { return 2 }
|
||||
if str_eq(name, "engram_neighbors_json") { return 3 }
|
||||
|
||||
@@ -50,6 +50,16 @@
|
||||
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;
|
||||
@@ -75,6 +85,7 @@ static inline void* el_win_dlsym(void* handle, const char* name) {
|
||||
#include <direct.h> /* _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() (<io.h>) 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) {
|
||||
@@ -114,4 +125,63 @@ 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 <curl/curl.h> 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 */
|
||||
|
||||
+114
-22
@@ -21,6 +21,10 @@
|
||||
|
||||
#include "el_runtime.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
/* Windows OS-boundary shim (winsock/dlsym/popen). Threading stays on <pthread.h> (winpthreads). */
|
||||
#include "el_platform_win.h"
|
||||
#else
|
||||
#include <stdarg.h>
|
||||
#include <strings.h> /* strcasecmp */
|
||||
#include <stdint.h>
|
||||
@@ -42,7 +46,16 @@
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
/* On POSIX, sockets close with the same close() as files; el_platform_win.h supplies the Windows
|
||||
variant. Defined here so the socket call sites are identical across platforms. */
|
||||
static inline int el_closesocket(int s) { return close(s); }
|
||||
#endif
|
||||
/* libcurl: present on POSIX and on the WITH_CURL Windows build; absent on the curl-less Windows
|
||||
validation build, where el_platform_win.h supplies degradable stubs. On POSIX (_WIN32 undefined)
|
||||
this is always taken, so the POSIX build is unchanged. */
|
||||
#if !defined(_WIN32) || defined(HAVE_CURL)
|
||||
#include <curl/curl.h>
|
||||
#endif
|
||||
|
||||
/* ── Internal allocators ─────────────────────────────────────────────────── */
|
||||
|
||||
@@ -71,8 +84,14 @@ static _Thread_local ElArena _tl_arena = {NULL, 0, 0};
|
||||
static _Thread_local int _tl_arena_active = 0;
|
||||
|
||||
/* Binary-safe fs_read length — set by fs_read, consumed by http_send_response.
|
||||
* Allows serving PNGs and other binary files without strlen truncation. */
|
||||
static _Thread_local size_t _tl_fs_read_len = 0;
|
||||
* Allows serving PNGs and other binary files without strlen truncation.
|
||||
* PAIRED with the buffer pointer it describes: the length may only be applied
|
||||
* to the exact buffer fs_read returned. Without the pairing, any handler that
|
||||
* fs_read a file and then WRAPPED it into a larger response had that response
|
||||
* truncated to the file's length (Content-Length lied AND the send stopped
|
||||
* short) — the safety-contact onboarding trap, 2026-07-17. */
|
||||
static _Thread_local size_t _tl_fs_read_len = 0;
|
||||
static _Thread_local const char* _tl_fs_read_buf = NULL;
|
||||
|
||||
static void el_arena_track(char* p) {
|
||||
if (!_tl_arena_active || !p) return;
|
||||
@@ -90,6 +109,8 @@ static void el_arena_track(char* p) {
|
||||
void el_request_start(void) {
|
||||
_tl_arena.count = 0;
|
||||
_tl_arena_active = 1;
|
||||
_tl_fs_read_len = 0; /* never let a previous request's file length */
|
||||
_tl_fs_read_buf = NULL; /* leak into this response's byte accounting */
|
||||
}
|
||||
|
||||
/* Called by http_worker after the El handler returns and the response is sent.
|
||||
@@ -1430,11 +1451,14 @@ static void http_send_response(int fd, const char* body) {
|
||||
}
|
||||
|
||||
const char* eff_body = is_envelope ? env_body : body;
|
||||
/* Use the real byte count from fs_read if available (handles binary files
|
||||
* with embedded null bytes — PNG, WOFF2, etc.). Fall back to strlen for
|
||||
* normal text/JSON responses where _tl_fs_read_len is 0. */
|
||||
size_t blen = (_tl_fs_read_len > 0) ? _tl_fs_read_len : strlen(eff_body);
|
||||
/* Use the real byte count from fs_read ONLY when this body IS the exact
|
||||
* buffer fs_read returned (binary files with embedded null bytes — PNG,
|
||||
* WOFF2, etc.). Any other body — wrapped, enveloped, or derived — must be
|
||||
* measured with strlen, or it is truncated/over-read to the file's size. */
|
||||
size_t blen = (_tl_fs_read_len > 0 && eff_body == _tl_fs_read_buf)
|
||||
? _tl_fs_read_len : strlen(eff_body);
|
||||
_tl_fs_read_len = 0; /* consume — one-shot per response */
|
||||
_tl_fs_read_buf = NULL;
|
||||
int head_only = _tl_http_head_only;
|
||||
|
||||
JsonBuf hdrs; jb_init(&hdrs);
|
||||
@@ -1484,12 +1508,20 @@ static void http_send_response(int fd, const char* body) {
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
#ifdef _WIN32
|
||||
SOCKET fd;
|
||||
#else
|
||||
int fd;
|
||||
#endif
|
||||
} HttpWorkerArg;
|
||||
|
||||
static void* http_worker(void* arg) {
|
||||
HttpWorkerArg* a = (HttpWorkerArg*)arg;
|
||||
#ifdef _WIN32
|
||||
SOCKET fd = a->fd;
|
||||
#else
|
||||
int fd = a->fd;
|
||||
#endif
|
||||
free(a);
|
||||
char *method = NULL, *path = NULL, *body = NULL;
|
||||
if (http_read_request(fd, &method, &path, &body, NULL) == 0) {
|
||||
@@ -1506,11 +1538,22 @@ static void* http_worker(void* arg) {
|
||||
const char* rs = EL_CSTR(r);
|
||||
/* Copy response out BEFORE arena teardown.
|
||||
* For binary files, _tl_fs_read_len holds the real byte count —
|
||||
* use memcpy instead of strdup so null bytes are preserved. */
|
||||
size_t rlen = _tl_fs_read_len > 0 ? _tl_fs_read_len : (rs ? strlen(rs) : 0);
|
||||
* use memcpy instead of strdup so null bytes are preserved.
|
||||
* The stored length applies ONLY when the response IS the exact
|
||||
* fs_read buffer; a wrapped/derived response must use strlen or
|
||||
* it gets truncated (or over-read) to the file's length. */
|
||||
size_t rlen;
|
||||
if (_tl_fs_read_len > 0 && rs && rs == _tl_fs_read_buf) {
|
||||
rlen = _tl_fs_read_len; /* raw file bytes — binary-safe */
|
||||
} else {
|
||||
rlen = rs ? strlen(rs) : 0;
|
||||
_tl_fs_read_len = 0; /* hint doesn't describe this body */
|
||||
_tl_fs_read_buf = NULL;
|
||||
}
|
||||
response = malloc(rlen + 1);
|
||||
if (response && rs) { memcpy(response, rs, rlen); response[rlen] = '\0'; }
|
||||
else if (response) { response[0] = '\0'; }
|
||||
if (_tl_fs_read_len > 0) _tl_fs_read_buf = response; /* hint follows the copy */
|
||||
} else {
|
||||
response = el_strdup_persist("el-runtime: no http handler registered");
|
||||
}
|
||||
@@ -1521,7 +1564,7 @@ static void* http_worker(void* arg) {
|
||||
free(response);
|
||||
}
|
||||
free(method); free(path); free(body);
|
||||
close(fd);
|
||||
el_closesocket(fd);
|
||||
/* release a slot */
|
||||
pthread_mutex_lock(&_http_conn_mu);
|
||||
_http_conn_active--;
|
||||
@@ -1551,14 +1594,18 @@ void http_serve(el_val_t port, el_val_t handler) {
|
||||
addr.sin6_addr = in6addr_any;
|
||||
addr.sin6_port = htons((uint16_t)p);
|
||||
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
perror("bind"); close(sock); return;
|
||||
perror("bind"); el_closesocket(sock); return;
|
||||
}
|
||||
if (listen(sock, 64) < 0) { perror("listen"); close(sock); return; }
|
||||
if (listen(sock, 64) < 0) { perror("listen"); el_closesocket(sock); return; }
|
||||
fprintf(stderr, "[http] listening on [::]:%d (dual-stack)\n", p);
|
||||
while (1) {
|
||||
struct sockaddr_in6 cli;
|
||||
socklen_t clen = sizeof(cli);
|
||||
#ifdef _WIN32
|
||||
SOCKET cfd = accept(sock, (struct sockaddr*)&cli, &clen);
|
||||
#else
|
||||
int cfd = accept(sock, (struct sockaddr*)&cli, &clen);
|
||||
#endif
|
||||
if (cfd < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
perror("accept"); break;
|
||||
@@ -1570,11 +1617,11 @@ void http_serve(el_val_t port, el_val_t handler) {
|
||||
_http_conn_active++;
|
||||
pthread_mutex_unlock(&_http_conn_mu);
|
||||
HttpWorkerArg* arg = malloc(sizeof(HttpWorkerArg));
|
||||
if (!arg) { close(cfd); continue; }
|
||||
if (!arg) { el_closesocket(cfd); continue; }
|
||||
arg->fd = cfd;
|
||||
pthread_t tid;
|
||||
if (pthread_create(&tid, NULL, http_worker, arg) != 0) {
|
||||
close(cfd); free(arg);
|
||||
el_closesocket(cfd); free(arg);
|
||||
pthread_mutex_lock(&_http_conn_mu);
|
||||
_http_conn_active--;
|
||||
pthread_cond_signal(&_http_conn_cv);
|
||||
@@ -1583,7 +1630,7 @@ void http_serve(el_val_t port, el_val_t handler) {
|
||||
}
|
||||
pthread_detach(tid);
|
||||
}
|
||||
close(sock);
|
||||
el_closesocket(sock);
|
||||
}
|
||||
|
||||
/* ── http_serve_async — non-blocking HTTP server ─────────────────────────── */
|
||||
@@ -1819,7 +1866,11 @@ static el_val_t http_build_headers_map(const char* hdr_block) {
|
||||
|
||||
static void* http_worker_v2(void* arg) {
|
||||
HttpWorkerArg* a = (HttpWorkerArg*)arg;
|
||||
#ifdef _WIN32
|
||||
SOCKET fd = a->fd;
|
||||
#else
|
||||
int fd = a->fd;
|
||||
#endif
|
||||
free(a);
|
||||
char *method = NULL, *path = NULL, *body = NULL, *hdr_block = NULL;
|
||||
if (http_read_request(fd, &method, &path, &body, &hdr_block) == 0) {
|
||||
@@ -1832,10 +1883,20 @@ static void* http_worker_v2(void* arg) {
|
||||
el_val_t hmap = http_build_headers_map(hdr_block ? hdr_block : "");
|
||||
el_val_t r = h(EL_STR(dispatch_method), EL_STR(path), hmap, EL_STR(body));
|
||||
const char* rs = EL_CSTR(r);
|
||||
size_t rlen = _tl_fs_read_len > 0 ? _tl_fs_read_len : (rs ? strlen(rs) : 0);
|
||||
/* Same pairing rule as the v1 worker: the fs_read length is only
|
||||
* trustworthy for the exact buffer fs_read returned. */
|
||||
size_t rlen;
|
||||
if (_tl_fs_read_len > 0 && rs && rs == _tl_fs_read_buf) {
|
||||
rlen = _tl_fs_read_len; /* raw file bytes — binary-safe */
|
||||
} else {
|
||||
rlen = rs ? strlen(rs) : 0;
|
||||
_tl_fs_read_len = 0; /* hint doesn't describe this body */
|
||||
_tl_fs_read_buf = NULL;
|
||||
}
|
||||
response = malloc(rlen + 1);
|
||||
if (response && rs) { memcpy(response, rs, rlen); response[rlen] = '\0'; }
|
||||
else if (response) { response[0] = '\0'; }
|
||||
if (_tl_fs_read_len > 0) _tl_fs_read_buf = response; /* hint follows the copy */
|
||||
el_release(hmap);
|
||||
} else {
|
||||
response = el_strdup_persist(
|
||||
@@ -1849,7 +1910,7 @@ static void* http_worker_v2(void* arg) {
|
||||
free(response);
|
||||
}
|
||||
free(method); free(path); free(body); free(hdr_block);
|
||||
close(fd);
|
||||
el_closesocket(fd);
|
||||
pthread_mutex_lock(&_http_conn_mu);
|
||||
_http_conn_active--;
|
||||
pthread_cond_signal(&_http_conn_cv);
|
||||
@@ -1879,14 +1940,18 @@ void http_serve_v2(el_val_t port, el_val_t handler) {
|
||||
addr.sin6_addr = in6addr_any;
|
||||
addr.sin6_port = htons((uint16_t)p);
|
||||
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
perror("bind"); close(sock); return;
|
||||
perror("bind"); el_closesocket(sock); return;
|
||||
}
|
||||
if (listen(sock, 64) < 0) { perror("listen"); close(sock); return; }
|
||||
if (listen(sock, 64) < 0) { perror("listen"); el_closesocket(sock); return; }
|
||||
fprintf(stderr, "[http v2] listening on [::]:%d (dual-stack)\n", p);
|
||||
while (1) {
|
||||
struct sockaddr_in6 cli;
|
||||
socklen_t clen = sizeof(cli);
|
||||
#ifdef _WIN32
|
||||
SOCKET cfd = accept(sock, (struct sockaddr*)&cli, &clen);
|
||||
#else
|
||||
int cfd = accept(sock, (struct sockaddr*)&cli, &clen);
|
||||
#endif
|
||||
if (cfd < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
perror("accept"); break;
|
||||
@@ -1898,11 +1963,11 @@ void http_serve_v2(el_val_t port, el_val_t handler) {
|
||||
_http_conn_active++;
|
||||
pthread_mutex_unlock(&_http_conn_mu);
|
||||
HttpWorkerArg* arg = malloc(sizeof(HttpWorkerArg));
|
||||
if (!arg) { close(cfd); continue; }
|
||||
if (!arg) { el_closesocket(cfd); continue; }
|
||||
arg->fd = cfd;
|
||||
pthread_t tid;
|
||||
if (pthread_create(&tid, NULL, http_worker_v2, arg) != 0) {
|
||||
close(cfd); free(arg);
|
||||
el_closesocket(cfd); free(arg);
|
||||
pthread_mutex_lock(&_http_conn_mu);
|
||||
_http_conn_active--;
|
||||
pthread_cond_signal(&_http_conn_cv);
|
||||
@@ -1911,7 +1976,7 @@ void http_serve_v2(el_val_t port, el_val_t handler) {
|
||||
}
|
||||
pthread_detach(tid);
|
||||
}
|
||||
close(sock);
|
||||
el_closesocket(sock);
|
||||
}
|
||||
|
||||
/* Build the response envelope a 4-arg handler can return. We hand-write
|
||||
@@ -1951,6 +2016,7 @@ el_val_t http_response(el_val_t status, el_val_t headers_json, el_val_t body) {
|
||||
el_val_t fs_read(el_val_t pathv) {
|
||||
const char* path = EL_CSTR(pathv);
|
||||
_tl_fs_read_len = 0;
|
||||
_tl_fs_read_buf = NULL;
|
||||
if (!path) return el_wrap_str(el_strdup(""));
|
||||
FILE* f = fopen(path, "rb");
|
||||
if (!f) return el_wrap_str(el_strdup(""));
|
||||
@@ -1962,6 +2028,7 @@ el_val_t fs_read(el_val_t pathv) {
|
||||
size_t got = fread(buf, 1, (size_t)sz, f);
|
||||
buf[got] = '\0';
|
||||
_tl_fs_read_len = got; /* store real byte count for binary-safe send */
|
||||
_tl_fs_read_buf = buf; /* ...valid ONLY for this exact buffer */
|
||||
fclose(f);
|
||||
return el_wrap_str(buf);
|
||||
}
|
||||
@@ -2056,6 +2123,23 @@ el_val_t exec(el_val_t cmdv) {
|
||||
el_val_t exec_bg(el_val_t cmdv) {
|
||||
const char* cmd = EL_CSTR(cmdv);
|
||||
if (!cmd || !*cmd) return el_wrap_str(el_strdup(""));
|
||||
#ifdef _WIN32
|
||||
/* Windows: no fork/exec. Launch a detached `cmd /c <command>` with no console window via
|
||||
CreateProcess (DETACHED_PROCESS | CREATE_NO_WINDOW). Returns the PID as a string, "" on fail.
|
||||
Mirrors the POSIX branch: child runs independently, caller is not blocked. */
|
||||
char cmdline[8192];
|
||||
snprintf(cmdline, sizeof(cmdline), "cmd.exe /c %s", cmd);
|
||||
STARTUPINFOA si; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si);
|
||||
PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(pi));
|
||||
BOOL ok = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE,
|
||||
DETACHED_PROCESS | CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
|
||||
if (!ok) return el_wrap_str(el_strdup(""));
|
||||
char pidbuf[32];
|
||||
snprintf(pidbuf, sizeof(pidbuf), "%lu", (unsigned long)pi.dwProcessId);
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
return el_wrap_str(el_strdup(pidbuf));
|
||||
#else
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
/* fork failed */
|
||||
@@ -2078,6 +2162,7 @@ el_val_t exec_bg(el_val_t cmdv) {
|
||||
char pidbuf[32];
|
||||
snprintf(pidbuf, sizeof(pidbuf), "%d", (int)pid);
|
||||
return el_wrap_str(el_strdup(pidbuf));
|
||||
#endif
|
||||
}
|
||||
|
||||
el_val_t fs_list(el_val_t pathv) {
|
||||
@@ -3417,8 +3502,10 @@ el_val_t json_get_raw(el_val_t json_str, el_val_t key) {
|
||||
const char* k = EL_CSTR(key);
|
||||
const char* p = json_find_key(json, k);
|
||||
/* Clear fs_read binary-length hint — result is a fresh null-terminated
|
||||
* string, not the raw file bytes, so Content-Length must use strlen. */
|
||||
* string, not the raw file bytes, so Content-Length must use strlen.
|
||||
* (Kept although the pointer pairing now makes this redundant.) */
|
||||
_tl_fs_read_len = 0;
|
||||
_tl_fs_read_buf = NULL;
|
||||
if (!p) return el_wrap_str(el_strdup(""));
|
||||
const char* end = json_skip_value(p);
|
||||
size_t n = (size_t)(end - p);
|
||||
@@ -4290,7 +4377,12 @@ static int _el_decompose_earth(el_caltime_t* ct, struct tm* tm_out, int* abbr_le
|
||||
localtime_r(&s, &tm);
|
||||
*tm_out = tm;
|
||||
if (abbr_buf && abbr_cap > 0) {
|
||||
/* mingw's struct tm has no tm_zone (BSD/glibc extension); no abbrev available there. */
|
||||
#ifdef _WIN32
|
||||
const char* z_str = "";
|
||||
#else
|
||||
const char* z_str = tm.tm_zone ? tm.tm_zone : "";
|
||||
#endif
|
||||
size_t n = strlen(z_str);
|
||||
if (n >= abbr_cap) n = abbr_cap - 1;
|
||||
memcpy(abbr_buf, z_str, n);
|
||||
|
||||
@@ -1072,6 +1072,7 @@ el_val_t __engram_save(el_val_t path) { return engram_save
|
||||
el_val_t __engram_load(el_val_t path) { return engram_load(path); }
|
||||
|
||||
el_val_t __engram_get_node_json(el_val_t id) { return engram_get_node_json(id); }
|
||||
el_val_t __engram_get_node_by_label(el_val_t label) { return engram_get_node_by_label(label); }
|
||||
|
||||
el_val_t __engram_search_json(el_val_t query, el_val_t limit) {
|
||||
return engram_search_json(query, limit);
|
||||
|
||||
@@ -226,6 +226,7 @@ el_val_t __engram_activate(el_val_t query, el_val_t depth);
|
||||
el_val_t __engram_save(el_val_t path);
|
||||
el_val_t __engram_load(el_val_t path);
|
||||
el_val_t __engram_get_node_json(el_val_t id);
|
||||
el_val_t __engram_get_node_by_label(el_val_t label);
|
||||
el_val_t __engram_search_json(el_val_t query, el_val_t limit);
|
||||
el_val_t __engram_scan_nodes_json(el_val_t limit, el_val_t offset);
|
||||
el_val_t __engram_scan_nodes_by_type_json(el_val_t node_type, el_val_t limit, el_val_t offset);
|
||||
|
||||
Reference in New Issue
Block a user