From 2b6856b6928f2e2043888e2dde920195620b2925 Mon Sep 17 00:00:00 2001 From: doylet Date: Thu, 16 Jul 2026 14:16:48 +1000 Subject: [PATCH] Fix DN_OSExec not quoting CLI args on Windows --- Single-Header/dn_single_header.cpp | 74 +++++++++--- Single-Header/dn_single_header.h | 6 +- Source/Extra/dn_seshc.cpp | 183 +++++------------------------ Source/Extra/dn_seshc.h | 9 +- Source/OS/dn_os_w32.cpp | 72 +++++++++--- Source/dn.h | 4 +- 6 files changed, 150 insertions(+), 198 deletions(-) diff --git a/Single-Header/dn_single_header.cpp b/Single-Header/dn_single_header.cpp index f98f527..3564f03 100644 --- a/Single-Header/dn_single_header.cpp +++ b/Single-Header/dn_single_header.cpp @@ -1,4 +1,4 @@ -// Generated by the DN single header generator 2026-07-15 22:47:49 +// Generated by the DN single header generator 2026-07-16 14:16:33 // DN: Single header generator commented out => #if defined(_CLANGD) // #define DN_WITH_TESTS 1 @@ -15440,12 +15440,54 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs if (cmd_line.count == 0) return result; - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - DN_Str8 cmd_rendered = DN_Str8SliceRender(cmd_line, DN_Str8Lit(" "), &scratch.arena); - DN_Str16 cmd16 = DN_OS_W32Str8ToStr16(&scratch.arena, cmd_rendered); - DN_Str16 working_dir16 = DN_OS_W32Str8ToStr16(&scratch.arena, args->working_dir); + // NOTE: Render the command line into single string to pass into the Win32 API. We'll quote the + // string on behalf of the user if there's whitespace in the string + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); + DN_Str8 cmd = {}; + { + DN_USize count_req = 0; + DN_Str8 cmd_separator = DN_Str8Lit(" "); + for (DN_ForIt(it, DN_Str8, &cmd_line)) { + if (it.index) + count_req += cmd_separator.count; - DN_Str8Builder env_builder = DN_Str8BuilderFromArena(&scratch.arena); + if (DN_Str8Find(*it.data, DN_Str8FindFlag_Whitespace).found) + count_req += 2; // If there's whitespace, we'll quote the string for the user + count_req += it.data->count; + } + + // NOTE: Build the string + cmd = DN_Str8AllocArena(count_req, DN_ZMem_No, &scratch.arena); + if (cmd.data) { + DN_USize write_index = 0; + for (DN_ForIt(it, DN_Str8, &cmd_line)) { + if (it.index) { + DN_Memcpy(cmd.data + write_index, cmd_separator.data, cmd_separator.count); + write_index += cmd_separator.count; + } + + // NOTE: Quote the string if needed + DN_Str8 str8 = *it.data; + bool needs_quotes = DN_Str8Find(str8, DN_Str8FindFlag_Whitespace).found; + if (needs_quotes) + cmd.data[write_index++] = '\"'; + + // NOTE: Write contents + DN_Memcpy(cmd.data + write_index, str8.data, str8.count); + write_index += str8.count; + + // NOTE: Close quote if needed + if (needs_quotes) + cmd.data[write_index++] = '\"'; + } + DN_Assert(write_index == cmd.count); + } + } + DN_AssertF(cmd.count < DN_Kilobytes(32), "Command exceeds the allowable size in Win32"); + + DN_Str16 cmd16 = DN_OS_W32Str8ToStr16(&scratch.arena, cmd); + DN_Str16 working_dir16 = DN_OS_W32Str8ToStr16(&scratch.arena, args->working_dir); + DN_Str8Builder env_builder = DN_Str8BuilderFromArena(&scratch.arena); DN_Str8BuilderAppendArrayRef(&env_builder, args->environment.data, args->environment.count); if (env_builder.string_size) DN_Str8BuilderAppendRef(&env_builder, DN_Str8Lit("\0")); @@ -15479,7 +15521,7 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs err, result.os_error_code, "Failed to create stdout pipe to redirect the output of the command '%.*s': %.*s", - DN_Str8PrintFmt(cmd_rendered), + DN_Str8PrintFmt(cmd), DN_Str8PrintFmt(win_error.msg)); DN_TCScratchEnd(&scratch); return result; @@ -15492,14 +15534,14 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs result.os_error_code, "Failed to make stdout 'read' pipe non-inheritable when trying to " "execute command '%.*s': %.*s", - DN_Str8PrintFmt(cmd_rendered), + DN_Str8PrintFmt(cmd), DN_Str8PrintFmt(win_error.msg)); DN_TCScratchEnd(&scratch); return result; } } - // NOTE: Redirect stderr /////////////////////////////////////////////////////////////////////// + // NOTE: Redirect stderr HANDLE stderr_read = {}; HANDLE stderr_write = {}; DN_DEFER @@ -15522,7 +15564,7 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs err, result.os_error_code, "Failed to create stderr pipe to redirect the output of the command '%.*s': %.*s", - DN_Str8PrintFmt(cmd_rendered), + DN_Str8PrintFmt(cmd), DN_Str8PrintFmt(win_error.msg)); DN_TCScratchEnd(&scratch); return result; @@ -15532,11 +15574,11 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena); result.os_error_code = win_error.code; DN_ErrSinkAppendF(err, - result.os_error_code, - "Failed to make stderr 'read' pipe non-inheritable when trying to " - "execute command '%.*s': %.*s", - DN_Str8PrintFmt(cmd_rendered), - DN_Str8PrintFmt(win_error.msg)); + result.os_error_code, + "Failed to make stderr 'read' pipe non-inheritable when trying to " + "execute command '%.*s': %.*s", + DN_Str8PrintFmt(cmd), + DN_Str8PrintFmt(win_error.msg)); DN_TCScratchEnd(&scratch); return result; } @@ -15564,7 +15606,7 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs if (!create_result) { DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena); result.os_error_code = win_error.code; - DN_ErrSinkAppendF(err, result.os_error_code, "Failed to execute command '%.*s': %.*s", DN_Str8PrintFmt(cmd_rendered), DN_Str8PrintFmt(win_error.msg)); + DN_ErrSinkAppendF(err, result.os_error_code, "Failed to execute command '%.*s': %.*s", DN_Str8PrintFmt(cmd), DN_Str8PrintFmt(win_error.msg)); DN_TCScratchEnd(&scratch); return result; } diff --git a/Single-Header/dn_single_header.h b/Single-Header/dn_single_header.h index 699b3c7..a9c8bca 100644 --- a/Single-Header/dn_single_header.h +++ b/Single-Header/dn_single_header.h @@ -1,4 +1,4 @@ -// Generated by the DN single header generator 2026-07-15 22:47:49 +// Generated by the DN single header generator 2026-07-16 14:16:33 #if !defined(DN_H) #define DN_H @@ -4317,8 +4317,8 @@ typedef struct DN_OSExecAsyncHandle DN_OSExecAsyncHandle; struct DN_OSExecAsyncHandle { DN_OSExecFlags exec_flags; - DN_U32 os_error_code; - DN_U32 exit_code; + DN_U32 os_error_code; + DN_U32 exit_code; void *process; void *stdout_read; void *stdout_write; diff --git a/Source/Extra/dn_seshc.cpp b/Source/Extra/dn_seshc.cpp index fe6de5e..3a9313e 100644 --- a/Source/Extra/dn_seshc.cpp +++ b/Source/Extra/dn_seshc.cpp @@ -1,81 +1,5 @@ #include "dn_seshc.h" -struct DN_SHSendDMThreadContext_ -{ - DN_Arena arena; - DN_SHCLIArgs args; - DN_Str8 sesh_pkey_hex; - DN_Str8 msg; -}; - -void DN_SH_LogBroadcast(DN_LogType type, DN_SHCLIArgs *cli_args, DN_Str8 short_desc, char const *fmt, ...) -{ - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - va_list args; - va_start(args, fmt); - DN_Str8 msg = DN_Str8FromFmtVArena(&scratch.arena, fmt, args); - va_end(args); - DN_Str8 prefix = {}; - switch (type) { - case DN_LogType_Debug: prefix = DN_Str8Lit("🐞 "); break; - case DN_LogType_Info: prefix = DN_Str8Lit("â„šī¸ "); break; - case DN_LogType_Warning: prefix = DN_Str8Lit("âš ī¸ "); break; - case DN_LogType_Error: prefix = DN_Str8Lit("🚨 "); break; - case DN_LogType_Count: break; - } - DN_LogF(type, "%.*s%.*s", DN_Str8PrintFmt(prefix), DN_Str8PrintFmt(msg)); - - DN_Date date = DN_OS_DateLocalTimeNow(); - - // NOTE: 12-hour conversion - DN_U8 hour12 = date.hour % 12; - if (hour12 == 0) - hour12 = 12; - char const *am_pm = date.hour < 12 ? "AM" : "PM"; - - // Time-of-day emoji - DN_Str8 time_emoji = {}; - if (date.hour >= 5 && date.hour <= 11) - time_emoji = DN_Str8Lit("â˜€ī¸"); - else if (date.hour >= 12 && date.hour <= 16) - time_emoji = DN_Str8Lit("đŸŒ¤ī¸"); - else if (date.hour >= 17 && date.hour <= 19) - time_emoji = DN_Str8Lit("🌅"); - else if (date.hour >= 20 && date.hour <= 23) - time_emoji = DN_Str8Lit("✨"); - else - time_emoji = DN_Str8Lit("🌑"); - DN_SH_CLIArgsSendDMDetachedFmt(cli_args, - cli_args->recipient_pkey, - "%.*s %.*s\n" - "%.*s %u-%02u-%02u %u:%02u:%02u %s\n" - "%.*s", - DN_Str8PrintFmt(prefix), - DN_Str8PrintFmt(short_desc), - DN_Str8PrintFmt(time_emoji), - date.year, - date.month, - date.day, - hour12, - date.minutes, - date.seconds, - am_pm, - DN_Str8PrintFmt(msg)); - DN_TCScratchEnd(&scratch); -} - -DN_SHCLIArgs DN_SH_CLIArgsCopy(DN_SHCLIArgs const *src, DN_Arena *arena) -{ - DN_SHCLIArgs result = {}; - result = *src; - result.exe_path = DN_Str8FromStr8Arena(src->exe_path, arena); - result.account_secret = DN_Str8FromStr8Arena(src->account_secret, arena); - result.seed_node_url = DN_Str8FromStr8Arena(src->seed_node_url, arena); - result.display_name = DN_Str8FromStr8Arena(src->display_name, arena); - result.recipient_pkey = DN_Str8FromStr8Arena(src->recipient_pkey, arena); - return result; -} - DN_Str8 DN_SH_CLIOptionsStr8() { DN_Str8 result = DN_Str8Lit( @@ -153,92 +77,39 @@ DN_SHCLIEatArgResult DN_SH_CLIEatArg(DN_SHCLIArgs *args, char const **arg_ptr, i return result; } -bool DN_SH_CLIArgsSendDMBlockingFmt(DN_SHCLIArgs *cli_args, DN_Str8 sesh_pkey_hex, char const *fmt, ...) +bool DN_SH_CLIArgsSendDMFmt(DN_SHCLIArgs *cli_args, DN_Str8 sesh_pkey_hex, char const *fmt, ...) { - bool result = false; - if (cli_args->valid) { - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - - va_list args; - va_start(args, fmt); - DN_Str8 msg = DN_Str8FromFmtVArena(&scratch.arena, fmt, args); - va_end(args); - - // NOTE: Escape quotes and quote the input strings - DN_Str8 display_name = cli_args->display_name; - display_name = DN_Str8ReplaceSensitive(display_name, DN_Str8Lit("\""), DN_Str8Lit("\\\""), 0, &scratch.arena); - display_name = DN_Str8FromFmtArena(&scratch.arena, "\"%.*s\"", DN_Str8PrintFmt(display_name)); - - msg = DN_Str8ReplaceSensitive(msg, DN_Str8Lit("\""), DN_Str8Lit("\\\""), 0, &scratch.arena); - msg = DN_Str8FromFmtArena(&scratch.arena, "\"%.*s\"", DN_Str8PrintFmt(msg)); - - DN_Str8 cmds[] = { - cli_args->exe_path, - DN_Str8Lit("--account-secret"), - cli_args->account_secret, - DN_Str8Lit("--seed-node-url"), - cli_args->seed_node_url, - DN_Str8Lit("--display-name"), - display_name, - DN_Str8Lit("send"), - sesh_pkey_hex, - msg, - }; - - DN_Str8Slice cmd_line = {}; - cmd_line.data = cmds; - cmd_line.count = DN_ArrayCountU(cmds); - - DN_OSExecArgs exec_args = {}; - exec_args.flags |= DN_OSExecFlags_SaveOutput; - - DN_OSExecResult exec = DN_OS_Exec(cmd_line, &exec_args, &scratch.arena, nullptr); - result = exec.os_error_code == 0 && exec.exit_code == 0; - if (!result) { - DN_LogErrorF("Error sending message to %.*s (exit code: %d, os exit code: %d): %.*s", - DN_Str8PrintFmt(cli_args->recipient_pkey), - exec.exit_code, - exec.os_error_code, - DN_Str8PrintFmt(exec.stderr_text)); - } - DN_TCScratchEnd(&scratch); - - } - return result; -} - -static int DN_SH_CLIArgsSendDMDetachedFmtThreadFunc_(DN_OSThread *thread) -{ - DN_SHSendDMThreadContext_ *context = DN_Cast(DN_SHSendDMThreadContext_ *) thread->user_context; - DN_SH_CLIArgsSendDMBlockingFmt(&context->args, context->sesh_pkey_hex, "%.*s", DN_Str8PrintFmt(context->msg)); - DN_ArenaDeinit(&context->arena); - return 0; -} - -void DN_SH_CLIArgsSendDMDetachedFmt(DN_SHCLIArgs *cli_args, DN_Str8 sesh_pkey_hex, char const *fmt, ...) -{ - if (!cli_args->valid) - return; - - // NOTE: Setup context - DN_Arena arena = DN_OS_ArenaFromHeapVirtual(0, 0, DN_MemFlags_Nil); - DN_SHSendDMThreadContext_ *context = DN_ArenaNewZ(&arena, DN_SHSendDMThreadContext_); - context->args = DN_SH_CLIArgsCopy(cli_args, &arena); - context->sesh_pkey_hex = DN_Str8FromStr8Arena(sesh_pkey_hex, &arena); + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); va_list args; va_start(args, fmt); - context->msg = DN_Str8FromFmtVArena(&arena, fmt, args); + DN_Str8 msg = DN_Str8FromFmtVArena(&scratch.arena, fmt, args); va_end(args); - // NOTE: Set the arena last after all allocations - context->arena = arena; + DN_Str8 cmds[] = { + cli_args->exe_path, + DN_Str8Lit("--account-secret"), + cli_args->account_secret, + DN_Str8Lit("--seed-node-url"), + cli_args->seed_node_url, + DN_Str8Lit("--display-name"), + DN_Str8FromFmtArena(&scratch.arena, "\"%.*s\"", DN_Str8PrintFmt(cli_args->display_name)), + DN_Str8Lit("send"), + sesh_pkey_hex, + msg, + }; - // NOTE: Setup thread to be detached - DN_OSThread thread = {}; - DN_OSThreadInitArgs thread_args = DN_OS_ThreadInitArgsDefault(); - thread_args.flags |= DN_OSThreadFlags_Detached; + DN_Str8Slice cmd_line = {}; + cmd_line.data = cmds; + cmd_line.count = DN_ArrayCountU(cmds); - // NOTE: Execute and forget - DN_OS_ThreadInit(&thread, DN_SH_CLIArgsSendDMDetachedFmtThreadFunc_, thread_args, context); + DN_OSExecArgs exec_args = {}; + exec_args.flags |= DN_OSExecFlags_SaveOutput; + DN_OSExecResult exec = DN_OS_Exec(cmd_line, &exec_args, &scratch.arena, nullptr); + DN_LogInfoF("OUT (exit code:%zu, os: %zu): %.*s", exec.exit_code, exec.os_error_code, DN_Str8PrintFmt(exec.stdout_text)); + DN_LogInfoF("ERR: %.*s", DN_Str8PrintFmt(exec.stderr_text)); + DN_TCScratchEnd(&scratch); + + bool result = exec.os_error_code == 0 && exec.exit_code == 0; + return result; } diff --git a/Source/Extra/dn_seshc.h b/Source/Extra/dn_seshc.h index b803e05..1001b82 100644 --- a/Source/Extra/dn_seshc.h +++ b/Source/Extra/dn_seshc.h @@ -29,11 +29,8 @@ struct DN_SHCLIEatArgResult // Overview // Helper library that provides some useful APIs for programatically calling the Seshc CLI // executable to send messages from your C/C++ application. -void DN_SH_LogBroadcast (DN_LogType type, DN_SHCLIArgs *cli_args, DN_Str8 short_desc, char const *fmt, ...); -DN_SHCLIArgs DN_SH_CLIArgsCopy (DN_SHCLIArgs const *src, DN_Arena *arena); -DN_Str8 DN_SH_CLIOptionsStr8 (); -DN_SHCLIEatArgResult DN_SH_CLIEatArg (DN_SHCLIArgs *args, char const **arg_ptr, int arg_count); -bool DN_SH_CLIArgsSendDMBlockingFmt(DN_SHCLIArgs *cli_args, DN_Str8 sesh_pkey_hex, char const *fmt, ...); -void DN_SH_CLIArgsSendDMDetachedFmt(DN_SHCLIArgs *cli_args, DN_Str8 sesh_pkey_hex, char const *fmt, ...); +DN_Str8 DN_SH_CLIOptionsStr8 (); +DN_SHCLIEatArgResult DN_SH_CLIEatArg (DN_SHCLIArgs *args, char const **arg_ptr, int arg_count); +bool DN_SH_CLIArgsSendDMFmt(DN_SHCLIArgs *cli_args, DN_Str8 sesh_pkey_hex, char const *fmt, ...); #endif // #if !defined(DN_SESHC_H) diff --git a/Source/OS/dn_os_w32.cpp b/Source/OS/dn_os_w32.cpp index 9cc7bb3..ab52f82 100644 --- a/Source/OS/dn_os_w32.cpp +++ b/Source/OS/dn_os_w32.cpp @@ -854,12 +854,54 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs if (cmd_line.count == 0) return result; - DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); - DN_Str8 cmd_rendered = DN_Str8SliceRender(cmd_line, DN_Str8Lit(" "), &scratch.arena); - DN_Str16 cmd16 = DN_OS_W32Str8ToStr16(&scratch.arena, cmd_rendered); - DN_Str16 working_dir16 = DN_OS_W32Str8ToStr16(&scratch.arena, args->working_dir); + // NOTE: Render the command line into single string to pass into the Win32 API. We'll quote the + // string on behalf of the user if there's whitespace in the string + DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); + DN_Str8 cmd = {}; + { + DN_USize count_req = 0; + DN_Str8 cmd_separator = DN_Str8Lit(" "); + for (DN_ForIt(it, DN_Str8, &cmd_line)) { + if (it.index) + count_req += cmd_separator.count; - DN_Str8Builder env_builder = DN_Str8BuilderFromArena(&scratch.arena); + if (DN_Str8Find(*it.data, DN_Str8FindFlag_Whitespace).found) + count_req += 2; // If there's whitespace, we'll quote the string for the user + count_req += it.data->count; + } + + // NOTE: Build the string + cmd = DN_Str8AllocArena(count_req, DN_ZMem_No, &scratch.arena); + if (cmd.data) { + DN_USize write_index = 0; + for (DN_ForIt(it, DN_Str8, &cmd_line)) { + if (it.index) { + DN_Memcpy(cmd.data + write_index, cmd_separator.data, cmd_separator.count); + write_index += cmd_separator.count; + } + + // NOTE: Quote the string if needed + DN_Str8 str8 = *it.data; + bool needs_quotes = DN_Str8Find(str8, DN_Str8FindFlag_Whitespace).found; + if (needs_quotes) + cmd.data[write_index++] = '\"'; + + // NOTE: Write contents + DN_Memcpy(cmd.data + write_index, str8.data, str8.count); + write_index += str8.count; + + // NOTE: Close quote if needed + if (needs_quotes) + cmd.data[write_index++] = '\"'; + } + DN_Assert(write_index == cmd.count); + } + } + DN_AssertF(cmd.count < DN_Kilobytes(32), "Command exceeds the allowable size in Win32"); + + DN_Str16 cmd16 = DN_OS_W32Str8ToStr16(&scratch.arena, cmd); + DN_Str16 working_dir16 = DN_OS_W32Str8ToStr16(&scratch.arena, args->working_dir); + DN_Str8Builder env_builder = DN_Str8BuilderFromArena(&scratch.arena); DN_Str8BuilderAppendArrayRef(&env_builder, args->environment.data, args->environment.count); if (env_builder.string_size) DN_Str8BuilderAppendRef(&env_builder, DN_Str8Lit("\0")); @@ -893,7 +935,7 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs err, result.os_error_code, "Failed to create stdout pipe to redirect the output of the command '%.*s': %.*s", - DN_Str8PrintFmt(cmd_rendered), + DN_Str8PrintFmt(cmd), DN_Str8PrintFmt(win_error.msg)); DN_TCScratchEnd(&scratch); return result; @@ -906,14 +948,14 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs result.os_error_code, "Failed to make stdout 'read' pipe non-inheritable when trying to " "execute command '%.*s': %.*s", - DN_Str8PrintFmt(cmd_rendered), + DN_Str8PrintFmt(cmd), DN_Str8PrintFmt(win_error.msg)); DN_TCScratchEnd(&scratch); return result; } } - // NOTE: Redirect stderr /////////////////////////////////////////////////////////////////////// + // NOTE: Redirect stderr HANDLE stderr_read = {}; HANDLE stderr_write = {}; DN_DEFER @@ -936,7 +978,7 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs err, result.os_error_code, "Failed to create stderr pipe to redirect the output of the command '%.*s': %.*s", - DN_Str8PrintFmt(cmd_rendered), + DN_Str8PrintFmt(cmd), DN_Str8PrintFmt(win_error.msg)); DN_TCScratchEnd(&scratch); return result; @@ -946,11 +988,11 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena); result.os_error_code = win_error.code; DN_ErrSinkAppendF(err, - result.os_error_code, - "Failed to make stderr 'read' pipe non-inheritable when trying to " - "execute command '%.*s': %.*s", - DN_Str8PrintFmt(cmd_rendered), - DN_Str8PrintFmt(win_error.msg)); + result.os_error_code, + "Failed to make stderr 'read' pipe non-inheritable when trying to " + "execute command '%.*s': %.*s", + DN_Str8PrintFmt(cmd), + DN_Str8PrintFmt(win_error.msg)); DN_TCScratchEnd(&scratch); return result; } @@ -978,7 +1020,7 @@ DN_API DN_OSExecAsyncHandle DN_OS_ExecAsync(DN_Str8Slice cmd_line, DN_OSExecArgs if (!create_result) { DN_OSW32Error win_error = DN_OS_W32LastError(&scratch.arena); result.os_error_code = win_error.code; - DN_ErrSinkAppendF(err, result.os_error_code, "Failed to execute command '%.*s': %.*s", DN_Str8PrintFmt(cmd_rendered), DN_Str8PrintFmt(win_error.msg)); + DN_ErrSinkAppendF(err, result.os_error_code, "Failed to execute command '%.*s': %.*s", DN_Str8PrintFmt(cmd), DN_Str8PrintFmt(win_error.msg)); DN_TCScratchEnd(&scratch); return result; } diff --git a/Source/dn.h b/Source/dn.h index ef0476a..9f3a58b 100644 --- a/Source/dn.h +++ b/Source/dn.h @@ -2387,8 +2387,8 @@ typedef struct DN_OSExecAsyncHandle DN_OSExecAsyncHandle; struct DN_OSExecAsyncHandle { DN_OSExecFlags exec_flags; - DN_U32 os_error_code; - DN_U32 exit_code; + DN_U32 os_error_code; + DN_U32 exit_code; void *process; void *stdout_read; void *stdout_write;