From 6ee553ffcee25914eaf17e620d831b1e3fb8cef3 Mon Sep 17 00:00:00 2001 From: woodtygr Date: Thu, 29 May 2025 16:02:53 -0400 Subject: [PATCH 01/19] Add files via upload --- quickjs-libc.c | 483 +++++++++++++++++++++++++++++++++++++++++++++++-- test_std.js | 387 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 857 insertions(+), 13 deletions(-) create mode 100644 test_std.js diff --git a/quickjs-libc.c b/quickjs-libc.c index d08ff7f74..06a851d52 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -1034,6 +1034,14 @@ typedef struct { bool is_popen; } JSSTDFile; +#if defined(__MINGW32__) || defined(__MINGW64__) +typedef struct { + FILE *f; + int is_kind; + char filename[64]; +} JSTMPFile; +#endif + static bool is_stdio(FILE *f) { return f == stdin || f == stdout || f == stderr; @@ -1044,7 +1052,15 @@ static void js_std_file_finalizer(JSRuntime *rt, JSValueConst val) JSThreadState *ts = js_get_thread_state(rt); JSSTDFile *s = JS_GetOpaque(val, ts->std_file_class_id); if (s) { +#if defined(__MINGW32__) || defined(__MINGW64__) + JSTMPFile *ss = (JSTMPFile*) s; + if (ss->is_kind==2) { + if (ss->f) fclose(ss->f); + if (ss->filename[0] != 0) remove(ss->filename); + } else if (s->f && !is_stdio(s->f)) { +#else if (s->f && !is_stdio(s->f)) { +#endif #if !defined(__wasi__) if (s->is_popen) pclose(s->f); @@ -1207,6 +1223,62 @@ static JSValue js_std_fdopen(JSContext *ctx, JSValueConst this_val, } #if !defined(__wasi__) +#if defined(__MINGW32__) || defined(__MINGW64__) +// c:/tmp no longer works in windows. c:/ doesn't work. permissions! +static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSTMPFile *s; + JSValue obj; + obj = JS_NewObjectClass(ctx, ts->std_file_class_id); + if (JS_IsException(obj)) + return obj; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + + char * env = getenv("TMP"); + if (!env) env = getenv("TEMP"); + int i = 0; + if (env) { + while (env[i]) { + s->filename[i] = env[i]; + i++; + if (i > 50) return JS_NULL; + }; + }; + char* fname = &s->filename[i]; + char* templ = "\\qXXXXXXX"; + while (templ[0]) { + fname[0] = templ[0]; + fname++; templ++; + }; + fname[0] = 0; + int mkf = mkstemp(s->filename); + if (mkf == -1) { + JS_FreeValue(ctx, obj); + js_free(ctx, s); + return JS_NULL; + }; + int fd = dup(mkf); + s->f = fdopen( fd, "a+"); + close(mkf); + if (argc >= 1) js_set_error_object(ctx, argv[0], s->f ? 0 : errno); + if (!s->f) { + JS_FreeValue(ctx, obj); + js_free(ctx, s); + return JS_NULL; + }; + + s->is_kind = 2; + JS_SetOpaque(obj, s); + return obj; +} +#else static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -1218,7 +1290,8 @@ static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, return JS_NULL; return js_new_std_file(ctx, f, false); } -#endif +#endif // _WIN32 +#endif // !defined(__wasi__) static JSValue js_std_sprintf(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) @@ -3057,7 +3130,20 @@ static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val, } #endif -#if !defined(_WIN32) && !defined(__wasi__) +/* start of mods - windows versions +js_os_symlink - now includes _WIN32 +js_os_pipe - now includes _WIN32 (fd version) +js_os_exec - now includes _WIN32 + -build_ms_envp - _WIN32 only ( one large double-nulled string, no array ) +js_watchpid - linux and _WIN32, hybrid function to do the wait, no status +js_os_kill - now includes _WIN32 (just a kill on pid, no signals) +js_std_tmpfile new _WIN32 version. permissions fail these days. + -std file finalizer - added remove tmpfile on delete + +verified with test_std.js modifications. +//#if !defined(_WIN32) && !defined(__wasi__) +*/ +#if !defined(__wasi__) static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -3072,12 +3158,92 @@ static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, JS_FreeCString(ctx, target); return JS_EXCEPTION; } +#ifdef _WIN32 + int isdir = 0; // might need to pass a value in for folders. + if (argc >=2) { + if (JS_ToInt32(ctx, &isdir, argv[2])) return JS_EXCEPTION; + }; + err = CreateSymbolicLinkA(linkpath, target, ( isdir | 2 ) ) ; + if (!err) err = GetLastError(); + else err = 0; +#else err = js_get_errno(symlink(target, linkpath)); +#endif JS_FreeCString(ctx, target); JS_FreeCString(ctx, linkpath); return JS_NewInt32(ctx, err); } +#endif + +#if defined(_WIN32) +#define MS_ENVP_BUFFSIZE 8192 +static char *build_ms_envp(JSContext *ctx, JSValue obj) +{ + uint32_t len, i; + JSPropertyEnum *tab; + char *envp, *pair; + const char *key, *str; + JSValue val; + size_t key_len, str_len; + + if (JS_GetOwnPropertyNames(ctx, &tab, &len, obj, + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0) + return NULL; + envp = js_mallocz(ctx,MS_ENVP_BUFFSIZE); + pair = envp; + if (!envp) goto fail; + for(i = 0; i < len; i++) { + val = JS_GetProperty(ctx, obj, tab[i].atom); + if (JS_IsException(val)) + goto fail; + str = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!str) goto fail; + key = JS_AtomToCString(ctx, tab[i].atom); + if (!key) { + JS_FreeCString(ctx, str); + goto fail; + } + key_len = strlen(key); + str_len = strlen(str); + /* parallel to build_envp for comparison + pair = js_malloc(ctx, key_len + str_len + 2); + if (!pair) { + JS_FreeCString(ctx, key); + JS_FreeCString(ctx, str); + goto fail; + }*/ + memcpy(pair, key, key_len); + pair += key_len; + pair[0] = '='; pair++; + memcpy(pair, str, str_len); + pair += str_len; + pair[0] = '\0'; + pair++; + //envp[i] = pair; + JS_FreeCString(ctx, key); + JS_FreeCString(ctx, str); + } + pair[0] = '\0'; + end: + for(i = 0; i < len; i++) + JS_FreeAtom(ctx, tab[i].atom); + js_free(ctx, tab); + return envp; + fail: + if (envp) { + //for(i = 0; i < len; i++) js_free(ctx, envp[i]); + js_free(ctx, envp); + envp = NULL; + } + goto end; + +}; + +#endif + +#if !defined(_WIN32) && !defined(__wasi__) /* return [path, errorcode] */ static JSValue js_os_readlink(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) @@ -3230,19 +3396,32 @@ static void js_os_exec_once_init(void) #endif +#endif // !_WIN32 !__wasi__ + + +#if !defined(__wasi__) + /* exec(args[, options]) -> exitcode */ +#define OS_EXEC_CMD_BUFFSIZE 2048 static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValueConst options, args = argv[0]; JSValue val, ret_val; - const char **exec_argv, *file = NULL, *str, *cwd = NULL; - char **envp = environ; + const char *file = NULL, *str, *cwd = NULL; uint32_t exec_argc, i; - int ret, pid, status; - bool block_flag = true, use_path = true; + int ret, pid; + bool block_flag = true, use_path = true, specified_fd = false; static const char *std_name[3] = { "stdin", "stdout", "stderr" }; int std_fds[3]; +#ifdef _WIN32 + char cmdbuff [OS_EXEC_CMD_BUFFSIZE]; + int cmdi; + char *envp = 0; +#else + char **envp = environ; + const char **exec_argv; + int status; uint32_t uid = -1, gid = -1; int ngroups = -1; gid_t groups[64]; @@ -3272,6 +3451,7 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, exec_argv[i] = str; } exec_argv[exec_argc] = NULL; +#endif for(i = 0; i < 3; i++) std_fds[i] = i; @@ -3317,9 +3497,11 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, if (ret) goto exception; std_fds[i] = fd; + specified_fd = true; } } +#ifndef _WIN32 val = JS_GetPropertyStr(ctx, options, "env"); if (JS_IsException(val)) goto exception; @@ -3329,7 +3511,7 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, if (!envp) goto exception; } - + val = JS_GetPropertyStr(ctx, options, "uid"); if (JS_IsException(val)) goto exception; @@ -3339,7 +3521,7 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, if (ret) goto exception; } - + val = JS_GetPropertyStr(ctx, options, "gid"); if (JS_IsException(val)) goto exception; @@ -3382,10 +3564,177 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, if (idx < len) goto exception; } + + } +#else + val = JS_GetPropertyStr(ctx, options, "env"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + envp = build_ms_envp(ctx, val); + JS_FreeValue(ctx, val); + if (!envp) + goto exception; + } + + + } + + cmdi = 0; int cmdsl; + if (cwd) { + cmdsl = strlen(cwd); + if (cmdsl) { + strncpy(&cmdbuff[cmdi], cwd, cmdsl); + cmdi += cmdsl; + }; + }; + if (file) { + cmdsl = strlen(file); + if (cmdsl) { + strncpy(&cmdbuff[cmdi], file, cmdsl); + cmdi += cmdsl; + cmdbuff[cmdi] = '"'; + cmdi++; + }; +#define WIN32_OS_EXEC_OPTION1 +#define WIN32_OS_EXEC_WATCHING +// still playing with cwd + file combinations +#ifdef WIN32_OS_EXEC_OPTION1 + } else { + strncpy(&cmdbuff[cmdi], "cmd.exe /c \"", 12); + cmdi += 12; +#endif + }; + val = JS_GetPropertyStr(ctx, args, "length"); + if (JS_IsException(val)) goto exception; + ret = JS_ToUint32(ctx, &exec_argc, val); + JS_FreeValue(ctx, val); + if (ret) goto exception; + /* arbitrary limit to avoid overflow */ + if (exec_argc < 1 || exec_argc > 65535) { + JS_ThrowTypeError(ctx, "invalid number of arguments"); + goto exception; } + for(i = 0; i < exec_argc; i++) { + val = JS_GetPropertyUint32(ctx, args, i); + if (JS_IsException(val)) goto exception; + str = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!str) goto exception; + cmdsl = strlen(str); + if (cmdsl) { +#ifdef WIN32_OS_EXEC_OPTION1 + if ((file) || (i != 0)) { +#else + if (true) { +#endif + if (OS_EXEC_CMD_BUFFSIZE < (cmdi + cmdsl)) { + JS_ThrowRangeError(ctx, "exec command line too long."); + goto exception; + }; + cmdbuff[cmdi] = ' '; + cmdi++; + }; + strncpy(&cmdbuff[cmdi], str, cmdsl); + cmdi += cmdsl; + }; + + } + cmdbuff[cmdi] = '"'; + cmdi++; + cmdbuff[cmdi] = 0; +#ifdef WIN32_OS_EXEC_WORKING + printf("path to exec: \"%s\"\r\n", cmdbuff); +#endif +#endif //_WIN32 -#if !defined(EMSCRIPTEN) && !defined(__wasi__) +#ifdef _WIN32 + STARTUPINFO istart; + PROCESS_INFORMATION iproc; + // memset was absolutely necessary. I kept getting deeper system crashes + memset(&iproc, 0, sizeof(iproc)); + memset(&istart, 0, sizeof(istart) ); + istart.cb = sizeof(istart); + + if (specified_fd) { // this is also something worth tinkering with + istart.dwFlags = STARTF_USESTDHANDLES; + }; + + istart.hStdInput = (HANDLE) _get_osfhandle( std_fds[0] ); + if (istart.hStdInput == INVALID_HANDLE_VALUE) { + JS_ThrowInternalError(ctx, "failed to associate stdin of process"); + goto exception; + }; + istart.hStdOutput = (HANDLE) _get_osfhandle( std_fds[1] ); + if (istart.hStdOutput == INVALID_HANDLE_VALUE) { + JS_ThrowInternalError(ctx, "failed to associate stdout of process"); + goto exception; + }; + istart.hStdError = (HANDLE) _get_osfhandle( std_fds[2] ); + if (istart.hStdError == INVALID_HANDLE_VALUE) { + JS_ThrowInternalError(ctx, "failed to associate stderr of process"); + goto exception; + }; +/* + printf("using handles %" PRIx64 " from %d, %" PRIx64 " from %d, %" PRIx64 " from %d\r\n", + (int64_t) istart.hStdInput, std_fds[0], (int64_t) istart.hStdOutput, std_fds[1], + (int64_t) istart.hStdError, std_fds[2]); + +BOOL CreateProcessA( + [in, optional] LPCSTR lpApplicationName, + [in, out, optional] LPSTR lpCommandLine, + [in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes, + [in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes, + [in] BOOL bInheritHandles, + [in] DWORD dwCreationFlags, + [in, optional] LPVOID lpEnvironment, + [in, optional] LPCSTR lpCurrentDirectory, + [in] LPSTARTUPINFOA lpStartupInfo, + [out] LPPROCESS_INFORMATION lpProcessInformation +);*/ + + int cflags = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW; // prevents output on stdout + + // seems like inherithandles and environ crash the return value if not perfect! + // we may also need to sim a cmd.exe chdir and a manual push with a cwd .... + if (!CreateProcessA(0, cmdbuff, 0, 0, true, cflags, envp, 0, &istart, &iproc) ) + { + int e = GetLastError(); + JS_ThrowInternalError(ctx, "failed to start process with error: %d", e); + goto exception; + }; + + int32_t excode = 0; + pid = iproc.dwProcessId; + HANDLE phandle = iproc.hProcess; + HANDLE thandle = iproc.hThread; + int besafe = 0; + if (block_flag) { + for(;;) { + int dwait = WaitForSingleObject(phandle, INFINITE); + if (dwait == WAIT_OBJECT_0) { + GetExitCodeProcess (phandle, (DWORD*) &excode); + ret_val = JS_NewInt32(ctx, (int32_t) excode); + excode = GetLastError(); + break; + } else { + besafe++; if (besafe==20) { + JS_ThrowPlainError(ctx, "exec process did not complete."); + goto exception; + }; + }; + }; + CloseHandle(phandle); + CloseHandle(thandle); + } else { + excode = (int32_t) iproc.dwProcessId; + ret_val = JS_NewInt32(ctx, excode); //(int64_t) phandle); + } + +#else // _WIN32 + +#if !defined(EMSCRIPTEN) && !defined(__wasi__) // should happen pre-fork because it calls dlsym() // and that's not an async-signal-safe function js_once(&js_os_exec_once, js_os_exec_once_init); @@ -3457,9 +3806,11 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, ret = pid; } ret_val = JS_NewInt32(ctx, ret); +#endif // !_WIN32 done: JS_FreeCString(ctx, file); JS_FreeCString(ctx, cwd); +#ifndef _WIN32 for(i = 0; i < exec_argc; i++) JS_FreeCString(ctx, exec_argv[i]); js_free(ctx, exec_argv); @@ -3473,10 +3824,115 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, js_free(ctx, envp); } return ret_val; +#else + if (envp) js_free(ctx, envp); + return ret_val; +#endif exception: ret_val = JS_EXCEPTION; goto done; } +#endif + +#ifdef _WIN32 +/* pipe() -> [read_fd, write_fd] or null if error */ +static JSValue js_os_pipe(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int pipe_fds[2], ret; + JSValue obj; +#ifdef _WIN32 +#define PIPE_BUFFSIZE 4096 +#define WIN_PIPE_SPECIALFLAGS 0 +#define WIN_OS_PIPE_USE_FDS + HANDLE pipe_handles[2]; + struct _SECURITY_ATTRIBUTES secur; + secur.nLength = sizeof(secur); + secur.lpSecurityDescriptor = 0; + secur.bInheritHandle = true; + ret = CreatePipe(&pipe_handles[0], &pipe_handles[1], &secur, PIPE_BUFFSIZE); + if (ret == 0) return JS_NULL; +#ifdef WIN_OS_PIPE_USE_FDS + pipe_fds[0] = _open_osfhandle( (intptr_t) pipe_handles[0], WIN_PIPE_SPECIALFLAGS ); + if (pipe_fds[0] == -1 ) { + CloseHandle(pipe_handles[0]); + return JS_NULL; + }; + pipe_fds[1] = _open_osfhandle( (intptr_t) pipe_handles[1], WIN_PIPE_SPECIALFLAGS ); + if (pipe_fds[1] == -1 ) { + CloseHandle(pipe_handles[1]); + return JS_NULL; + }; +#else + pipe_fds[0] = pipe_handles[0]; + pipe_fds[1] = pipe_handles[1]; +#endif // WIN_OS_PIPE_USE_FDS +#else + ret = pipe(pipe_fds); + if (ret < 0) return JS_NULL; +#endif // _WIN32 + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, pipe_fds[0]), + JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, pipe_fds[1]), + JS_PROP_C_W_E); + return obj; +} + +/* kill(pid, sig) */ +static JSValue js_os_kill(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int pid; + HANDLE ph; + DWORD flags = PROCESS_TERMINATE; + if (JS_ToInt32(ctx, &pid, argv[0])) + return JS_EXCEPTION; + ph = OpenProcess(flags, false, (DWORD) pid); + if (!ph) return JS_NewInt32(ctx, GetLastError()); + if (TerminateProcess(ph, 0)) return JS_NULL; + int err = GetLastError(); + return JS_NewInt32(ctx,err); +} + +/* watchpid(pid, blocking) -> -error/0= is still waiting/pid = complete */ +static JSValue js_os_watchpid(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int pid; + HANDLE ph; + int block = 0, ret; + DWORD options = 0, flags = PROCESS_QUERY_INFORMATION | SYNCHRONIZE; + if (JS_ToInt32(ctx, &pid, argv[0])) return JS_EXCEPTION; + if ((argc > 1) && (JS_ToInt32(ctx, &block, argv[1]))) return JS_EXCEPTION; + if (block==1) options = INFINITE; + ph = OpenProcess(flags, false, (DWORD) pid); + if (!ph) return JS_NewInt32(ctx, -GetLastError()); + ret = WaitForSingleObject((HANDLE) ph, options); + if (ret == WAIT_TIMEOUT) return JS_NewInt32(ctx, 0); // timed out + if (ret != 0) return JS_NewInt32(ctx, -GetLastError()); + return JS_NewInt32(ctx, pid); +} +#else +/* watchpid(pid, blocking) -> -error/0= is still waiting/pid = complete */ +static JSValue js_os_watchpid(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int pid, status, block = 0, options = 0, ret; + if (JS_ToInt32(ctx, &pid, argv[0])) return JS_EXCEPTION; + if ((argc > 1) && (JS_ToInt32(ctx, &block, argv[1]))) return JS_EXCEPTION; + if (!block) options = WNOHANG; + ret = waitpid(pid, &status, options); + if (ret == 0) return JS_NewInt32(ctx, 0); + if (ret==pid) return JS_NewInt32(ctx, pid); + return JS_NewInt32(ctx, -errno); +} +#endif + + +#if !defined(_WIN32) && !defined(__wasi__) /* getpid() -> pid */ static JSValue js_os_getpid(JSContext *ctx, JSValueConst this_val, @@ -4113,17 +4569,18 @@ static const JSCFunctionListEntry js_os_funcs[] = { JS_CFUNC_DEF("sleep", 1, js_os_sleep ), #if !defined(__wasi__) JS_CFUNC_DEF("realpath", 1, js_os_realpath ), + JS_CFUNC_DEF("symlink", 3, js_os_symlink ), + JS_CFUNC_DEF("pipe", 0, js_os_pipe ), + JS_CFUNC_DEF("exec", 1, js_os_exec ), + JS_CFUNC_DEF("watchpid", 2, js_os_watchpid ), + JS_CFUNC_DEF("kill", 2, js_os_kill ), #endif #if !defined(_WIN32) && !defined(__wasi__) JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ), - JS_CFUNC_DEF("symlink", 2, js_os_symlink ), JS_CFUNC_DEF("readlink", 1, js_os_readlink ), - JS_CFUNC_DEF("exec", 1, js_os_exec ), JS_CFUNC_DEF("getpid", 0, js_os_getpid ), JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ), OS_FLAG(WNOHANG), - JS_CFUNC_DEF("pipe", 0, js_os_pipe ), - JS_CFUNC_DEF("kill", 2, js_os_kill ), JS_CFUNC_DEF("dup", 1, js_os_dup ), JS_CFUNC_DEF("dup2", 2, js_os_dup2 ), #endif diff --git a/test_std.js b/test_std.js new file mode 100644 index 000000000..207f39ed6 --- /dev/null +++ b/test_std.js @@ -0,0 +1,387 @@ +import * as std from "qjs:std"; +import * as os from "qjs:os"; +import { assert } from "./assert.js"; + +const isWin = os.platform === 'win32'; +const isCygwin = os.platform === 'cygwin'; + + +function test_printf() +{ + assert(std.sprintf("a=%d s=%s", 123, "abc"), "a=123 s=abc"); + assert(std.sprintf("%010d", 123), "0000000123"); + assert(std.sprintf("%x", -2), "fffffffe"); + assert(std.sprintf("%lx", -2), "fffffffffffffffe"); + assert(std.sprintf("%10.1f", 2.1), " 2.1"); + assert(std.sprintf("%*.*f", 10, 2, -2.13), " -2.13"); + assert(std.sprintf("%#lx", 0x7fffffffffffffffn), "0x7fffffffffffffff"); +} + +function test_file1() +{ + var f, len, str, size, buf, ret, i, str1, ab; + + f = std.tmpfile(); + str = "hello world\n"; + f.puts(str); + + f.seek(0, std.SEEK_SET); + ab = f.readAsArrayBuffer(); + assert([...new Uint8Array(ab)], str.split("").map(c => c.charCodeAt(0))); + + f.seek(0, std.SEEK_SET); + str1 = f.readAsString(); + assert(str1, str); + + f.seek(0, std.SEEK_END); + size = f.tell(); + assert(size, str.length); + + f.seek(0, std.SEEK_SET); + + buf = new Uint8Array(size); + ret = f.read(buf.buffer, 0, size); + assert(ret, size); + for(i = 0; i < size; i++) + assert(buf[i], str.charCodeAt(i)); + + f.close(); +} + +function test_file2() +{ + var f, str, i, size; + f = std.tmpfile(); + str = "hello world\n"; + size = str.length; + for(i = 0; i < size; i++) + f.putByte(str.charCodeAt(i)); + f.seek(0, std.SEEK_SET); + for(i = 0; i < size; i++) { + assert(str.charCodeAt(i), f.getByte()); + } + assert(f.getByte(), -1); + f.close(); +} + +function test_getline() +{ + var f, line, line_count, lines, i; + + lines = ["hello world", "line 1", "line 2" ]; + f = std.tmpfile(); + for(i = 0; i < lines.length; i++) { + f.puts(lines[i], "\n"); + } + + f.seek(0, std.SEEK_SET); + assert(!f.eof()); + line_count = 0; + for(;;) { + line = f.getline(); + if (line === null) + break; + assert(line, lines[line_count]); + line_count++; + } + assert(f.eof()); + assert(line_count, lines.length); + + f.close(); +} + +function test_popen() +{ + var str, f, fname = "tmp_file.txt"; + var ta, content = "hello world"; + var cmd = isWin ? "type" : "cat"; + + ta = new Uint8Array([...content].map(c => c.charCodeAt(0))); + std.writeFile(fname, ta); + assert(std.loadFile(fname), content); + std.writeFile(fname, ta.buffer); + assert(std.loadFile(fname), content); + std.writeFile(fname, content); + assert(std.loadFile(fname), content); + + /* execute shell command */ + f = std.popen(cmd + " " + fname, "r"); + str = f.readAsString(); + f.close(); + + assert(str, content); + + os.remove(fname); +} + +function test_os() +{ + var fd, fpath, fname, fdir, buf, buf2, i, files, err, fdate, st, link_path; + + // XXX(bnoordhuis) disabled because stdio is not a tty on CI + //assert(os.isatty(0)); + + fdir = "test_tmp_dir"; + fname = "tmp_file.txt"; + fpath = fdir + "/" + fname; + link_path = fdir + "/test_link"; + + os.remove(link_path); + os.remove(fpath); + os.remove(fdir); + + err = os.mkdir(fdir, 0o755); + assert(err, 0); + + fd = os.open(fpath, os.O_RDWR | os.O_CREAT | os.O_TRUNC); + assert(fd >= 0); + + buf = new Uint8Array(10); + for(i = 0; i < buf.length; i++) + buf[i] = i; + assert(os.write(fd, buf.buffer, 0, buf.length), buf.length); + + assert(os.seek(fd, 0, std.SEEK_SET), 0); + buf2 = new Uint8Array(buf.length); + assert(os.read(fd, buf2.buffer, 0, buf2.length), buf2.length); + + for(i = 0; i < buf.length; i++) + assert(buf[i] == buf2[i]); + + if (typeof BigInt !== "undefined") { + assert(os.seek(fd, BigInt(6), std.SEEK_SET), BigInt(6)); + assert(os.read(fd, buf2.buffer, 0, 1), 1); + assert(buf[6] == buf2[0]); + } + + assert(os.close(fd), 0); + + [files, err] = os.readdir(fdir); + assert(err, 0); + assert(files.indexOf(fname) >= 0); + + fdate = 10000; + + err = os.utimes(fpath, fdate, fdate); + assert(err, 0); + + [st, err] = os.stat(fpath); + assert(err, 0); + assert(st.mode & os.S_IFMT, os.S_IFREG); + + + if (!isWin) { + // moved + assert(st.mtime, fdate); + + err = os.symlink(fname, link_path); + assert(err, 0); + + [st, err] = os.lstat(link_path); + assert(err, 0); + assert(st.mode & os.S_IFMT, os.S_IFLNK); + + [buf, err] = os.readlink(link_path); + assert(err, 0); + assert(buf, fname); + + assert(os.remove(link_path) === 0); + + } + + [buf, err] = os.getcwd(); + assert(err, 0); + + [buf2, err] = os.realpath("."); + assert(err, 0); + + assert(buf, buf2); + + assert(os.remove(fpath) === 0); + + fd = os.open(fpath, os.O_RDONLY); + assert(fd < 0); + + assert(os.remove(fdir) === 0); +} + +function test_os_pipe() { + + var fds, fr, fw, ab; + fds = os.pipe(); +// console.log("got: " + fds[0] + " " + fds[1]); + fw = std.fdopen(fds[1], "w"); + fr = std.fdopen(fds[0], "r"); + const tstr = "this is a test\r"; + fw.puts(tstr + "\n"); +// fw.puts("this is slow!\r\n"); + fw.puts("\r\n"); + fw.flush(); +// fw.close(); + var barray = new Uint8Array(64); +// ab = fr.read(barray.buffer, 0, 64); +// console.log(ab); +// console.log(fr.getline()); +// console.log(fr.getline()); + ab = fr.getline(); + assert(ab.length, tstr.length); + fw.close(); + fr.close(); +}; + +test_os_pipe(); + +function test_win_os_exec() { + var ret, fds, pid, f, status; + +/* functionality just isn't the same in win32! + in fact, in MSYS the command prompt behaves differently. + I did manage all of this with FDS and PIDS and not handles + hello/r is an artifact of the getline unix emulation + I made the executive decision so that this isn't useless: + watchpid and kill should be made compatible on all systems. + my os.kill is just a terminateprocess call. it returns early. + watchpid is too fast for the kill to complete in VC + NT + at least we can monitor status of a script and execute programs. +*/ + ret = os.exec(["smeegul desgpglam golum"]); + assert(ret, 1); + + ret = os.exec(["echo test 0"]); + assert(ret, 0); + + ret = os.exec(["cmd.exe", "/c", "exit 2"], { usePath: false }); + assert(ret, 2); + + fds = os.pipe(); + pid = os.exec(["echo %FOO%"], { + stdout: fds[1], + block: false, + env: { FOO: "hello" }, + } ); + assert(pid >= 0); + os.close(fds[1]); /* close the write end (as it is only in the child) */ + f = std.fdopen(fds[0], "r"); + var gl = f.getline(); + assert(gl, "hello\r"); + assert(f.getline(), null); + f.close(); + ret = os.watchpid(pid, 1); + assert(ret, pid); + pid = os.exec(["cat"], { block: false } ); + assert(pid >= 0); + os.kill(pid, os.SIGTERM); + os.sleep(1); + ret = os.watchpid(pid, 0); + assert(ret, pid); + // notes from before...... + // Flaky on cygwin for unclear reasons, see + // https://github.com/quickjs-ng/quickjs/issues/184 +}; + +function test_os_exec() +{ + var ret, fds, pid, f, status; + + ret = os.exec(["true"]); + assert(ret, 0); + + ret = os.exec(["/bin/sh", "-c", "exit 1"], { usePath: false }); + assert(ret, 1); + + fds = os.pipe(); + pid = os.exec(["sh", "-c", "echo $FOO"], { + stdout: fds[1], + block: false, + env: { FOO: "hello" }, + } ); + assert(pid >= 0); + os.close(fds[1]); /* close the write end (as it is only in the child) */ + f = std.fdopen(fds[0], "r"); + assert(f.getline(), "hello"); + assert(f.getline(), null); + f.close(); + /* + [ret, status] = os.waitpid(pid, 0); + assert(ret, pid); + assert(status & 0x7f, 0); /* exited */ + //assert(status >> 8, 0); /* exit code */ + + // added to test watchpid (for win32 compatible code) + ret = os.watchpid(pid, 1); + assert(ret, pid); + + pid = os.exec(["cat"], { block: false } ); + ret = os.watchpid(pid, 0); + assert(ret, 0); + assert(pid >= 0); + os.kill(pid, os.SIGTERM); + //os.sleep(1); + [ret, status] = os.waitpid(pid, 0); + assert(ret, pid); + // Flaky on cygwin for unclear reasons, see + // https://github.com/quickjs-ng/quickjs/issues/184 + if (!isCygwin) { + assert(status & 0x7f, os.SIGTERM); + } +} + +function test_interval() +{ + var t = os.setInterval(f, 1); + function f() { + if (++f.count === 3) os.clearInterval(t); + } + f.count = 0; +} + +function test_timeout() +{ + var th, i; + + /* just test that a timer can be inserted and removed */ + th = []; + for(i = 0; i < 3; i++) + th[i] = os.setTimeout(function () { }, 1000); + for(i = 0; i < 3; i++) + os.clearTimeout(th[i]); +} + +function test_timeout_order() +{ + var s = ""; + os.setTimeout(a, 0); + os.setTimeout(b, 100); + os.setTimeout(d, 700); + function a() { s += "a"; os.setTimeout(c, 300); } + function b() { s += "b"; } + function c() { s += "c"; } + function d() { assert(s, "abc"); } // not "acb" +} + +function test_stdio_close() +{ + for (const f of [std.in, std.out, std.err]) { + let caught = false; + try { + f.close(); + } catch (e) { + assert(/cannot close stdio/.test(e.message)); + caught = true; + } + assert(caught); + } +} + +test_printf(); +test_file1(); +test_file2(); +test_getline(); +test_popen(); +test_os(); +if (!isWin) test_os_exec(); +else test_win_os_exec(); +test_interval(); +test_timeout(); +test_timeout_order(); +test_stdio_close(); From f323678262314526f3c617cd62feb1bce1f16eef Mon Sep 17 00:00:00 2001 From: woodtygr Date: Thu, 29 May 2025 16:10:23 -0400 Subject: [PATCH 02/19] Add files via upload --- quickjs-libc.c | 483 ++----------------------------------------------- 1 file changed, 13 insertions(+), 470 deletions(-) diff --git a/quickjs-libc.c b/quickjs-libc.c index 06a851d52..d08ff7f74 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -1034,14 +1034,6 @@ typedef struct { bool is_popen; } JSSTDFile; -#if defined(__MINGW32__) || defined(__MINGW64__) -typedef struct { - FILE *f; - int is_kind; - char filename[64]; -} JSTMPFile; -#endif - static bool is_stdio(FILE *f) { return f == stdin || f == stdout || f == stderr; @@ -1052,15 +1044,7 @@ static void js_std_file_finalizer(JSRuntime *rt, JSValueConst val) JSThreadState *ts = js_get_thread_state(rt); JSSTDFile *s = JS_GetOpaque(val, ts->std_file_class_id); if (s) { -#if defined(__MINGW32__) || defined(__MINGW64__) - JSTMPFile *ss = (JSTMPFile*) s; - if (ss->is_kind==2) { - if (ss->f) fclose(ss->f); - if (ss->filename[0] != 0) remove(ss->filename); - } else if (s->f && !is_stdio(s->f)) { -#else if (s->f && !is_stdio(s->f)) { -#endif #if !defined(__wasi__) if (s->is_popen) pclose(s->f); @@ -1223,62 +1207,6 @@ static JSValue js_std_fdopen(JSContext *ctx, JSValueConst this_val, } #if !defined(__wasi__) -#if defined(__MINGW32__) || defined(__MINGW64__) -// c:/tmp no longer works in windows. c:/ doesn't work. permissions! -static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSRuntime *rt = JS_GetRuntime(ctx); - JSThreadState *ts = js_get_thread_state(rt); - JSTMPFile *s; - JSValue obj; - obj = JS_NewObjectClass(ctx, ts->std_file_class_id); - if (JS_IsException(obj)) - return obj; - s = js_mallocz(ctx, sizeof(*s)); - if (!s) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - - char * env = getenv("TMP"); - if (!env) env = getenv("TEMP"); - int i = 0; - if (env) { - while (env[i]) { - s->filename[i] = env[i]; - i++; - if (i > 50) return JS_NULL; - }; - }; - char* fname = &s->filename[i]; - char* templ = "\\qXXXXXXX"; - while (templ[0]) { - fname[0] = templ[0]; - fname++; templ++; - }; - fname[0] = 0; - int mkf = mkstemp(s->filename); - if (mkf == -1) { - JS_FreeValue(ctx, obj); - js_free(ctx, s); - return JS_NULL; - }; - int fd = dup(mkf); - s->f = fdopen( fd, "a+"); - close(mkf); - if (argc >= 1) js_set_error_object(ctx, argv[0], s->f ? 0 : errno); - if (!s->f) { - JS_FreeValue(ctx, obj); - js_free(ctx, s); - return JS_NULL; - }; - - s->is_kind = 2; - JS_SetOpaque(obj, s); - return obj; -} -#else static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -1290,8 +1218,7 @@ static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, return JS_NULL; return js_new_std_file(ctx, f, false); } -#endif // _WIN32 -#endif // !defined(__wasi__) +#endif static JSValue js_std_sprintf(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) @@ -3130,20 +3057,7 @@ static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val, } #endif -/* start of mods - windows versions -js_os_symlink - now includes _WIN32 -js_os_pipe - now includes _WIN32 (fd version) -js_os_exec - now includes _WIN32 - -build_ms_envp - _WIN32 only ( one large double-nulled string, no array ) -js_watchpid - linux and _WIN32, hybrid function to do the wait, no status -js_os_kill - now includes _WIN32 (just a kill on pid, no signals) -js_std_tmpfile new _WIN32 version. permissions fail these days. - -std file finalizer - added remove tmpfile on delete - -verified with test_std.js modifications. -//#if !defined(_WIN32) && !defined(__wasi__) -*/ -#if !defined(__wasi__) +#if !defined(_WIN32) && !defined(__wasi__) static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -3158,92 +3072,12 @@ static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, JS_FreeCString(ctx, target); return JS_EXCEPTION; } -#ifdef _WIN32 - int isdir = 0; // might need to pass a value in for folders. - if (argc >=2) { - if (JS_ToInt32(ctx, &isdir, argv[2])) return JS_EXCEPTION; - }; - err = CreateSymbolicLinkA(linkpath, target, ( isdir | 2 ) ) ; - if (!err) err = GetLastError(); - else err = 0; -#else err = js_get_errno(symlink(target, linkpath)); -#endif JS_FreeCString(ctx, target); JS_FreeCString(ctx, linkpath); return JS_NewInt32(ctx, err); } -#endif - -#if defined(_WIN32) -#define MS_ENVP_BUFFSIZE 8192 -static char *build_ms_envp(JSContext *ctx, JSValue obj) -{ - uint32_t len, i; - JSPropertyEnum *tab; - char *envp, *pair; - const char *key, *str; - JSValue val; - size_t key_len, str_len; - - if (JS_GetOwnPropertyNames(ctx, &tab, &len, obj, - JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0) - return NULL; - envp = js_mallocz(ctx,MS_ENVP_BUFFSIZE); - pair = envp; - if (!envp) goto fail; - for(i = 0; i < len; i++) { - val = JS_GetProperty(ctx, obj, tab[i].atom); - if (JS_IsException(val)) - goto fail; - str = JS_ToCString(ctx, val); - JS_FreeValue(ctx, val); - if (!str) goto fail; - key = JS_AtomToCString(ctx, tab[i].atom); - if (!key) { - JS_FreeCString(ctx, str); - goto fail; - } - key_len = strlen(key); - str_len = strlen(str); - /* parallel to build_envp for comparison - pair = js_malloc(ctx, key_len + str_len + 2); - if (!pair) { - JS_FreeCString(ctx, key); - JS_FreeCString(ctx, str); - goto fail; - }*/ - memcpy(pair, key, key_len); - pair += key_len; - pair[0] = '='; pair++; - memcpy(pair, str, str_len); - pair += str_len; - pair[0] = '\0'; - pair++; - //envp[i] = pair; - JS_FreeCString(ctx, key); - JS_FreeCString(ctx, str); - } - pair[0] = '\0'; - end: - for(i = 0; i < len; i++) - JS_FreeAtom(ctx, tab[i].atom); - js_free(ctx, tab); - return envp; - fail: - if (envp) { - //for(i = 0; i < len; i++) js_free(ctx, envp[i]); - js_free(ctx, envp); - envp = NULL; - } - goto end; - -}; - -#endif - -#if !defined(_WIN32) && !defined(__wasi__) /* return [path, errorcode] */ static JSValue js_os_readlink(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) @@ -3396,32 +3230,19 @@ static void js_os_exec_once_init(void) #endif -#endif // !_WIN32 !__wasi__ - - -#if !defined(__wasi__) - /* exec(args[, options]) -> exitcode */ -#define OS_EXEC_CMD_BUFFSIZE 2048 static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValueConst options, args = argv[0]; JSValue val, ret_val; - const char *file = NULL, *str, *cwd = NULL; + const char **exec_argv, *file = NULL, *str, *cwd = NULL; + char **envp = environ; uint32_t exec_argc, i; - int ret, pid; - bool block_flag = true, use_path = true, specified_fd = false; + int ret, pid, status; + bool block_flag = true, use_path = true; static const char *std_name[3] = { "stdin", "stdout", "stderr" }; int std_fds[3]; -#ifdef _WIN32 - char cmdbuff [OS_EXEC_CMD_BUFFSIZE]; - int cmdi; - char *envp = 0; -#else - char **envp = environ; - const char **exec_argv; - int status; uint32_t uid = -1, gid = -1; int ngroups = -1; gid_t groups[64]; @@ -3451,7 +3272,6 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, exec_argv[i] = str; } exec_argv[exec_argc] = NULL; -#endif for(i = 0; i < 3; i++) std_fds[i] = i; @@ -3497,11 +3317,9 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, if (ret) goto exception; std_fds[i] = fd; - specified_fd = true; } } -#ifndef _WIN32 val = JS_GetPropertyStr(ctx, options, "env"); if (JS_IsException(val)) goto exception; @@ -3511,7 +3329,7 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, if (!envp) goto exception; } - + val = JS_GetPropertyStr(ctx, options, "uid"); if (JS_IsException(val)) goto exception; @@ -3521,7 +3339,7 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, if (ret) goto exception; } - + val = JS_GetPropertyStr(ctx, options, "gid"); if (JS_IsException(val)) goto exception; @@ -3564,177 +3382,10 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, if (idx < len) goto exception; } - - } -#else - val = JS_GetPropertyStr(ctx, options, "env"); - if (JS_IsException(val)) - goto exception; - if (!JS_IsUndefined(val)) { - envp = build_ms_envp(ctx, val); - JS_FreeValue(ctx, val); - if (!envp) - goto exception; - } - - - } - - cmdi = 0; int cmdsl; - if (cwd) { - cmdsl = strlen(cwd); - if (cmdsl) { - strncpy(&cmdbuff[cmdi], cwd, cmdsl); - cmdi += cmdsl; - }; - }; - if (file) { - cmdsl = strlen(file); - if (cmdsl) { - strncpy(&cmdbuff[cmdi], file, cmdsl); - cmdi += cmdsl; - cmdbuff[cmdi] = '"'; - cmdi++; - }; -#define WIN32_OS_EXEC_OPTION1 -#define WIN32_OS_EXEC_WATCHING -// still playing with cwd + file combinations -#ifdef WIN32_OS_EXEC_OPTION1 - } else { - strncpy(&cmdbuff[cmdi], "cmd.exe /c \"", 12); - cmdi += 12; -#endif - }; - val = JS_GetPropertyStr(ctx, args, "length"); - if (JS_IsException(val)) goto exception; - ret = JS_ToUint32(ctx, &exec_argc, val); - JS_FreeValue(ctx, val); - if (ret) goto exception; - /* arbitrary limit to avoid overflow */ - if (exec_argc < 1 || exec_argc > 65535) { - JS_ThrowTypeError(ctx, "invalid number of arguments"); - goto exception; } - for(i = 0; i < exec_argc; i++) { - val = JS_GetPropertyUint32(ctx, args, i); - if (JS_IsException(val)) goto exception; - str = JS_ToCString(ctx, val); - JS_FreeValue(ctx, val); - if (!str) goto exception; - cmdsl = strlen(str); - if (cmdsl) { -#ifdef WIN32_OS_EXEC_OPTION1 - if ((file) || (i != 0)) { -#else - if (true) { -#endif - if (OS_EXEC_CMD_BUFFSIZE < (cmdi + cmdsl)) { - JS_ThrowRangeError(ctx, "exec command line too long."); - goto exception; - }; - cmdbuff[cmdi] = ' '; - cmdi++; - }; - strncpy(&cmdbuff[cmdi], str, cmdsl); - cmdi += cmdsl; - }; - - } - cmdbuff[cmdi] = '"'; - cmdi++; - cmdbuff[cmdi] = 0; -#ifdef WIN32_OS_EXEC_WORKING - printf("path to exec: \"%s\"\r\n", cmdbuff); -#endif -#endif //_WIN32 -#ifdef _WIN32 - STARTUPINFO istart; - PROCESS_INFORMATION iproc; - // memset was absolutely necessary. I kept getting deeper system crashes - memset(&iproc, 0, sizeof(iproc)); - memset(&istart, 0, sizeof(istart) ); - istart.cb = sizeof(istart); - - if (specified_fd) { // this is also something worth tinkering with - istart.dwFlags = STARTF_USESTDHANDLES; - }; - - istart.hStdInput = (HANDLE) _get_osfhandle( std_fds[0] ); - if (istart.hStdInput == INVALID_HANDLE_VALUE) { - JS_ThrowInternalError(ctx, "failed to associate stdin of process"); - goto exception; - }; - istart.hStdOutput = (HANDLE) _get_osfhandle( std_fds[1] ); - if (istart.hStdOutput == INVALID_HANDLE_VALUE) { - JS_ThrowInternalError(ctx, "failed to associate stdout of process"); - goto exception; - }; - istart.hStdError = (HANDLE) _get_osfhandle( std_fds[2] ); - if (istart.hStdError == INVALID_HANDLE_VALUE) { - JS_ThrowInternalError(ctx, "failed to associate stderr of process"); - goto exception; - }; -/* - printf("using handles %" PRIx64 " from %d, %" PRIx64 " from %d, %" PRIx64 " from %d\r\n", - (int64_t) istart.hStdInput, std_fds[0], (int64_t) istart.hStdOutput, std_fds[1], - (int64_t) istart.hStdError, std_fds[2]); - -BOOL CreateProcessA( - [in, optional] LPCSTR lpApplicationName, - [in, out, optional] LPSTR lpCommandLine, - [in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes, - [in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes, - [in] BOOL bInheritHandles, - [in] DWORD dwCreationFlags, - [in, optional] LPVOID lpEnvironment, - [in, optional] LPCSTR lpCurrentDirectory, - [in] LPSTARTUPINFOA lpStartupInfo, - [out] LPPROCESS_INFORMATION lpProcessInformation -);*/ - - int cflags = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW; // prevents output on stdout - - // seems like inherithandles and environ crash the return value if not perfect! - // we may also need to sim a cmd.exe chdir and a manual push with a cwd .... - if (!CreateProcessA(0, cmdbuff, 0, 0, true, cflags, envp, 0, &istart, &iproc) ) - { - int e = GetLastError(); - JS_ThrowInternalError(ctx, "failed to start process with error: %d", e); - goto exception; - }; - - int32_t excode = 0; - pid = iproc.dwProcessId; - HANDLE phandle = iproc.hProcess; - HANDLE thandle = iproc.hThread; - int besafe = 0; - if (block_flag) { - for(;;) { - int dwait = WaitForSingleObject(phandle, INFINITE); - if (dwait == WAIT_OBJECT_0) { - GetExitCodeProcess (phandle, (DWORD*) &excode); - ret_val = JS_NewInt32(ctx, (int32_t) excode); - excode = GetLastError(); - break; - } else { - besafe++; if (besafe==20) { - JS_ThrowPlainError(ctx, "exec process did not complete."); - goto exception; - }; - }; - }; - CloseHandle(phandle); - CloseHandle(thandle); - } else { - excode = (int32_t) iproc.dwProcessId; - ret_val = JS_NewInt32(ctx, excode); //(int64_t) phandle); - } - -#else // _WIN32 - -#if !defined(EMSCRIPTEN) && !defined(__wasi__) +#if !defined(EMSCRIPTEN) && !defined(__wasi__) // should happen pre-fork because it calls dlsym() // and that's not an async-signal-safe function js_once(&js_os_exec_once, js_os_exec_once_init); @@ -3806,11 +3457,9 @@ BOOL CreateProcessA( ret = pid; } ret_val = JS_NewInt32(ctx, ret); -#endif // !_WIN32 done: JS_FreeCString(ctx, file); JS_FreeCString(ctx, cwd); -#ifndef _WIN32 for(i = 0; i < exec_argc; i++) JS_FreeCString(ctx, exec_argv[i]); js_free(ctx, exec_argv); @@ -3824,115 +3473,10 @@ BOOL CreateProcessA( js_free(ctx, envp); } return ret_val; -#else - if (envp) js_free(ctx, envp); - return ret_val; -#endif exception: ret_val = JS_EXCEPTION; goto done; } -#endif - -#ifdef _WIN32 -/* pipe() -> [read_fd, write_fd] or null if error */ -static JSValue js_os_pipe(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int pipe_fds[2], ret; - JSValue obj; -#ifdef _WIN32 -#define PIPE_BUFFSIZE 4096 -#define WIN_PIPE_SPECIALFLAGS 0 -#define WIN_OS_PIPE_USE_FDS - HANDLE pipe_handles[2]; - struct _SECURITY_ATTRIBUTES secur; - secur.nLength = sizeof(secur); - secur.lpSecurityDescriptor = 0; - secur.bInheritHandle = true; - ret = CreatePipe(&pipe_handles[0], &pipe_handles[1], &secur, PIPE_BUFFSIZE); - if (ret == 0) return JS_NULL; -#ifdef WIN_OS_PIPE_USE_FDS - pipe_fds[0] = _open_osfhandle( (intptr_t) pipe_handles[0], WIN_PIPE_SPECIALFLAGS ); - if (pipe_fds[0] == -1 ) { - CloseHandle(pipe_handles[0]); - return JS_NULL; - }; - pipe_fds[1] = _open_osfhandle( (intptr_t) pipe_handles[1], WIN_PIPE_SPECIALFLAGS ); - if (pipe_fds[1] == -1 ) { - CloseHandle(pipe_handles[1]); - return JS_NULL; - }; -#else - pipe_fds[0] = pipe_handles[0]; - pipe_fds[1] = pipe_handles[1]; -#endif // WIN_OS_PIPE_USE_FDS -#else - ret = pipe(pipe_fds); - if (ret < 0) return JS_NULL; -#endif // _WIN32 - obj = JS_NewArray(ctx); - if (JS_IsException(obj)) - return obj; - JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, pipe_fds[0]), - JS_PROP_C_W_E); - JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, pipe_fds[1]), - JS_PROP_C_W_E); - return obj; -} - -/* kill(pid, sig) */ -static JSValue js_os_kill(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int pid; - HANDLE ph; - DWORD flags = PROCESS_TERMINATE; - if (JS_ToInt32(ctx, &pid, argv[0])) - return JS_EXCEPTION; - ph = OpenProcess(flags, false, (DWORD) pid); - if (!ph) return JS_NewInt32(ctx, GetLastError()); - if (TerminateProcess(ph, 0)) return JS_NULL; - int err = GetLastError(); - return JS_NewInt32(ctx,err); -} - -/* watchpid(pid, blocking) -> -error/0= is still waiting/pid = complete */ -static JSValue js_os_watchpid(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int pid; - HANDLE ph; - int block = 0, ret; - DWORD options = 0, flags = PROCESS_QUERY_INFORMATION | SYNCHRONIZE; - if (JS_ToInt32(ctx, &pid, argv[0])) return JS_EXCEPTION; - if ((argc > 1) && (JS_ToInt32(ctx, &block, argv[1]))) return JS_EXCEPTION; - if (block==1) options = INFINITE; - ph = OpenProcess(flags, false, (DWORD) pid); - if (!ph) return JS_NewInt32(ctx, -GetLastError()); - ret = WaitForSingleObject((HANDLE) ph, options); - if (ret == WAIT_TIMEOUT) return JS_NewInt32(ctx, 0); // timed out - if (ret != 0) return JS_NewInt32(ctx, -GetLastError()); - return JS_NewInt32(ctx, pid); -} -#else -/* watchpid(pid, blocking) -> -error/0= is still waiting/pid = complete */ -static JSValue js_os_watchpid(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int pid, status, block = 0, options = 0, ret; - if (JS_ToInt32(ctx, &pid, argv[0])) return JS_EXCEPTION; - if ((argc > 1) && (JS_ToInt32(ctx, &block, argv[1]))) return JS_EXCEPTION; - if (!block) options = WNOHANG; - ret = waitpid(pid, &status, options); - if (ret == 0) return JS_NewInt32(ctx, 0); - if (ret==pid) return JS_NewInt32(ctx, pid); - return JS_NewInt32(ctx, -errno); -} -#endif - - -#if !defined(_WIN32) && !defined(__wasi__) /* getpid() -> pid */ static JSValue js_os_getpid(JSContext *ctx, JSValueConst this_val, @@ -4569,18 +4113,17 @@ static const JSCFunctionListEntry js_os_funcs[] = { JS_CFUNC_DEF("sleep", 1, js_os_sleep ), #if !defined(__wasi__) JS_CFUNC_DEF("realpath", 1, js_os_realpath ), - JS_CFUNC_DEF("symlink", 3, js_os_symlink ), - JS_CFUNC_DEF("pipe", 0, js_os_pipe ), - JS_CFUNC_DEF("exec", 1, js_os_exec ), - JS_CFUNC_DEF("watchpid", 2, js_os_watchpid ), - JS_CFUNC_DEF("kill", 2, js_os_kill ), #endif #if !defined(_WIN32) && !defined(__wasi__) JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ), + JS_CFUNC_DEF("symlink", 2, js_os_symlink ), JS_CFUNC_DEF("readlink", 1, js_os_readlink ), + JS_CFUNC_DEF("exec", 1, js_os_exec ), JS_CFUNC_DEF("getpid", 0, js_os_getpid ), JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ), OS_FLAG(WNOHANG), + JS_CFUNC_DEF("pipe", 0, js_os_pipe ), + JS_CFUNC_DEF("kill", 2, js_os_kill ), JS_CFUNC_DEF("dup", 1, js_os_dup ), JS_CFUNC_DEF("dup2", 2, js_os_dup2 ), #endif From 7774b71d6ebb440cb92a0e1d691af89cd9ee1cde Mon Sep 17 00:00:00 2001 From: woodtygr Date: Thu, 29 May 2025 16:13:45 -0400 Subject: [PATCH 03/19] Add files via upload --- tests/test_std.js | 95 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 4 deletions(-) diff --git a/tests/test_std.js b/tests/test_std.js index 32648ef57..207f39ed6 100644 --- a/tests/test_std.js +++ b/tests/test_std.js @@ -161,16 +161,19 @@ function test_os() assert(files.indexOf(fname) >= 0); fdate = 10000; - + err = os.utimes(fpath, fdate, fdate); assert(err, 0); [st, err] = os.stat(fpath); assert(err, 0); assert(st.mode & os.S_IFMT, os.S_IFREG); - assert(st.mtime, fdate); + if (!isWin) { + // moved + assert(st.mtime, fdate); + err = os.symlink(fname, link_path); assert(err, 0); @@ -183,6 +186,7 @@ function test_os() assert(buf, fname); assert(os.remove(link_path) === 0); + } [buf, err] = os.getcwd(); @@ -201,6 +205,80 @@ function test_os() assert(os.remove(fdir) === 0); } +function test_os_pipe() { + + var fds, fr, fw, ab; + fds = os.pipe(); +// console.log("got: " + fds[0] + " " + fds[1]); + fw = std.fdopen(fds[1], "w"); + fr = std.fdopen(fds[0], "r"); + const tstr = "this is a test\r"; + fw.puts(tstr + "\n"); +// fw.puts("this is slow!\r\n"); + fw.puts("\r\n"); + fw.flush(); +// fw.close(); + var barray = new Uint8Array(64); +// ab = fr.read(barray.buffer, 0, 64); +// console.log(ab); +// console.log(fr.getline()); +// console.log(fr.getline()); + ab = fr.getline(); + assert(ab.length, tstr.length); + fw.close(); + fr.close(); +}; + +test_os_pipe(); + +function test_win_os_exec() { + var ret, fds, pid, f, status; + +/* functionality just isn't the same in win32! + in fact, in MSYS the command prompt behaves differently. + I did manage all of this with FDS and PIDS and not handles + hello/r is an artifact of the getline unix emulation + I made the executive decision so that this isn't useless: + watchpid and kill should be made compatible on all systems. + my os.kill is just a terminateprocess call. it returns early. + watchpid is too fast for the kill to complete in VC + NT + at least we can monitor status of a script and execute programs. +*/ + ret = os.exec(["smeegul desgpglam golum"]); + assert(ret, 1); + + ret = os.exec(["echo test 0"]); + assert(ret, 0); + + ret = os.exec(["cmd.exe", "/c", "exit 2"], { usePath: false }); + assert(ret, 2); + + fds = os.pipe(); + pid = os.exec(["echo %FOO%"], { + stdout: fds[1], + block: false, + env: { FOO: "hello" }, + } ); + assert(pid >= 0); + os.close(fds[1]); /* close the write end (as it is only in the child) */ + f = std.fdopen(fds[0], "r"); + var gl = f.getline(); + assert(gl, "hello\r"); + assert(f.getline(), null); + f.close(); + ret = os.watchpid(pid, 1); + assert(ret, pid); + pid = os.exec(["cat"], { block: false } ); + assert(pid >= 0); + os.kill(pid, os.SIGTERM); + os.sleep(1); + ret = os.watchpid(pid, 0); + assert(ret, pid); + // notes from before...... + // Flaky on cygwin for unclear reasons, see + // https://github.com/quickjs-ng/quickjs/issues/184 +}; + function test_os_exec() { var ret, fds, pid, f, status; @@ -223,14 +301,22 @@ function test_os_exec() assert(f.getline(), "hello"); assert(f.getline(), null); f.close(); + /* [ret, status] = os.waitpid(pid, 0); assert(ret, pid); assert(status & 0x7f, 0); /* exited */ - assert(status >> 8, 0); /* exit code */ + //assert(status >> 8, 0); /* exit code */ + + // added to test watchpid (for win32 compatible code) + ret = os.watchpid(pid, 1); + assert(ret, pid); pid = os.exec(["cat"], { block: false } ); + ret = os.watchpid(pid, 0); + assert(ret, 0); assert(pid >= 0); os.kill(pid, os.SIGTERM); + //os.sleep(1); [ret, status] = os.waitpid(pid, 0); assert(ret, pid); // Flaky on cygwin for unclear reasons, see @@ -293,7 +379,8 @@ test_file2(); test_getline(); test_popen(); test_os(); -!isWin && test_os_exec(); +if (!isWin) test_os_exec(); +else test_win_os_exec(); test_interval(); test_timeout(); test_timeout_order(); From ba9e4c4e80208ff78ccd02ce0174e67d8768a207 Mon Sep 17 00:00:00 2001 From: woodtygr Date: Thu, 29 May 2025 16:14:38 -0400 Subject: [PATCH 04/19] Add files via upload --- tests/test_std.js | 95 ++--------------------------------------------- 1 file changed, 4 insertions(+), 91 deletions(-) diff --git a/tests/test_std.js b/tests/test_std.js index 207f39ed6..32648ef57 100644 --- a/tests/test_std.js +++ b/tests/test_std.js @@ -161,19 +161,16 @@ function test_os() assert(files.indexOf(fname) >= 0); fdate = 10000; - + err = os.utimes(fpath, fdate, fdate); assert(err, 0); [st, err] = os.stat(fpath); assert(err, 0); assert(st.mode & os.S_IFMT, os.S_IFREG); - + assert(st.mtime, fdate); if (!isWin) { - // moved - assert(st.mtime, fdate); - err = os.symlink(fname, link_path); assert(err, 0); @@ -186,7 +183,6 @@ function test_os() assert(buf, fname); assert(os.remove(link_path) === 0); - } [buf, err] = os.getcwd(); @@ -205,80 +201,6 @@ function test_os() assert(os.remove(fdir) === 0); } -function test_os_pipe() { - - var fds, fr, fw, ab; - fds = os.pipe(); -// console.log("got: " + fds[0] + " " + fds[1]); - fw = std.fdopen(fds[1], "w"); - fr = std.fdopen(fds[0], "r"); - const tstr = "this is a test\r"; - fw.puts(tstr + "\n"); -// fw.puts("this is slow!\r\n"); - fw.puts("\r\n"); - fw.flush(); -// fw.close(); - var barray = new Uint8Array(64); -// ab = fr.read(barray.buffer, 0, 64); -// console.log(ab); -// console.log(fr.getline()); -// console.log(fr.getline()); - ab = fr.getline(); - assert(ab.length, tstr.length); - fw.close(); - fr.close(); -}; - -test_os_pipe(); - -function test_win_os_exec() { - var ret, fds, pid, f, status; - -/* functionality just isn't the same in win32! - in fact, in MSYS the command prompt behaves differently. - I did manage all of this with FDS and PIDS and not handles - hello/r is an artifact of the getline unix emulation - I made the executive decision so that this isn't useless: - watchpid and kill should be made compatible on all systems. - my os.kill is just a terminateprocess call. it returns early. - watchpid is too fast for the kill to complete in VC + NT - at least we can monitor status of a script and execute programs. -*/ - ret = os.exec(["smeegul desgpglam golum"]); - assert(ret, 1); - - ret = os.exec(["echo test 0"]); - assert(ret, 0); - - ret = os.exec(["cmd.exe", "/c", "exit 2"], { usePath: false }); - assert(ret, 2); - - fds = os.pipe(); - pid = os.exec(["echo %FOO%"], { - stdout: fds[1], - block: false, - env: { FOO: "hello" }, - } ); - assert(pid >= 0); - os.close(fds[1]); /* close the write end (as it is only in the child) */ - f = std.fdopen(fds[0], "r"); - var gl = f.getline(); - assert(gl, "hello\r"); - assert(f.getline(), null); - f.close(); - ret = os.watchpid(pid, 1); - assert(ret, pid); - pid = os.exec(["cat"], { block: false } ); - assert(pid >= 0); - os.kill(pid, os.SIGTERM); - os.sleep(1); - ret = os.watchpid(pid, 0); - assert(ret, pid); - // notes from before...... - // Flaky on cygwin for unclear reasons, see - // https://github.com/quickjs-ng/quickjs/issues/184 -}; - function test_os_exec() { var ret, fds, pid, f, status; @@ -301,22 +223,14 @@ function test_os_exec() assert(f.getline(), "hello"); assert(f.getline(), null); f.close(); - /* [ret, status] = os.waitpid(pid, 0); assert(ret, pid); assert(status & 0x7f, 0); /* exited */ - //assert(status >> 8, 0); /* exit code */ - - // added to test watchpid (for win32 compatible code) - ret = os.watchpid(pid, 1); - assert(ret, pid); + assert(status >> 8, 0); /* exit code */ pid = os.exec(["cat"], { block: false } ); - ret = os.watchpid(pid, 0); - assert(ret, 0); assert(pid >= 0); os.kill(pid, os.SIGTERM); - //os.sleep(1); [ret, status] = os.waitpid(pid, 0); assert(ret, pid); // Flaky on cygwin for unclear reasons, see @@ -379,8 +293,7 @@ test_file2(); test_getline(); test_popen(); test_os(); -if (!isWin) test_os_exec(); -else test_win_os_exec(); +!isWin && test_os_exec(); test_interval(); test_timeout(); test_timeout_order(); From 3d43bdb6da13a87d5cec9aea12a5aab599d4e48f Mon Sep 17 00:00:00 2001 From: woodtygr Date: Fri, 30 May 2025 16:42:58 -0400 Subject: [PATCH 05/19] Update test_std.js I put this back like it was. Changes will be split into smaller parts and I've gotten confused when permissions went back and forth --- test_std.js | 95 +++-------------------------------------------------- 1 file changed, 4 insertions(+), 91 deletions(-) diff --git a/test_std.js b/test_std.js index 207f39ed6..32648ef57 100644 --- a/test_std.js +++ b/test_std.js @@ -161,19 +161,16 @@ function test_os() assert(files.indexOf(fname) >= 0); fdate = 10000; - + err = os.utimes(fpath, fdate, fdate); assert(err, 0); [st, err] = os.stat(fpath); assert(err, 0); assert(st.mode & os.S_IFMT, os.S_IFREG); - + assert(st.mtime, fdate); if (!isWin) { - // moved - assert(st.mtime, fdate); - err = os.symlink(fname, link_path); assert(err, 0); @@ -186,7 +183,6 @@ function test_os() assert(buf, fname); assert(os.remove(link_path) === 0); - } [buf, err] = os.getcwd(); @@ -205,80 +201,6 @@ function test_os() assert(os.remove(fdir) === 0); } -function test_os_pipe() { - - var fds, fr, fw, ab; - fds = os.pipe(); -// console.log("got: " + fds[0] + " " + fds[1]); - fw = std.fdopen(fds[1], "w"); - fr = std.fdopen(fds[0], "r"); - const tstr = "this is a test\r"; - fw.puts(tstr + "\n"); -// fw.puts("this is slow!\r\n"); - fw.puts("\r\n"); - fw.flush(); -// fw.close(); - var barray = new Uint8Array(64); -// ab = fr.read(barray.buffer, 0, 64); -// console.log(ab); -// console.log(fr.getline()); -// console.log(fr.getline()); - ab = fr.getline(); - assert(ab.length, tstr.length); - fw.close(); - fr.close(); -}; - -test_os_pipe(); - -function test_win_os_exec() { - var ret, fds, pid, f, status; - -/* functionality just isn't the same in win32! - in fact, in MSYS the command prompt behaves differently. - I did manage all of this with FDS and PIDS and not handles - hello/r is an artifact of the getline unix emulation - I made the executive decision so that this isn't useless: - watchpid and kill should be made compatible on all systems. - my os.kill is just a terminateprocess call. it returns early. - watchpid is too fast for the kill to complete in VC + NT - at least we can monitor status of a script and execute programs. -*/ - ret = os.exec(["smeegul desgpglam golum"]); - assert(ret, 1); - - ret = os.exec(["echo test 0"]); - assert(ret, 0); - - ret = os.exec(["cmd.exe", "/c", "exit 2"], { usePath: false }); - assert(ret, 2); - - fds = os.pipe(); - pid = os.exec(["echo %FOO%"], { - stdout: fds[1], - block: false, - env: { FOO: "hello" }, - } ); - assert(pid >= 0); - os.close(fds[1]); /* close the write end (as it is only in the child) */ - f = std.fdopen(fds[0], "r"); - var gl = f.getline(); - assert(gl, "hello\r"); - assert(f.getline(), null); - f.close(); - ret = os.watchpid(pid, 1); - assert(ret, pid); - pid = os.exec(["cat"], { block: false } ); - assert(pid >= 0); - os.kill(pid, os.SIGTERM); - os.sleep(1); - ret = os.watchpid(pid, 0); - assert(ret, pid); - // notes from before...... - // Flaky on cygwin for unclear reasons, see - // https://github.com/quickjs-ng/quickjs/issues/184 -}; - function test_os_exec() { var ret, fds, pid, f, status; @@ -301,22 +223,14 @@ function test_os_exec() assert(f.getline(), "hello"); assert(f.getline(), null); f.close(); - /* [ret, status] = os.waitpid(pid, 0); assert(ret, pid); assert(status & 0x7f, 0); /* exited */ - //assert(status >> 8, 0); /* exit code */ - - // added to test watchpid (for win32 compatible code) - ret = os.watchpid(pid, 1); - assert(ret, pid); + assert(status >> 8, 0); /* exit code */ pid = os.exec(["cat"], { block: false } ); - ret = os.watchpid(pid, 0); - assert(ret, 0); assert(pid >= 0); os.kill(pid, os.SIGTERM); - //os.sleep(1); [ret, status] = os.waitpid(pid, 0); assert(ret, pid); // Flaky on cygwin for unclear reasons, see @@ -379,8 +293,7 @@ test_file2(); test_getline(); test_popen(); test_os(); -if (!isWin) test_os_exec(); -else test_win_os_exec(); +!isWin && test_os_exec(); test_interval(); test_timeout(); test_timeout_order(); From a5395d3cb39bc543a45664bb9acf8fc7324bc80d Mon Sep 17 00:00:00 2001 From: woodtygr Date: Fri, 30 May 2025 17:03:06 -0400 Subject: [PATCH 06/19] Delete test_std.js getting back to original codebase --- test_std.js | 300 ---------------------------------------------------- 1 file changed, 300 deletions(-) delete mode 100644 test_std.js diff --git a/test_std.js b/test_std.js deleted file mode 100644 index 32648ef57..000000000 --- a/test_std.js +++ /dev/null @@ -1,300 +0,0 @@ -import * as std from "qjs:std"; -import * as os from "qjs:os"; -import { assert } from "./assert.js"; - -const isWin = os.platform === 'win32'; -const isCygwin = os.platform === 'cygwin'; - - -function test_printf() -{ - assert(std.sprintf("a=%d s=%s", 123, "abc"), "a=123 s=abc"); - assert(std.sprintf("%010d", 123), "0000000123"); - assert(std.sprintf("%x", -2), "fffffffe"); - assert(std.sprintf("%lx", -2), "fffffffffffffffe"); - assert(std.sprintf("%10.1f", 2.1), " 2.1"); - assert(std.sprintf("%*.*f", 10, 2, -2.13), " -2.13"); - assert(std.sprintf("%#lx", 0x7fffffffffffffffn), "0x7fffffffffffffff"); -} - -function test_file1() -{ - var f, len, str, size, buf, ret, i, str1, ab; - - f = std.tmpfile(); - str = "hello world\n"; - f.puts(str); - - f.seek(0, std.SEEK_SET); - ab = f.readAsArrayBuffer(); - assert([...new Uint8Array(ab)], str.split("").map(c => c.charCodeAt(0))); - - f.seek(0, std.SEEK_SET); - str1 = f.readAsString(); - assert(str1, str); - - f.seek(0, std.SEEK_END); - size = f.tell(); - assert(size, str.length); - - f.seek(0, std.SEEK_SET); - - buf = new Uint8Array(size); - ret = f.read(buf.buffer, 0, size); - assert(ret, size); - for(i = 0; i < size; i++) - assert(buf[i], str.charCodeAt(i)); - - f.close(); -} - -function test_file2() -{ - var f, str, i, size; - f = std.tmpfile(); - str = "hello world\n"; - size = str.length; - for(i = 0; i < size; i++) - f.putByte(str.charCodeAt(i)); - f.seek(0, std.SEEK_SET); - for(i = 0; i < size; i++) { - assert(str.charCodeAt(i), f.getByte()); - } - assert(f.getByte(), -1); - f.close(); -} - -function test_getline() -{ - var f, line, line_count, lines, i; - - lines = ["hello world", "line 1", "line 2" ]; - f = std.tmpfile(); - for(i = 0; i < lines.length; i++) { - f.puts(lines[i], "\n"); - } - - f.seek(0, std.SEEK_SET); - assert(!f.eof()); - line_count = 0; - for(;;) { - line = f.getline(); - if (line === null) - break; - assert(line, lines[line_count]); - line_count++; - } - assert(f.eof()); - assert(line_count, lines.length); - - f.close(); -} - -function test_popen() -{ - var str, f, fname = "tmp_file.txt"; - var ta, content = "hello world"; - var cmd = isWin ? "type" : "cat"; - - ta = new Uint8Array([...content].map(c => c.charCodeAt(0))); - std.writeFile(fname, ta); - assert(std.loadFile(fname), content); - std.writeFile(fname, ta.buffer); - assert(std.loadFile(fname), content); - std.writeFile(fname, content); - assert(std.loadFile(fname), content); - - /* execute shell command */ - f = std.popen(cmd + " " + fname, "r"); - str = f.readAsString(); - f.close(); - - assert(str, content); - - os.remove(fname); -} - -function test_os() -{ - var fd, fpath, fname, fdir, buf, buf2, i, files, err, fdate, st, link_path; - - // XXX(bnoordhuis) disabled because stdio is not a tty on CI - //assert(os.isatty(0)); - - fdir = "test_tmp_dir"; - fname = "tmp_file.txt"; - fpath = fdir + "/" + fname; - link_path = fdir + "/test_link"; - - os.remove(link_path); - os.remove(fpath); - os.remove(fdir); - - err = os.mkdir(fdir, 0o755); - assert(err, 0); - - fd = os.open(fpath, os.O_RDWR | os.O_CREAT | os.O_TRUNC); - assert(fd >= 0); - - buf = new Uint8Array(10); - for(i = 0; i < buf.length; i++) - buf[i] = i; - assert(os.write(fd, buf.buffer, 0, buf.length), buf.length); - - assert(os.seek(fd, 0, std.SEEK_SET), 0); - buf2 = new Uint8Array(buf.length); - assert(os.read(fd, buf2.buffer, 0, buf2.length), buf2.length); - - for(i = 0; i < buf.length; i++) - assert(buf[i] == buf2[i]); - - if (typeof BigInt !== "undefined") { - assert(os.seek(fd, BigInt(6), std.SEEK_SET), BigInt(6)); - assert(os.read(fd, buf2.buffer, 0, 1), 1); - assert(buf[6] == buf2[0]); - } - - assert(os.close(fd), 0); - - [files, err] = os.readdir(fdir); - assert(err, 0); - assert(files.indexOf(fname) >= 0); - - fdate = 10000; - - err = os.utimes(fpath, fdate, fdate); - assert(err, 0); - - [st, err] = os.stat(fpath); - assert(err, 0); - assert(st.mode & os.S_IFMT, os.S_IFREG); - assert(st.mtime, fdate); - - if (!isWin) { - err = os.symlink(fname, link_path); - assert(err, 0); - - [st, err] = os.lstat(link_path); - assert(err, 0); - assert(st.mode & os.S_IFMT, os.S_IFLNK); - - [buf, err] = os.readlink(link_path); - assert(err, 0); - assert(buf, fname); - - assert(os.remove(link_path) === 0); - } - - [buf, err] = os.getcwd(); - assert(err, 0); - - [buf2, err] = os.realpath("."); - assert(err, 0); - - assert(buf, buf2); - - assert(os.remove(fpath) === 0); - - fd = os.open(fpath, os.O_RDONLY); - assert(fd < 0); - - assert(os.remove(fdir) === 0); -} - -function test_os_exec() -{ - var ret, fds, pid, f, status; - - ret = os.exec(["true"]); - assert(ret, 0); - - ret = os.exec(["/bin/sh", "-c", "exit 1"], { usePath: false }); - assert(ret, 1); - - fds = os.pipe(); - pid = os.exec(["sh", "-c", "echo $FOO"], { - stdout: fds[1], - block: false, - env: { FOO: "hello" }, - } ); - assert(pid >= 0); - os.close(fds[1]); /* close the write end (as it is only in the child) */ - f = std.fdopen(fds[0], "r"); - assert(f.getline(), "hello"); - assert(f.getline(), null); - f.close(); - [ret, status] = os.waitpid(pid, 0); - assert(ret, pid); - assert(status & 0x7f, 0); /* exited */ - assert(status >> 8, 0); /* exit code */ - - pid = os.exec(["cat"], { block: false } ); - assert(pid >= 0); - os.kill(pid, os.SIGTERM); - [ret, status] = os.waitpid(pid, 0); - assert(ret, pid); - // Flaky on cygwin for unclear reasons, see - // https://github.com/quickjs-ng/quickjs/issues/184 - if (!isCygwin) { - assert(status & 0x7f, os.SIGTERM); - } -} - -function test_interval() -{ - var t = os.setInterval(f, 1); - function f() { - if (++f.count === 3) os.clearInterval(t); - } - f.count = 0; -} - -function test_timeout() -{ - var th, i; - - /* just test that a timer can be inserted and removed */ - th = []; - for(i = 0; i < 3; i++) - th[i] = os.setTimeout(function () { }, 1000); - for(i = 0; i < 3; i++) - os.clearTimeout(th[i]); -} - -function test_timeout_order() -{ - var s = ""; - os.setTimeout(a, 0); - os.setTimeout(b, 100); - os.setTimeout(d, 700); - function a() { s += "a"; os.setTimeout(c, 300); } - function b() { s += "b"; } - function c() { s += "c"; } - function d() { assert(s, "abc"); } // not "acb" -} - -function test_stdio_close() -{ - for (const f of [std.in, std.out, std.err]) { - let caught = false; - try { - f.close(); - } catch (e) { - assert(/cannot close stdio/.test(e.message)); - caught = true; - } - assert(caught); - } -} - -test_printf(); -test_file1(); -test_file2(); -test_getline(); -test_popen(); -test_os(); -!isWin && test_os_exec(); -test_interval(); -test_timeout(); -test_timeout_order(); -test_stdio_close(); From e7e1a1fa8940d509992a9b295388e3a029666df2 Mon Sep 17 00:00:00 2001 From: woodtygr Date: Fri, 30 May 2025 21:54:30 -0400 Subject: [PATCH 07/19] Update quickjs-libc.c modifies std_tmpfile to work properly with MINGW (I think MSVC will work okay) std_file finalizer modified creates os_symlink for win32 --- quickjs-libc.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 6 deletions(-) diff --git a/quickjs-libc.c b/quickjs-libc.c index d08ff7f74..572bd3772 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -1034,6 +1034,15 @@ typedef struct { bool is_popen; } JSSTDFile; +#if defined(__MINGW32__) || defined(__MINGW64__) +typedef struct { + FILE *f; + int is_kind; + char filename[64]; +} JSTMPFile; +#endif + + static bool is_stdio(FILE *f) { return f == stdin || f == stdout || f == stderr; @@ -1044,17 +1053,26 @@ static void js_std_file_finalizer(JSRuntime *rt, JSValueConst val) JSThreadState *ts = js_get_thread_state(rt); JSSTDFile *s = JS_GetOpaque(val, ts->std_file_class_id); if (s) { +#if defined(__MINGW32__) || defined(__MINGW64__) + JSTMPFile *ss = (JSTMPFile*) s; + if (ss->is_kind==2) { + if (ss->f) fclose(ss->f); + if (ss->filename[0] != 0) remove(ss->filename); + } else if (s->f && !is_stdio(s->f)) { +#else if (s->f && !is_stdio(s->f)) { +#endif #if !defined(__wasi__) if (s->is_popen) pclose(s->f); else #endif fclose(s->f); + } js_free_rt(rt, s); - } -} + }; +}; static ssize_t js_get_errno(ssize_t ret) { @@ -1207,6 +1225,62 @@ static JSValue js_std_fdopen(JSContext *ctx, JSValueConst this_val, } #if !defined(__wasi__) +#if defined(__MINGW32__) || defined(__MINGW64__) +// c:/tmp no longer works in windows. c:/ doesn't work. permissions! +static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = js_get_thread_state(rt); + JSTMPFile *s; + JSValue obj; + obj = JS_NewObjectClass(ctx, ts->std_file_class_id); + if (JS_IsException(obj)) + return obj; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + + char * env = getenv("TMP"); + if (!env) env = getenv("TEMP"); + int i = 0; + if (env) { + while (env[i]) { + s->filename[i] = env[i]; + i++; + if (i > 50) return JS_NULL; + }; + }; + char* fname = &s->filename[i]; + char* templ = "\\qXXXXXXX"; + while (templ[0]) { + fname[0] = templ[0]; + fname++; templ++; + }; + fname[0] = 0; + int mkf = mkstemp(s->filename); + if (mkf == -1) { + JS_FreeValue(ctx, obj); + js_free(ctx, s); + return JS_NULL; + }; + int fd = dup(mkf); + s->f = fdopen( fd, "a+"); + close(mkf); + if (argc >= 1) js_set_error_object(ctx, argv[0], s->f ? 0 : errno); + if (!s->f) { + JS_FreeValue(ctx, obj); + js_free(ctx, s); + return JS_NULL; + }; + + s->is_kind = 2; + JS_SetOpaque(obj, s); + return obj; +} +#else // MINGW static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -1218,7 +1292,8 @@ static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, return JS_NULL; return js_new_std_file(ctx, f, false); } -#endif +#endif // !MINGW +#endif // WASI static JSValue js_std_sprintf(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) @@ -3055,9 +3130,7 @@ static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val, } return make_string_error(ctx, buf, err); } -#endif -#if !defined(_WIN32) && !defined(__wasi__) static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -3072,12 +3145,27 @@ static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, JS_FreeCString(ctx, target); return JS_EXCEPTION; } +#ifdef _WIN32 + int isdirflag = 0; // might need to pass a value in for folders. + if (argc > 2) { + if (JS_ToInt32(ctx, &isdirflag, argv[2])) return JS_EXCEPTION; + }; + printf("symbolic link: %s -> %s; isdir: %d", target, linkpath, isdirflag); + // 2 = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE + err = CreateSymbolicLinkA(linkpath, target, ( isdirflag | 2 ) ) ; + printf(" returned %d\r\n", err); + if (!err) err = GetLastError(); + else err = 0; +#else err = js_get_errno(symlink(target, linkpath)); +#endif JS_FreeCString(ctx, target); JS_FreeCString(ctx, linkpath); return JS_NewInt32(ctx, err); } +#endif +#if !defined(_WIN32) && !defined(__wasi__) /* return [path, errorcode] */ static JSValue js_os_readlink(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) @@ -4113,10 +4201,10 @@ static const JSCFunctionListEntry js_os_funcs[] = { JS_CFUNC_DEF("sleep", 1, js_os_sleep ), #if !defined(__wasi__) JS_CFUNC_DEF("realpath", 1, js_os_realpath ), + JS_CFUNC_DEF("symlink", 2, js_os_symlink ), #endif #if !defined(_WIN32) && !defined(__wasi__) JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ), - JS_CFUNC_DEF("symlink", 2, js_os_symlink ), JS_CFUNC_DEF("readlink", 1, js_os_readlink ), JS_CFUNC_DEF("exec", 1, js_os_exec ), JS_CFUNC_DEF("getpid", 0, js_os_getpid ), From 18baa918b7695a0aad9b1964e361755ce1deb8ea Mon Sep 17 00:00:00 2001 From: woodtygr Date: Fri, 30 May 2025 21:56:36 -0400 Subject: [PATCH 08/19] Update test_std.js moved a time issue out of WIN32 section. I moved os.symlink out as well and commented, you need admin rights or a setting to execute. test works now with these changes in Windows 11. --- tests/test_std.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/test_std.js b/tests/test_std.js index 32648ef57..c914fb92b 100644 --- a/tests/test_std.js +++ b/tests/test_std.js @@ -168,11 +168,19 @@ function test_os() [st, err] = os.stat(fpath); assert(err, 0); assert(st.mode & os.S_IFMT, os.S_IFREG); - assert(st.mtime, fdate); + if (!isWin) { + + // I bet this can be fixed in windows 10 + assert(st.mtime, fdate); + + /* symlink in windows 10+ requres admin or SeCreateSymbolicLinkPrivilege privilege found under: + Computer Configuration\Windows Settings\Security Settings\Local Policies\User Rights Assignment\ + */ err = os.symlink(fname, link_path); assert(err, 0); + [st, err] = os.lstat(link_path); assert(err, 0); @@ -298,3 +306,5 @@ test_interval(); test_timeout(); test_timeout_order(); test_stdio_close(); + +std.exit(0); From 11e0daa0f3da94414bb421bdeffea7c723e15316 Mon Sep 17 00:00:00 2001 From: woodtygr Date: Mon, 2 Jun 2025 01:22:14 -0400 Subject: [PATCH 09/19] took out some comments ... okay, sorry, I took out more than comments. I had some printf functions left in there! I think that warrants some testing, that I didn't see that printf! The reason I didn't see that printf was that windows 10 requires admin rights to test symlinks, and I have it disabled in the test script. I've left a documented flag in test_std.js and properly tested this again. MING64, CLANG64, MSVC, ClangCL with VC Build --- quickjs-libc.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/quickjs-libc.c b/quickjs-libc.c index 572bd3772..eaef09b69 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -1071,8 +1071,8 @@ static void js_std_file_finalizer(JSRuntime *rt, JSValueConst val) } js_free_rt(rt, s); - }; -}; + } +} static ssize_t js_get_errno(ssize_t ret) { @@ -1226,7 +1226,6 @@ static JSValue js_std_fdopen(JSContext *ctx, JSValueConst this_val, #if !defined(__wasi__) #if defined(__MINGW32__) || defined(__MINGW64__) -// c:/tmp no longer works in windows. c:/ doesn't work. permissions! static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -1235,7 +1234,7 @@ static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, JSTMPFile *s; JSValue obj; obj = JS_NewObjectClass(ctx, ts->std_file_class_id); - if (JS_IsException(obj)) + if (JS_IsException(obj)) return obj; s = js_mallocz(ctx, sizeof(*s)); if (!s) { @@ -1244,38 +1243,40 @@ static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, } char * env = getenv("TMP"); - if (!env) env = getenv("TEMP"); + if (!env) + env = getenv("TEMP"); int i = 0; if (env) { while (env[i]) { s->filename[i] = env[i]; i++; - if (i > 50) return JS_NULL; - }; - }; + if (i > 50) + return JS_NULL; + } + } char* fname = &s->filename[i]; char* templ = "\\qXXXXXXX"; while (templ[0]) { fname[0] = templ[0]; fname++; templ++; - }; + } fname[0] = 0; int mkf = mkstemp(s->filename); if (mkf == -1) { JS_FreeValue(ctx, obj); js_free(ctx, s); return JS_NULL; - }; + } int fd = dup(mkf); s->f = fdopen( fd, "a+"); close(mkf); - if (argc >= 1) js_set_error_object(ctx, argv[0], s->f ? 0 : errno); + if (argc >= 1) + js_set_error_object(ctx, argv[0], s->f ? 0 : errno); if (!s->f) { JS_FreeValue(ctx, obj); js_free(ctx, s); return JS_NULL; - }; - + } s->is_kind = 2; JS_SetOpaque(obj, s); return obj; @@ -3130,7 +3131,8 @@ static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val, } return make_string_error(ctx, buf, err); } - +/* in WIN32: (0 | err) os.symlink(target, linkpath, bool isDirectory) + @ msdn: linkpath and target have reversed meaning! */ static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -3146,14 +3148,12 @@ static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; } #ifdef _WIN32 - int isdirflag = 0; // might need to pass a value in for folders. + int isdirflag = 0; if (argc > 2) { if (JS_ToInt32(ctx, &isdirflag, argv[2])) return JS_EXCEPTION; - }; - printf("symbolic link: %s -> %s; isdir: %d", target, linkpath, isdirflag); + } // 2 = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE - err = CreateSymbolicLinkA(linkpath, target, ( isdirflag | 2 ) ) ; - printf(" returned %d\r\n", err); + err = CreateSymbolicLinkA(linkpath, target, ( 2 ) ) ; if (!err) err = GetLastError(); else err = 0; #else From e402f19e6c26b5e8fd068493001f5de745f2e298 Mon Sep 17 00:00:00 2001 From: woodtygr Date: Mon, 2 Jun 2025 01:31:18 -0400 Subject: [PATCH 10/19] fixed comments and proper symlink test flag const IS_WIN_ADMIN_TEST_FLAG = 0; modified so that someone can run as admin with this flag on and actually test symlink. comment above that clarified that "this code is broken". - That's not my code, my code makes the majority of the std_file finish executing. I just can't readlink from windows , and I can't do that mtime. ...... (but I don't need metric time, I'm not a scientist! --- tests/test_std.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/tests/test_std.js b/tests/test_std.js index c914fb92b..a9795a55e 100644 --- a/tests/test_std.js +++ b/tests/test_std.js @@ -169,18 +169,17 @@ function test_os() assert(err, 0); assert(st.mode & os.S_IFMT, os.S_IFREG); - - if (!isWin) { - - // I bet this can be fixed in windows 10 + if (!isWin) // returns some negative value in windows 11. had to remove this on my build. (CAP) assert(st.mtime, fdate); /* symlink in windows 10+ requres admin or SeCreateSymbolicLinkPrivilege privilege found under: Computer Configuration\Windows Settings\Security Settings\Local Policies\User Rights Assignment\ */ + const IS_WIN_ADMIN_TEST_FLAG = 0; + if (!isWin) { + err = os.symlink(fname, link_path); assert(err, 0); - [st, err] = os.lstat(link_path); assert(err, 0); @@ -191,6 +190,18 @@ function test_os() assert(buf, fname); assert(os.remove(link_path) === 0); + + } else if (IS_WIN_ADMIN_TEST_FLAG) { + + err = os.symlink(fname, link_path); + assert(err, 0); + + [st, err] = os.lstat(link_path); + assert(err, 0); + assert(st.mode & os.S_IFMT, os.S_IFLNK); + + assert(os.remove(link_path) === 0); + } [buf, err] = os.getcwd(); From e91b646d616097d2abf1257762e35eeeaeb3348a Mon Sep 17 00:00:00 2001 From: woodtygr Date: Mon, 2 Jun 2025 01:41:29 -0400 Subject: [PATCH 11/19] Update quickjs-libc.c *space* --- quickjs-libc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/quickjs-libc.c b/quickjs-libc.c index eaef09b69..5692c9632 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -1042,7 +1042,6 @@ typedef struct { } JSTMPFile; #endif - static bool is_stdio(FILE *f) { return f == stdin || f == stdout || f == stderr; From 330df2ac76b727980ec0c89af3071cca54032062 Mon Sep 17 00:00:00 2001 From: woodtygr Date: Mon, 2 Jun 2025 01:43:36 -0400 Subject: [PATCH 12/19] Update quickjs-libc.c ... --- quickjs-libc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickjs-libc.c b/quickjs-libc.c index 5692c9632..d5e73bd53 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -3131,7 +3131,7 @@ static JSValue js_os_realpath(JSContext *ctx, JSValueConst this_val, return make_string_error(ctx, buf, err); } /* in WIN32: (0 | err) os.symlink(target, linkpath, bool isDirectory) - @ msdn: linkpath and target have reversed meaning! */ + @ msdn: linkpath and target have reversed meaning than symlink! */ static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { From 7b68bffb68b7921515076311ca3c20b190a9ff76 Mon Sep 17 00:00:00 2001 From: woodtygr Date: Tue, 3 Jun 2025 10:43:41 -0400 Subject: [PATCH 13/19] Update test_std.js --- tests/test_std.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_std.js b/tests/test_std.js index a9795a55e..00eb55648 100644 --- a/tests/test_std.js +++ b/tests/test_std.js @@ -318,4 +318,3 @@ test_timeout(); test_timeout_order(); test_stdio_close(); -std.exit(0); From 4c04f212dc93bd5e651045d13af574d0375b9607 Mon Sep 17 00:00:00 2001 From: woodtygr Date: Tue, 3 Jun 2025 13:26:02 -0400 Subject: [PATCH 14/19] Update quickjs-libc.c altercation: MINGW64 ... no longer MINGW32 no longer duping the tmpfile handle. that was because of some other function and it's not needed. improved os_tmpfile names of variables for clarity. JSTMPFile stores a js_mallocz'd buffer instead. frees buffer. enumerated JSSTDFileKind kind; --- quickjs-libc.c | 89 +++++++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/quickjs-libc.c b/quickjs-libc.c index d5e73bd53..65e69b054 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -1034,11 +1034,12 @@ typedef struct { bool is_popen; } JSSTDFile; -#if defined(__MINGW32__) || defined(__MINGW64__) +#if defined(__MINGW64__) +enum JSSTDFileKind { STDFile, STDPipe, TMPFile }; typedef struct { FILE *f; - int is_kind; - char filename[64]; + enum JSSTDFileKind kind; + char *filename; } JSTMPFile; #endif @@ -1052,11 +1053,12 @@ static void js_std_file_finalizer(JSRuntime *rt, JSValueConst val) JSThreadState *ts = js_get_thread_state(rt); JSSTDFile *s = JS_GetOpaque(val, ts->std_file_class_id); if (s) { -#if defined(__MINGW32__) || defined(__MINGW64__) +#if defined(__MINGW64__) JSTMPFile *ss = (JSTMPFile*) s; - if (ss->is_kind==2) { + if (ss->kind == TMPFile) { if (ss->f) fclose(ss->f); - if (ss->filename[0] != 0) remove(ss->filename); + if (ss->filename != 0) remove(ss->filename); + js_free_rt(rt, ss->filename); } else if (s->f && !is_stdio(s->f)) { #else if (s->f && !is_stdio(s->f)) { @@ -1067,7 +1069,7 @@ static void js_std_file_finalizer(JSRuntime *rt, JSValueConst val) else #endif fclose(s->f); - + } js_free_rt(rt, s); } @@ -1224,7 +1226,7 @@ static JSValue js_std_fdopen(JSContext *ctx, JSValueConst this_val, } #if !defined(__wasi__) -#if defined(__MINGW32__) || defined(__MINGW64__) +#if defined(__MINGW64__) static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -1232,53 +1234,52 @@ static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, JSThreadState *ts = js_get_thread_state(rt); JSTMPFile *s; JSValue obj; + char* fn_template, *env_tmp, *fn_buff; + int mk_fd, fn_len; + obj = JS_NewObjectClass(ctx, ts->std_file_class_id); if (JS_IsException(obj)) - return obj; + return JS_EXCEPTION; s = js_mallocz(ctx, sizeof(*s)); if (!s) { JS_FreeValue(ctx, obj); return JS_EXCEPTION; } - - char * env = getenv("TMP"); - if (!env) - env = getenv("TEMP"); - int i = 0; - if (env) { - while (env[i]) { - s->filename[i] = env[i]; - i++; - if (i > 50) - return JS_NULL; - } - } - char* fname = &s->filename[i]; - char* templ = "\\qXXXXXXX"; - while (templ[0]) { - fname[0] = templ[0]; - fname++; templ++; - } - fname[0] = 0; - int mkf = mkstemp(s->filename); - if (mkf == -1) { - JS_FreeValue(ctx, obj); - js_free(ctx, s); - return JS_NULL; - } - int fd = dup(mkf); - s->f = fdopen( fd, "a+"); - close(mkf); + fn_template = "\\qXXXXXXX"; + env_tmp = getenv("TMP"); + if (!env_tmp) + env_tmp = getenv("TEMP"); + if (env_tmp) { + fn_len = strlen(env_tmp); + fn_buff = js_malloc(ctx, fn_len + 10); + memcpy(fn_buff, env_tmp, fn_len); + memcpy(fn_buff + fn_len, fn_template, 10); + } else { + fn_buff = js_mallocz(ctx, 10); + memcpy(fn_buff, fn_template, 10); + } + mk_fd = mkstemp(fn_buff); + if (mk_fd == -1) { + JS_ThrowInternalError(ctx, "tmpfile failed to create file with error: %d.", errno); + goto file_failure; + }; + //int fd = dup(mkf); + s->f = fdopen( mk_fd, "a+"); + if (s->f == 0) { + JS_ThrowInternalError(ctx, "tmpfile failed to open file with error: %d.", errno); + goto file_failure; + }; + s->filename = fn_buff; + s->kind = TMPFile; if (argc >= 1) js_set_error_object(ctx, argv[0], s->f ? 0 : errno); - if (!s->f) { - JS_FreeValue(ctx, obj); - js_free(ctx, s); - return JS_NULL; - } - s->is_kind = 2; JS_SetOpaque(obj, s); return obj; + file_failure: + JS_FreeValue(ctx, obj); + js_free(ctx, s); + js_free(ctx, fn_buff); + return JS_EXCEPTION; } #else // MINGW static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, From f29e7b50e9969b1ce9048a6ab77d2aecb957096c Mon Sep 17 00:00:00 2001 From: woodtygr Date: Tue, 3 Jun 2025 13:43:20 -0400 Subject: [PATCH 15/19] Update quickjs-libc.c fixed flag: SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE tested --- quickjs-libc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/quickjs-libc.c b/quickjs-libc.c index 65e69b054..ede2479c5 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -3147,13 +3147,13 @@ static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, JS_FreeCString(ctx, target); return JS_EXCEPTION; } -#ifdef _WIN32 +#if defined(_WIN32) int isdirflag = 0; if (argc > 2) { if (JS_ToInt32(ctx, &isdirflag, argv[2])) return JS_EXCEPTION; } - // 2 = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE - err = CreateSymbolicLinkA(linkpath, target, ( 2 ) ) ; + err = CreateSymbolicLinkA(linkpath, target, ( isdirflag | + SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE ) ) ; if (!err) err = GetLastError(); else err = 0; #else From 8d8b524ae71ecc4d64f58ffdc9ff80f4ef69bad5 Mon Sep 17 00:00:00 2001 From: woodtygr Date: Wed, 4 Jun 2025 06:32:09 -0400 Subject: [PATCH 16/19] Update test_std.js - no lstat I have to manually test this change unless we add a detect user or detect group function. Win32 has no lstat. Moved the comment and admin flag to the top for ease of use until someone comes up with a better solution. --- tests/test_std.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/test_std.js b/tests/test_std.js index 00eb55648..83295ee01 100644 --- a/tests/test_std.js +++ b/tests/test_std.js @@ -5,6 +5,10 @@ import { assert } from "./assert.js"; const isWin = os.platform === 'win32'; const isCygwin = os.platform === 'cygwin'; +/* symlink in windows 10+ requres admin or SeCreateSymbolicLinkPrivilege privilege found under: + Computer Configuration\Windows Settings\Security Settings\Local Policies\User Rights Assignment\ + set IS_WIN_ADMIN_TEST_FLAG to 1 and run as Administrator to test win32 os.symlink */ +const IS_WIN_ADMIN_TEST_FLAG = 0; function test_printf() { @@ -172,10 +176,6 @@ function test_os() if (!isWin) // returns some negative value in windows 11. had to remove this on my build. (CAP) assert(st.mtime, fdate); - /* symlink in windows 10+ requres admin or SeCreateSymbolicLinkPrivilege privilege found under: - Computer Configuration\Windows Settings\Security Settings\Local Policies\User Rights Assignment\ - */ - const IS_WIN_ADMIN_TEST_FLAG = 0; if (!isWin) { err = os.symlink(fname, link_path); @@ -196,10 +196,6 @@ function test_os() err = os.symlink(fname, link_path); assert(err, 0); - [st, err] = os.lstat(link_path); - assert(err, 0); - assert(st.mode & os.S_IFMT, os.S_IFLNK); - assert(os.remove(link_path) === 0); } From ec315383f8c9db018e9c509a3c4dfef3da578d3a Mon Sep 17 00:00:00 2001 From: woodtygr Date: Thu, 5 Jun 2025 14:41:54 -0400 Subject: [PATCH 17/19] not admin in win32 just skip symlink tests. removed extra line. win32 symlink fails on 1314 because admin rights required. removed flag. --- tests/test_std.js | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/tests/test_std.js b/tests/test_std.js index 83295ee01..60927fe45 100644 --- a/tests/test_std.js +++ b/tests/test_std.js @@ -5,11 +5,6 @@ import { assert } from "./assert.js"; const isWin = os.platform === 'win32'; const isCygwin = os.platform === 'cygwin'; -/* symlink in windows 10+ requres admin or SeCreateSymbolicLinkPrivilege privilege found under: - Computer Configuration\Windows Settings\Security Settings\Local Policies\User Rights Assignment\ - set IS_WIN_ADMIN_TEST_FLAG to 1 and run as Administrator to test win32 os.symlink */ -const IS_WIN_ADMIN_TEST_FLAG = 0; - function test_printf() { assert(std.sprintf("a=%d s=%s", 123, "abc"), "a=123 s=abc"); @@ -164,7 +159,7 @@ function test_os() assert(err, 0); assert(files.indexOf(fname) >= 0); - fdate = 10000; + fdate = 86400000; err = os.utimes(fpath, fdate, fdate); assert(err, 0); @@ -172,13 +167,11 @@ function test_os() [st, err] = os.stat(fpath); assert(err, 0); assert(st.mode & os.S_IFMT, os.S_IFREG); + assert(st.mtime, fdate); - if (!isWin) // returns some negative value in windows 11. had to remove this on my build. (CAP) - assert(st.mtime, fdate); + err = os.symlink(fname, link_path); if (!isWin) { - - err = os.symlink(fname, link_path); assert(err, 0); [st, err] = os.lstat(link_path); @@ -190,14 +183,10 @@ function test_os() assert(buf, fname); assert(os.remove(link_path) === 0); - - } else if (IS_WIN_ADMIN_TEST_FLAG) { - - err = os.symlink(fname, link_path); + + } else if (err != 1314) { assert(err, 0); - assert(os.remove(link_path) === 0); - } [buf, err] = os.getcwd(); @@ -313,4 +302,3 @@ test_interval(); test_timeout(); test_timeout_order(); test_stdio_close(); - From 68ec8720a0b4ba1b0a6b119379200f98fcc3bdeb Mon Sep 17 00:00:00 2001 From: woodtygr Date: Thu, 5 Jun 2025 15:23:14 -0400 Subject: [PATCH 18/19] tested and cleaned up cleaned up js_os_tmpfile cleaned up finalizer --- quickjs-libc.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/quickjs-libc.c b/quickjs-libc.c index ede2479c5..1f4e66097 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -1059,10 +1059,10 @@ static void js_std_file_finalizer(JSRuntime *rt, JSValueConst val) if (ss->f) fclose(ss->f); if (ss->filename != 0) remove(ss->filename); js_free_rt(rt, ss->filename); - } else if (s->f && !is_stdio(s->f)) { -#else - if (s->f && !is_stdio(s->f)) { + return; + }; #endif + if (s->f && !is_stdio(s->f)) { #if !defined(__wasi__) if (s->is_popen) pclose(s->f); @@ -1234,8 +1234,8 @@ static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, JSThreadState *ts = js_get_thread_state(rt); JSTMPFile *s; JSValue obj; - char* fn_template, *env_tmp, *fn_buff; - int mk_fd, fn_len; + char *env_tmp, *fn_buff; + int mk_fd, path_len, fn_template_len, fn_total_len; obj = JS_NewObjectClass(ctx, ts->std_file_class_id); if (JS_IsException(obj)) @@ -1245,28 +1245,33 @@ static JSValue js_std_tmpfile(JSContext *ctx, JSValueConst this_val, JS_FreeValue(ctx, obj); return JS_EXCEPTION; } - fn_template = "\\qXXXXXXX"; + static char* fn_template = "qXXXXXXX"; + fn_template_len = strlen(fn_template) + 1; + fn_total_len = fn_template_len; + path_len = 0; env_tmp = getenv("TMP"); if (!env_tmp) env_tmp = getenv("TEMP"); if (env_tmp) { - fn_len = strlen(env_tmp); - fn_buff = js_malloc(ctx, fn_len + 10); - memcpy(fn_buff, env_tmp, fn_len); - memcpy(fn_buff + fn_len, fn_template, 10); - } else { - fn_buff = js_mallocz(ctx, 10); - memcpy(fn_buff, fn_template, 10); + path_len = strlen(env_tmp); + fn_total_len = path_len + 1 + fn_template_len; } + fn_buff = js_malloc(ctx, path_len + fn_total_len); + if (env_tmp) { + memcpy(fn_buff, env_tmp, path_len); + fn_buff[path_len] = '\\'; + path_len++; + } + memcpy(fn_buff + path_len, fn_template, fn_template_len); mk_fd = mkstemp(fn_buff); if (mk_fd == -1) { JS_ThrowInternalError(ctx, "tmpfile failed to create file with error: %d.", errno); goto file_failure; }; - //int fd = dup(mkf); s->f = fdopen( mk_fd, "a+"); - if (s->f == 0) { + if (!s->f) { JS_ThrowInternalError(ctx, "tmpfile failed to open file with error: %d.", errno); + close(mk_fd); goto file_failure; }; s->filename = fn_buff; From 8988eca121e9956d51cfd639d7c460719d49257d Mon Sep 17 00:00:00 2001 From: woodtygr Date: Thu, 5 Jun 2025 15:34:02 -0400 Subject: [PATCH 19/19] formatting fixed indention level --- quickjs-libc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickjs-libc.c b/quickjs-libc.c index 1f4e66097..512057fde 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -3158,7 +3158,7 @@ static JSValue js_os_symlink(JSContext *ctx, JSValueConst this_val, if (JS_ToInt32(ctx, &isdirflag, argv[2])) return JS_EXCEPTION; } err = CreateSymbolicLinkA(linkpath, target, ( isdirflag | - SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE ) ) ; + SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE ) ) ; if (!err) err = GetLastError(); else err = 0; #else