From 7f22a27a10655dc40528f35034cbdf8c9543241c Mon Sep 17 00:00:00 2001 From: Ryan Prichard Date: Wed, 24 Feb 2016 02:14:19 -0600 Subject: win: integrate winpty (WIP) Handling of process exit is still broken. It detects the moment when the child process exits, then quickly stops polling for process output. It should continue polling for output until the agent has scraped all of the process' output. This problem is easy to notice by running a command like "dir && exit", but even typing "exit" can manifest the problem -- the "t" might not appear. winpty's Cygwin adapter handles shutdown by waiting for the agent to close the CONOUT pipe, which it does after it has scraped the child's last output. AFAIK, neovim doesn't do anything interesting when winpty closes the CONOUT pipe. --- src/nvim/os/pty_process_win.c | 189 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 src/nvim/os/pty_process_win.c (limited to 'src/nvim/os/pty_process_win.c') diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c new file mode 100644 index 0000000000..e75c92e7fb --- /dev/null +++ b/src/nvim/os/pty_process_win.c @@ -0,0 +1,189 @@ +#include +#include +#include + +#include "nvim/memory.h" +#include "nvim/os/pty_process_win.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/pty_process_win.c.generated.h" +#endif + +static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) +{ + uv_async_t *finish_async = (uv_async_t *)context; + uv_async_send(finish_async); +} + +bool pty_process_spawn(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL +{ + Process *proc = (Process *)ptyproc; + bool success = false; + winpty_error_ptr_t err = NULL; + winpty_config_t *cfg = NULL; + winpty_spawn_config_t *spawncfg = NULL; + winpty_t *wp = NULL; + char *in_name = NULL, *out_name = NULL; + HANDLE process_handle = NULL; + + assert(proc->in && proc->out && !proc->err); + + if (!(cfg = winpty_config_new( + WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err))) { + goto cleanup; + } + winpty_config_set_initial_size(cfg, ptyproc->width, ptyproc->height); + + if (!(wp = winpty_open(cfg, &err))) { + goto cleanup; + } + + in_name = utf16_to_utf8(winpty_conin_name(wp)); + out_name = utf16_to_utf8(winpty_conout_name(wp)); + uv_pipe_connect( + xmalloc(sizeof(uv_connect_t)), + &proc->in->uv.pipe, + in_name, + pty_process_connect_cb); + uv_pipe_connect( + xmalloc(sizeof(uv_connect_t)), + &proc->out->uv.pipe, + out_name, + pty_process_connect_cb); + + // XXX: Provide the correct ptyprocess parameters (at least, the cmdline... + // probably cwd too? what about environ?) + if (!(spawncfg = winpty_spawn_config_new( + WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, + L"C:\\Windows\\System32\\cmd.exe", + L"C:\\Windows\\System32\\cmd.exe", + NULL, NULL, + &err))) { + goto cleanup; + } + if (!winpty_spawn(wp, spawncfg, &process_handle, NULL, NULL, &err)) { + goto cleanup; + } + + uv_async_init(&proc->loop->uv, &ptyproc->finish_async, pty_process_finish2); + if (!RegisterWaitForSingleObject(&ptyproc->finish_wait, process_handle, + pty_process_finish1, &ptyproc->finish_async, INFINITE, 0)) { + abort(); + } + + ptyproc->wp = wp; + ptyproc->process_handle = process_handle; + wp = NULL; + process_handle = NULL; + success = true; + +cleanup: + winpty_error_free(err); + winpty_config_free(cfg); + winpty_spawn_config_free(spawncfg); + winpty_free(wp); + xfree(in_name); + xfree(out_name); + if (process_handle != NULL) { + CloseHandle(process_handle); + } + return success; +} + +void pty_process_resize(PtyProcess *ptyproc, uint16_t width, + uint16_t height) + FUNC_ATTR_NONNULL_ALL +{ + if (ptyproc->wp != NULL) { + winpty_set_size(ptyproc->wp, width, height, NULL); + } +} + +void pty_process_close(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL +{ + Process *proc = (Process *)ptyproc; + + ptyproc->is_closing = true; + pty_process_close_master(ptyproc); + + uv_handle_t *finish_async_handle = (uv_handle_t *)&ptyproc->finish_async; + if (ptyproc->finish_wait != NULL) { + // Use INVALID_HANDLE_VALUE to block until either the wait is cancelled + // or the callback has signalled the uv_async_t. + UnregisterWaitEx(ptyproc->finish_wait, INVALID_HANDLE_VALUE); + uv_close(finish_async_handle, pty_process_finish_closing); + } else { + pty_process_finish_closing(finish_async_handle); + } +} + +void pty_process_close_master(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL +{ + if (ptyproc->wp != NULL) { + winpty_free(ptyproc->wp); + ptyproc->wp = NULL; + } +} + +void pty_process_teardown(Loop *loop) + FUNC_ATTR_NONNULL_ALL +{ +} + +// Returns a string freeable with xfree. Never returns NULL (OOM is a fatal +// error). Windows appears to replace invalid UTF-16 code points (i.e. +// unpaired surrogates) using U+FFFD (the replacement character). +static char *utf16_to_utf8(LPCWSTR str) + FUNC_ATTR_NONNULL_ALL +{ + int len = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); + assert(len >= 1); // Even L"" has a non-zero length due to NUL terminator. + char *ret = xmalloc(len); + int len2 = WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, len, NULL, NULL); + assert(len == len2); + return ret; +} + +static void pty_process_connect_cb(uv_connect_t *req, int status) +{ + assert(status == 0); + xfree(req); +} + +static void pty_process_finish2(uv_async_t *finish_async) +{ + PtyProcess *ptyproc = + (PtyProcess *)((char *)finish_async - offsetof(PtyProcess, finish_async)); + Process *proc = (Process *)ptyproc; + + if (!ptyproc->is_closing) { + // If pty_process_close has already been called, be consistent and never + // call the internal_exit callback. + + DWORD exit_code = 0; + GetExitCodeProcess(ptyproc->process_handle, &exit_code); + proc->status = exit_code; + + if (proc->internal_exit_cb) { + proc->internal_exit_cb(proc); + } + } +} + +static void pty_process_finish_closing(uv_handle_t *finish_async) +{ + PtyProcess *ptyproc = + (PtyProcess *)((char *)finish_async - offsetof(PtyProcess, finish_async)); + Process *proc = (Process *)ptyproc; + + if (ptyproc->process_handle != NULL) { + CloseHandle(ptyproc->process_handle); + ptyproc->process_handle = NULL; + } + if (proc->internal_close_cb) { + proc->internal_close_cb(proc); + } +} -- cgit From 4b1f21de75f9981007d80aca8355239e8615d6bd Mon Sep 17 00:00:00 2001 From: erw7 Date: Tue, 28 Mar 2017 18:07:58 +0900 Subject: win: support :terminal --- src/nvim/os/pty_process_win.c | 232 ++++++++++++++++++++++++++++++------------ 1 file changed, 168 insertions(+), 64 deletions(-) (limited to 'src/nvim/os/pty_process_win.c') diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index e75c92e7fb..ea95c1bb09 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -2,30 +2,53 @@ #include #include +#include "nvim/vim.h" +#include "nvim/ascii.h" #include "nvim/memory.h" +#include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 #include "nvim/os/pty_process_win.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/pty_process_win.c.generated.h" #endif +static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) + FUNC_ATTR_NONNULL_ALL +{ + PtyProcess *ptyproc = + (PtyProcess *)((uv_handle_t *)wait_eof_timer->data); + Process *proc = (Process *)ptyproc; + + if (!uv_is_readable(proc->out->uvstream)) { + uv_timer_stop(&ptyproc->wait_eof_timer); + pty_process_finish2(ptyproc); + } +} + static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) + FUNC_ATTR_NONNULL_ALL { - uv_async_t *finish_async = (uv_async_t *)context; - uv_async_send(finish_async); + PtyProcess *ptyproc = (PtyProcess *)context; + Process *proc = (Process *)ptyproc; + + uv_timer_init(&proc->loop->uv, &ptyproc->wait_eof_timer); + ptyproc->wait_eof_timer.data = (void *)ptyproc; + uv_timer_start(&ptyproc->wait_eof_timer, wait_eof_timer_cb, 200, 200); } -bool pty_process_spawn(PtyProcess *ptyproc) +int pty_process_spawn(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { Process *proc = (Process *)ptyproc; - bool success = false; + int status = 0; winpty_error_ptr_t err = NULL; winpty_config_t *cfg = NULL; winpty_spawn_config_t *spawncfg = NULL; winpty_t *wp = NULL; char *in_name = NULL, *out_name = NULL; HANDLE process_handle = NULL; + uv_connect_t *in_req = NULL, *out_req = NULL; + wchar_t *cmdline = NULL, *cwd = NULL; assert(proc->in && proc->out && !proc->err); @@ -33,52 +56,70 @@ bool pty_process_spawn(PtyProcess *ptyproc) WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err))) { goto cleanup; } - winpty_config_set_initial_size(cfg, ptyproc->width, ptyproc->height); + winpty_config_set_initial_size( + cfg, + ptyproc->width, + ptyproc->height); if (!(wp = winpty_open(cfg, &err))) { goto cleanup; } - in_name = utf16_to_utf8(winpty_conin_name(wp)); - out_name = utf16_to_utf8(winpty_conout_name(wp)); + if ((status = utf16_to_utf8(winpty_conin_name(wp), &in_name))) { + goto cleanup; + } + if ((status = utf16_to_utf8(winpty_conout_name(wp), &out_name))) { + goto cleanup; + } + in_req = xmalloc(sizeof(uv_connect_t)); + out_req = xmalloc(sizeof(uv_connect_t)); uv_pipe_connect( - xmalloc(sizeof(uv_connect_t)), + in_req, &proc->in->uv.pipe, in_name, pty_process_connect_cb); uv_pipe_connect( - xmalloc(sizeof(uv_connect_t)), + out_req, &proc->out->uv.pipe, out_name, pty_process_connect_cb); - // XXX: Provide the correct ptyprocess parameters (at least, the cmdline... - // probably cwd too? what about environ?) + if (proc->cwd != NULL && (status = utf8_to_utf16(proc->cwd, &cwd))) { + goto cleanup; + } + if ((status = build_cmdline(proc->argv, &cmdline))) { + goto cleanup; + } if (!(spawncfg = winpty_spawn_config_new( WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, - L"C:\\Windows\\System32\\cmd.exe", - L"C:\\Windows\\System32\\cmd.exe", - NULL, NULL, - &err))) { + NULL, cmdline, cwd, NULL, &err))) { goto cleanup; } if (!winpty_spawn(wp, spawncfg, &process_handle, NULL, NULL, &err)) { goto cleanup; } + proc->pid = GetProcessId(process_handle); - uv_async_init(&proc->loop->uv, &ptyproc->finish_async, pty_process_finish2); - if (!RegisterWaitForSingleObject(&ptyproc->finish_wait, process_handle, - pty_process_finish1, &ptyproc->finish_async, INFINITE, 0)) { + if (!RegisterWaitForSingleObject( + &ptyproc->finish_wait, + process_handle, pty_process_finish1, ptyproc, + INFINITE, WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE)) { abort(); } + while (in_req->handle || out_req->handle) { + uv_run(&proc->loop->uv, UV_RUN_ONCE); + } + ptyproc->wp = wp; ptyproc->process_handle = process_handle; wp = NULL; process_handle = NULL; - success = true; cleanup: + if (err != NULL) { + status = (int)winpty_error_code(err); + } winpty_error_free(err); winpty_config_free(cfg); winpty_spawn_config_free(spawncfg); @@ -88,7 +129,11 @@ cleanup: if (process_handle != NULL) { CloseHandle(process_handle); } - return success; + xfree(in_req); + xfree(out_req); + xfree(cmdline); + xfree(cwd); + return status; } void pty_process_resize(PtyProcess *ptyproc, uint16_t width, @@ -105,17 +150,10 @@ void pty_process_close(PtyProcess *ptyproc) { Process *proc = (Process *)ptyproc; - ptyproc->is_closing = true; pty_process_close_master(ptyproc); - uv_handle_t *finish_async_handle = (uv_handle_t *)&ptyproc->finish_async; - if (ptyproc->finish_wait != NULL) { - // Use INVALID_HANDLE_VALUE to block until either the wait is cancelled - // or the callback has signalled the uv_async_t. - UnregisterWaitEx(ptyproc->finish_wait, INVALID_HANDLE_VALUE); - uv_close(finish_async_handle, pty_process_finish_closing); - } else { - pty_process_finish_closing(finish_async_handle); + if (proc->internal_close_cb) { + proc->internal_close_cb(proc); } } @@ -133,57 +171,123 @@ void pty_process_teardown(Loop *loop) { } -// Returns a string freeable with xfree. Never returns NULL (OOM is a fatal -// error). Windows appears to replace invalid UTF-16 code points (i.e. -// unpaired surrogates) using U+FFFD (the replacement character). -static char *utf16_to_utf8(LPCWSTR str) - FUNC_ATTR_NONNULL_ALL -{ - int len = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL); - assert(len >= 1); // Even L"" has a non-zero length due to NUL terminator. - char *ret = xmalloc(len); - int len2 = WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, len, NULL, NULL); - assert(len == len2); - return ret; -} - static void pty_process_connect_cb(uv_connect_t *req, int status) + FUNC_ATTR_NONNULL_ALL { assert(status == 0); - xfree(req); + req->handle = NULL; } -static void pty_process_finish2(uv_async_t *finish_async) +static void pty_process_finish2(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL { - PtyProcess *ptyproc = - (PtyProcess *)((char *)finish_async - offsetof(PtyProcess, finish_async)); Process *proc = (Process *)ptyproc; - if (!ptyproc->is_closing) { - // If pty_process_close has already been called, be consistent and never - // call the internal_exit callback. + UnregisterWaitEx(ptyproc->finish_wait, NULL); + uv_close((uv_handle_t *)&ptyproc->wait_eof_timer, NULL); + + DWORD exit_code = 0; + GetExitCodeProcess(ptyproc->process_handle, &exit_code); + proc->status = (int)exit_code; - DWORD exit_code = 0; - GetExitCodeProcess(ptyproc->process_handle, &exit_code); - proc->status = exit_code; + CloseHandle(ptyproc->process_handle); + ptyproc->process_handle = NULL; - if (proc->internal_exit_cb) { - proc->internal_exit_cb(proc); + proc->internal_exit_cb(proc); +} + +static int build_cmdline(char **argv, wchar_t **cmdline) + FUNC_ATTR_NONNULL_ALL +{ + char *args = NULL; + size_t args_len = 0, argc = 0; + int ret; + QUEUE q; + QUEUE_INIT(&q); + + while (*argv) { + arg_T *arg = xmalloc(sizeof(arg_T)); + arg->arg = (char *)xmalloc(strlen(*argv) * 2 + 3); + quote_cmd_arg(arg->arg, *argv); + args_len += strlen(arg->arg); + QUEUE_INIT(&arg->node); + QUEUE_INSERT_TAIL(&q, &arg->node); + argc++; + argv++; + } + args_len += argc; + args = xmalloc(args_len); + *args = NUL; + while (1) { + QUEUE *head = QUEUE_HEAD(&q); + QUEUE_REMOVE(head); + arg_T *arg = QUEUE_DATA(head, arg_T, node); + xstrlcat(args, arg->arg, args_len); + xfree(arg->arg); + xfree(arg); + if (QUEUE_EMPTY(&q)) { + break; + } else { + xstrlcat(args, " ", args_len); } } + ret = utf8_to_utf16(args, cmdline); + xfree(args); + return ret; } -static void pty_process_finish_closing(uv_handle_t *finish_async) +// Emulate quote_cmd_arg of libuv and quotes command line arguments +static void quote_cmd_arg(char *target, const char *source) + FUNC_ATTR_NONNULL_ALL { - PtyProcess *ptyproc = - (PtyProcess *)((char *)finish_async - offsetof(PtyProcess, finish_async)); - Process *proc = (Process *)ptyproc; + size_t len = strlen(source); + size_t i; + bool quote_hit = true; + char *start = target; + char tmp; - if (ptyproc->process_handle != NULL) { - CloseHandle(ptyproc->process_handle); - ptyproc->process_handle = NULL; + if (len == 0) { + *(target++) = '"'; + *(target++) = '"'; + *target = NUL; + return; } - if (proc->internal_close_cb) { - proc->internal_close_cb(proc); + + if (NULL == strpbrk(source, " \t\"")) { + strcpy(target, source); + return; + } + + if (NULL == strpbrk(source, "\"\\")) { + *(target++) = '"'; + strncpy(target, source, len); + target += len; + *(target++) = '"'; + *target = NUL; + return; + } + + *(target++) = NUL; + *(target++) = '"'; + for (i = len; i > 0; --i) { + *(target++) = source[i - 1]; + + if (quote_hit && source[i - 1] == '\\') { + *(target++) = '\\'; + } else if (source[i - 1] == '"') { + quote_hit = true; + *(target++) = '\\'; + } else { + quote_hit = false; + } + } + *target = '"'; + while (start < target) { + tmp = *start; + *start = *target; + *target = tmp; + start++; + target--; } + return; } -- cgit From e635754e8e9a92d1537ccc980c63eaa2e9d732bb Mon Sep 17 00:00:00 2001 From: erw7 Date: Wed, 29 Mar 2017 21:39:58 +0900 Subject: win/pty: jobstart, jobstop: fix null-pointer dereference - Make sure that proc->in is not NULL, because nvim crashed when starting a job with pty. - Make sure that proc->out is not NULL, because nvim crashed when stopping a job opened with pty. --- src/nvim/os/pty_process_win.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) (limited to 'src/nvim/os/pty_process_win.c') diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index ea95c1bb09..101f468005 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -19,7 +19,7 @@ static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) (PtyProcess *)((uv_handle_t *)wait_eof_timer->data); Process *proc = (Process *)ptyproc; - if (!uv_is_readable(proc->out->uvstream)) { + if (!proc->out || !uv_is_readable(proc->out->uvstream)) { uv_timer_stop(&ptyproc->wait_eof_timer); pty_process_finish2(ptyproc); } @@ -50,7 +50,7 @@ int pty_process_spawn(PtyProcess *ptyproc) uv_connect_t *in_req = NULL, *out_req = NULL; wchar_t *cmdline = NULL, *cwd = NULL; - assert(proc->in && proc->out && !proc->err); + assert(!proc->err); if (!(cfg = winpty_config_new( WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err))) { @@ -71,18 +71,22 @@ int pty_process_spawn(PtyProcess *ptyproc) if ((status = utf16_to_utf8(winpty_conout_name(wp), &out_name))) { goto cleanup; } - in_req = xmalloc(sizeof(uv_connect_t)); - out_req = xmalloc(sizeof(uv_connect_t)); - uv_pipe_connect( - in_req, - &proc->in->uv.pipe, - in_name, - pty_process_connect_cb); - uv_pipe_connect( - out_req, - &proc->out->uv.pipe, - out_name, - pty_process_connect_cb); + if (proc->in) { + in_req = xmalloc(sizeof(uv_connect_t)); + uv_pipe_connect( + in_req, + &proc->in->uv.pipe, + in_name, + pty_process_connect_cb); + } + if (proc->out) { + out_req = xmalloc(sizeof(uv_connect_t)); + uv_pipe_connect( + out_req, + &proc->out->uv.pipe, + out_name, + pty_process_connect_cb); + } if (proc->cwd != NULL && (status = utf8_to_utf16(proc->cwd, &cwd))) { goto cleanup; @@ -107,7 +111,7 @@ int pty_process_spawn(PtyProcess *ptyproc) abort(); } - while (in_req->handle || out_req->handle) { + while ((in_req && in_req->handle) || (out_req && out_req->handle)) { uv_run(&proc->loop->uv, UV_RUN_ONCE); } -- cgit From 3b992f16889b45215ab6f867edaec5201776d579 Mon Sep 17 00:00:00 2001 From: erw7 Date: Thu, 30 Mar 2017 18:30:40 +0900 Subject: win/pty: quote_cmd_arg(): check bounds --- src/nvim/os/pty_process_win.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'src/nvim/os/pty_process_win.c') diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index 101f468005..f3efe87d78 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -210,9 +210,10 @@ static int build_cmdline(char **argv, wchar_t **cmdline) QUEUE_INIT(&q); while (*argv) { + size_t buf_len = strlen(*argv) * 2 + 3; arg_T *arg = xmalloc(sizeof(arg_T)); - arg->arg = (char *)xmalloc(strlen(*argv) * 2 + 3); - quote_cmd_arg(arg->arg, *argv); + arg->arg = (char *)xmalloc(buf_len); + quote_cmd_arg(arg->arg, buf_len, *argv); args_len += strlen(arg->arg); QUEUE_INIT(&arg->node); QUEUE_INSERT_TAIL(&q, &arg->node); @@ -241,50 +242,50 @@ static int build_cmdline(char **argv, wchar_t **cmdline) } // Emulate quote_cmd_arg of libuv and quotes command line arguments -static void quote_cmd_arg(char *target, const char *source) +static void quote_cmd_arg(char *target, size_t remain, const char *source) FUNC_ATTR_NONNULL_ALL { - size_t len = strlen(source); + size_t src_len = strlen(source); size_t i; bool quote_hit = true; char *start = target; char tmp; - if (len == 0) { - *(target++) = '"'; - *(target++) = '"'; - *target = NUL; + if (src_len == 0) { + snprintf(target, remain, "\"\""); return; } if (NULL == strpbrk(source, " \t\"")) { - strcpy(target, source); + xstrlcpy(target, source, remain); return; } if (NULL == strpbrk(source, "\"\\")) { - *(target++) = '"'; - strncpy(target, source, len); - target += len; - *(target++) = '"'; - *target = NUL; + snprintf(target, remain, "\"%s\"", source); return; } + assert(remain--); *(target++) = NUL; + assert(remain--); *(target++) = '"'; - for (i = len; i > 0; --i) { + for (i = src_len; i > 0; i--) { + assert(remain--); *(target++) = source[i - 1]; if (quote_hit && source[i - 1] == '\\') { + assert(remain--); *(target++) = '\\'; } else if (source[i - 1] == '"') { quote_hit = true; + assert(remain--); *(target++) = '\\'; } else { quote_hit = false; } } + assert(remain); *target = '"'; while (start < target) { tmp = *start; -- cgit From 84fb794da653fc3bcc2832816ca0cab01b4e0400 Mon Sep 17 00:00:00 2001 From: erw7 Date: Sun, 2 Apr 2017 18:16:39 +0900 Subject: win/pyt: cleanup --- src/nvim/os/pty_process_win.c | 273 +++++++++++++++++++++++++----------------- 1 file changed, 166 insertions(+), 107 deletions(-) (limited to 'src/nvim/os/pty_process_win.c') diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index f3efe87d78..6a08e2868b 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -12,19 +12,6 @@ # include "os/pty_process_win.c.generated.h" #endif -static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) - FUNC_ATTR_NONNULL_ALL -{ - PtyProcess *ptyproc = - (PtyProcess *)((uv_handle_t *)wait_eof_timer->data); - Process *proc = (Process *)ptyproc; - - if (!proc->out || !uv_is_readable(proc->out->uvstream)) { - uv_timer_stop(&ptyproc->wait_eof_timer); - pty_process_finish2(ptyproc); - } -} - static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) FUNC_ATTR_NONNULL_ALL { @@ -36,6 +23,7 @@ static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) uv_timer_start(&ptyproc->wait_eof_timer, wait_eof_timer_cb, 200, 200); } +/// @returns zero on sucess, or error code of winpty or MultiByteToWideChar. int pty_process_spawn(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { @@ -44,34 +32,39 @@ int pty_process_spawn(PtyProcess *ptyproc) winpty_error_ptr_t err = NULL; winpty_config_t *cfg = NULL; winpty_spawn_config_t *spawncfg = NULL; - winpty_t *wp = NULL; - char *in_name = NULL, *out_name = NULL; + winpty_t *winpty_object = NULL; + char *in_name = NULL; + char *out_name = NULL; HANDLE process_handle = NULL; - uv_connect_t *in_req = NULL, *out_req = NULL; - wchar_t *cmdline = NULL, *cwd = NULL; + uv_connect_t *in_req = NULL; + uv_connect_t *out_req = NULL; + wchar_t *cmd_line = NULL; + wchar_t *cwd = NULL; assert(!proc->err); - if (!(cfg = winpty_config_new( - WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err))) { + cfg = winpty_config_new(WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err); + if (cfg == NULL) { goto cleanup; } - winpty_config_set_initial_size( - cfg, - ptyproc->width, - ptyproc->height); - if (!(wp = winpty_open(cfg, &err))) { + winpty_config_set_initial_size(cfg, ptyproc->width, ptyproc->height); + winpty_object = winpty_open(cfg, &err); + if (winpty_object == NULL) { goto cleanup; } - if ((status = utf16_to_utf8(winpty_conin_name(wp), &in_name))) { + status = utf16_to_utf8(winpty_conin_name(winpty_object), &in_name); + if (status != 0) { goto cleanup; } - if ((status = utf16_to_utf8(winpty_conout_name(wp), &out_name))) { + + status = utf16_to_utf8(winpty_conout_name(winpty_object), &out_name); + if (status != 0) { goto cleanup; } - if (proc->in) { + + if (proc->in != NULL) { in_req = xmalloc(sizeof(uv_connect_t)); uv_pipe_connect( in_req, @@ -79,7 +72,8 @@ int pty_process_spawn(PtyProcess *ptyproc) in_name, pty_process_connect_cb); } - if (proc->out) { + + if (proc->out != NULL) { out_req = xmalloc(sizeof(uv_connect_t)); uv_pipe_connect( out_req, @@ -88,46 +82,65 @@ int pty_process_spawn(PtyProcess *ptyproc) pty_process_connect_cb); } - if (proc->cwd != NULL && (status = utf8_to_utf16(proc->cwd, &cwd))) { - goto cleanup; + if (proc->cwd != NULL) { + status = utf8_to_utf16(proc->cwd, &cwd); + if (status != 0) { + goto cleanup; + } } - if ((status = build_cmdline(proc->argv, &cmdline))) { + + status = build_cmd_line(proc->argv, &cmd_line); + if (status != 0) { goto cleanup; } - if (!(spawncfg = winpty_spawn_config_new( + + spawncfg = winpty_spawn_config_new( WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, - NULL, cmdline, cwd, NULL, &err))) { + NULL, // Optional application name + cmd_line, + cwd, + NULL, // Optional environment variables + &err); + if (spawncfg == NULL) { goto cleanup; } - if (!winpty_spawn(wp, spawncfg, &process_handle, NULL, NULL, &err)) { + + if (!winpty_spawn(winpty_object, + spawncfg, + &process_handle, + NULL, // Optional thread handle + NULL, // Optional create process error + &err)) { goto cleanup; } proc->pid = GetProcessId(process_handle); if (!RegisterWaitForSingleObject( &ptyproc->finish_wait, - process_handle, pty_process_finish1, ptyproc, - INFINITE, WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE)) { + process_handle, + pty_process_finish1, + ptyproc, + INFINITE, + WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE)) { abort(); } - while ((in_req && in_req->handle) || (out_req && out_req->handle)) { + // Wait until pty_process_connect_cb is called. + while ((in_req != NULL && in_req->handle != NULL) + || (out_req != NULL && out_req->handle != NULL)) { uv_run(&proc->loop->uv, UV_RUN_ONCE); } - ptyproc->wp = wp; + ptyproc->winpty_object = winpty_object; ptyproc->process_handle = process_handle; - wp = NULL; + winpty_object = NULL; process_handle = NULL; cleanup: - if (err != NULL) { - status = (int)winpty_error_code(err); - } winpty_error_free(err); winpty_config_free(cfg); winpty_spawn_config_free(spawncfg); - winpty_free(wp); + winpty_free(winpty_object); xfree(in_name); xfree(out_name); if (process_handle != NULL) { @@ -135,7 +148,7 @@ cleanup: } xfree(in_req); xfree(out_req); - xfree(cmdline); + xfree(cmd_line); xfree(cwd); return status; } @@ -144,8 +157,8 @@ void pty_process_resize(PtyProcess *ptyproc, uint16_t width, uint16_t height) FUNC_ATTR_NONNULL_ALL { - if (ptyproc->wp != NULL) { - winpty_set_size(ptyproc->wp, width, height, NULL); + if (ptyproc->winpty_object != NULL) { + winpty_set_size(ptyproc->winpty_object, width, height, NULL); } } @@ -164,9 +177,9 @@ void pty_process_close(PtyProcess *ptyproc) void pty_process_close_master(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { - if (ptyproc->wp != NULL) { - winpty_free(ptyproc->wp); - ptyproc->wp = NULL; + if (ptyproc->winpty_object != NULL) { + winpty_free(ptyproc->winpty_object); + ptyproc->winpty_object = NULL; } } @@ -182,6 +195,19 @@ static void pty_process_connect_cb(uv_connect_t *req, int status) req->handle = NULL; } +static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) + FUNC_ATTR_NONNULL_ALL +{ + PtyProcess *ptyproc = + (PtyProcess *)((uv_handle_t *)wait_eof_timer->data); + Process *proc = (Process *)ptyproc; + + if (!proc->out || !uv_is_readable(proc->out->uvstream)) { + uv_timer_stop(&ptyproc->wait_eof_timer); + pty_process_finish2(ptyproc); + } +} + static void pty_process_finish2(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { @@ -200,99 +226,132 @@ static void pty_process_finish2(PtyProcess *ptyproc) proc->internal_exit_cb(proc); } -static int build_cmdline(char **argv, wchar_t **cmdline) +/// Build the command line to pass to CreateProcessW. +/// +/// @param[in] argv Array with string arguments. +/// @param[out] cmd_line Location where saved bulded cmd line. +/// +/// @returns zero on sucess, or error code of MultiByteToWideChar function. +/// +static int build_cmd_line(char **argv, wchar_t **cmd_line) FUNC_ATTR_NONNULL_ALL { - char *args = NULL; - size_t args_len = 0, argc = 0; - int ret; - QUEUE q; - QUEUE_INIT(&q); + size_t utf8_cmd_line_len = 0; + size_t argc = 0; + QUEUE args_q; + QUEUE_INIT(&args_q); while (*argv) { size_t buf_len = strlen(*argv) * 2 + 3; - arg_T *arg = xmalloc(sizeof(arg_T)); - arg->arg = (char *)xmalloc(buf_len); - quote_cmd_arg(arg->arg, buf_len, *argv); - args_len += strlen(arg->arg); - QUEUE_INIT(&arg->node); - QUEUE_INSERT_TAIL(&q, &arg->node); + ArgNode *arg_node = xmalloc(sizeof(*arg_node)); + arg_node->arg = xmalloc(buf_len); + quote_cmd_arg(arg_node->arg, buf_len, *argv); + utf8_cmd_line_len += strlen(arg_node->arg); + QUEUE_INIT(&arg_node->node); + QUEUE_INSERT_TAIL(&args_q, &arg_node->node); argc++; argv++; } - args_len += argc; - args = xmalloc(args_len); - *args = NUL; + + utf8_cmd_line_len += argc; + char *utf8_cmd_line = xmalloc(utf8_cmd_line_len); + *utf8_cmd_line = NUL; while (1) { - QUEUE *head = QUEUE_HEAD(&q); + QUEUE *head = QUEUE_HEAD(&args_q); QUEUE_REMOVE(head); - arg_T *arg = QUEUE_DATA(head, arg_T, node); - xstrlcat(args, arg->arg, args_len); - xfree(arg->arg); - xfree(arg); - if (QUEUE_EMPTY(&q)) { + ArgNode *arg_node = QUEUE_DATA(head, ArgNode, node); + xstrlcat(utf8_cmd_line, arg_node->arg, utf8_cmd_line_len); + xfree(arg_node->arg); + xfree(arg_node); + if (QUEUE_EMPTY(&args_q)) { break; } else { - xstrlcat(args, " ", args_len); + xstrlcat(utf8_cmd_line, " ", utf8_cmd_line_len); } } - ret = utf8_to_utf16(args, cmdline); - xfree(args); - return ret; + + int result = utf8_to_utf16(utf8_cmd_line, cmd_line); + if (result != 0) { + } + xfree(utf8_cmd_line); + return result; } -// Emulate quote_cmd_arg of libuv and quotes command line arguments -static void quote_cmd_arg(char *target, size_t remain, const char *source) +/// Emulate quote_cmd_arg of libuv and quotes command line argument. +/// Most of the code came from libuv. +/// +/// @param[out] dist Location where saved quotes argument. +/// @param dist_remaining Deistnation buffer size. +/// @param[in] src Pointer to argument. +/// +static void quote_cmd_arg(char *dist, size_t dist_remaining, const char *src) FUNC_ATTR_NONNULL_ALL { - size_t src_len = strlen(source); - size_t i; + size_t src_len = strlen(src); bool quote_hit = true; - char *start = target; - char tmp; + char *start = dist; if (src_len == 0) { - snprintf(target, remain, "\"\""); + // Need double quotation for empty argument. + snprintf(dist, dist_remaining, "\"\""); return; } - if (NULL == strpbrk(source, " \t\"")) { - xstrlcpy(target, source, remain); + if (NULL == strpbrk(src, " \t\"")) { + // No quotation needed. + xstrlcpy(dist, src, dist_remaining); return; } - if (NULL == strpbrk(source, "\"\\")) { - snprintf(target, remain, "\"%s\"", source); + if (NULL == strpbrk(src, "\"\\")) { + // No embedded double quotes or backlashes, so I can just wrap quote marks. + // around the whole thing. + snprintf(dist, dist_remaining, "\"%s\"", src); return; } - assert(remain--); - *(target++) = NUL; - assert(remain--); - *(target++) = '"'; - for (i = src_len; i > 0; i--) { - assert(remain--); - *(target++) = source[i - 1]; - - if (quote_hit && source[i - 1] == '\\') { - assert(remain--); - *(target++) = '\\'; - } else if (source[i - 1] == '"') { + // Expected input/output: + // input : hello"world + // output: "hello\"world" + // input : hello""world + // output: "hello\"\"world" + // input : hello\world + // output: hello\world + // input : hello\\world + // output: hello\\world + // input : hello\"world + // output: "hello\\\"world" + // input : hello\\"world + // output: "hello\\\\\"world" + // input : hello world\ + // output: "hello world\\" + + assert(dist_remaining--); + *(dist++) = NUL; + assert(dist_remaining--); + *(dist++) = '"'; + for (size_t i = src_len; i > 0; i--) { + assert(dist_remaining--); + *(dist++) = src[i - 1]; + if (quote_hit && src[i - 1] == '\\') { + assert(dist_remaining--); + *(dist++) = '\\'; + } else if (src[i - 1] == '"') { quote_hit = true; - assert(remain--); - *(target++) = '\\'; + assert(dist_remaining--); + *(dist++) = '\\'; } else { quote_hit = false; } } - assert(remain); - *target = '"'; - while (start < target) { - tmp = *start; - *start = *target; - *target = tmp; + assert(dist_remaining); + *dist = '"'; + + while (start < dist) { + char tmp = *start; + *start = *dist; + *dist = tmp; start++; - target--; + dist--; } - return; } -- cgit From d3a8c4f99289f7b65a68bf9ed5eeab34aa688e0e Mon Sep 17 00:00:00 2001 From: erw7 Date: Sun, 2 Apr 2017 18:32:23 +0900 Subject: win/pty: log errors --- src/nvim/os/pty_process_win.c | 112 ++++++++++++++++++++++++++++++------------ 1 file changed, 80 insertions(+), 32 deletions(-) (limited to 'src/nvim/os/pty_process_win.c') diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index 6a08e2868b..4bb5f64455 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -2,7 +2,9 @@ #include #include -#include "nvim/vim.h" +#include + +#include "nvim/os/os.h" #include "nvim/ascii.h" #include "nvim/memory.h" #include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 @@ -23,7 +25,7 @@ static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) uv_timer_start(&ptyproc->wait_eof_timer, wait_eof_timer_cb, 200, 200); } -/// @returns zero on sucess, or error code of winpty or MultiByteToWideChar. +/// @returns zero on success, or negative error code. int pty_process_spawn(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { @@ -40,27 +42,32 @@ int pty_process_spawn(PtyProcess *ptyproc) uv_connect_t *out_req = NULL; wchar_t *cmd_line = NULL; wchar_t *cwd = NULL; + const char *emsg = NULL; assert(!proc->err); cfg = winpty_config_new(WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err); if (cfg == NULL) { + emsg = "Failed, winpty_config_new."; goto cleanup; } winpty_config_set_initial_size(cfg, ptyproc->width, ptyproc->height); winpty_object = winpty_open(cfg, &err); if (winpty_object == NULL) { + emsg = "Failed, winpty_open."; goto cleanup; } status = utf16_to_utf8(winpty_conin_name(winpty_object), &in_name); if (status != 0) { + emsg = "Failed to convert in_name from utf16 to utf8."; goto cleanup; } status = utf16_to_utf8(winpty_conout_name(winpty_object), &out_name); if (status != 0) { + emsg = "Failed to convert out_name from utf16 to utf8."; goto cleanup; } @@ -85,12 +92,14 @@ int pty_process_spawn(PtyProcess *ptyproc) if (proc->cwd != NULL) { status = utf8_to_utf16(proc->cwd, &cwd); if (status != 0) { + emsg = "Failed to convert pwd form utf8 to utf16."; goto cleanup; } } status = build_cmd_line(proc->argv, &cmd_line); if (status != 0) { + emsg = "Failed to convert cmd line form utf8 to utf16."; goto cleanup; } @@ -102,15 +111,23 @@ int pty_process_spawn(PtyProcess *ptyproc) NULL, // Optional environment variables &err); if (spawncfg == NULL) { + emsg = "Failed winpty_spawn_config_new."; goto cleanup; } + DWORD win_err = 0; if (!winpty_spawn(winpty_object, spawncfg, &process_handle, NULL, // Optional thread handle - NULL, // Optional create process error + &win_err, &err)) { + if (win_err) { + status = (int)win_err; + emsg = "Failed spawn process."; + } else { + emsg = "Failed winpty_spawn."; + } goto cleanup; } proc->pid = GetProcessId(process_handle); @@ -137,6 +154,15 @@ int pty_process_spawn(PtyProcess *ptyproc) process_handle = NULL; cleanup: + if (status) { + // In the case of an error of MultiByteToWideChar or CreateProcessW. + ELOG("%s error code: %d", emsg, status); + status = os_translate_sys_error(status); + } else if (err != NULL) { + status = (int)winpty_error_code(err); + ELOG("%s error code: %d", emsg, status); + status = translate_winpty_error(status); + } winpty_error_free(err); winpty_config_free(cfg); winpty_spawn_config_free(spawncfg); @@ -198,8 +224,7 @@ static void pty_process_connect_cb(uv_connect_t *req, int status) static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) FUNC_ATTR_NONNULL_ALL { - PtyProcess *ptyproc = - (PtyProcess *)((uv_handle_t *)wait_eof_timer->data); + PtyProcess *ptyproc = wait_eof_timer->data; Process *proc = (Process *)ptyproc; if (!proc->out || !uv_is_readable(proc->out->uvstream)) { @@ -229,9 +254,9 @@ static void pty_process_finish2(PtyProcess *ptyproc) /// Build the command line to pass to CreateProcessW. /// /// @param[in] argv Array with string arguments. -/// @param[out] cmd_line Location where saved bulded cmd line. +/// @param[out] cmd_line Location where saved builded cmd line. /// -/// @returns zero on sucess, or error code of MultiByteToWideChar function. +/// @returns zero on success, or error code of MultiByteToWideChar function. /// static int build_cmd_line(char **argv, wchar_t **cmd_line) FUNC_ATTR_NONNULL_ALL @@ -271,8 +296,6 @@ static int build_cmd_line(char **argv, wchar_t **cmd_line) } int result = utf8_to_utf16(utf8_cmd_line, cmd_line); - if (result != 0) { - } xfree(utf8_cmd_line); return result; } @@ -280,33 +303,33 @@ static int build_cmd_line(char **argv, wchar_t **cmd_line) /// Emulate quote_cmd_arg of libuv and quotes command line argument. /// Most of the code came from libuv. /// -/// @param[out] dist Location where saved quotes argument. -/// @param dist_remaining Deistnation buffer size. +/// @param[out] dest Location where saved quotes argument. +/// @param dest_remaining Destination buffer size. /// @param[in] src Pointer to argument. /// -static void quote_cmd_arg(char *dist, size_t dist_remaining, const char *src) +static void quote_cmd_arg(char *dest, size_t dest_remaining, const char *src) FUNC_ATTR_NONNULL_ALL { size_t src_len = strlen(src); bool quote_hit = true; - char *start = dist; + char *start = dest; if (src_len == 0) { // Need double quotation for empty argument. - snprintf(dist, dist_remaining, "\"\""); + snprintf(dest, dest_remaining, "\"\""); return; } if (NULL == strpbrk(src, " \t\"")) { // No quotation needed. - xstrlcpy(dist, src, dist_remaining); + xstrlcpy(dest, src, dest_remaining); return; } if (NULL == strpbrk(src, "\"\\")) { // No embedded double quotes or backlashes, so I can just wrap quote marks. // around the whole thing. - snprintf(dist, dist_remaining, "\"%s\"", src); + snprintf(dest, dest_remaining, "\"%s\"", src); return; } @@ -326,32 +349,57 @@ static void quote_cmd_arg(char *dist, size_t dist_remaining, const char *src) // input : hello world\ // output: "hello world\\" - assert(dist_remaining--); - *(dist++) = NUL; - assert(dist_remaining--); - *(dist++) = '"'; + assert(dest_remaining--); + *(dest++) = NUL; + assert(dest_remaining--); + *(dest++) = '"'; for (size_t i = src_len; i > 0; i--) { - assert(dist_remaining--); - *(dist++) = src[i - 1]; + assert(dest_remaining--); + *(dest++) = src[i - 1]; if (quote_hit && src[i - 1] == '\\') { - assert(dist_remaining--); - *(dist++) = '\\'; + assert(dest_remaining--); + *(dest++) = '\\'; } else if (src[i - 1] == '"') { quote_hit = true; - assert(dist_remaining--); - *(dist++) = '\\'; + assert(dest_remaining--); + *(dest++) = '\\'; } else { quote_hit = false; } } - assert(dist_remaining); - *dist = '"'; + assert(dest_remaining); + *dest = '"'; - while (start < dist) { + while (start < dest) { char tmp = *start; - *start = *dist; - *dist = tmp; + *start = *dest; + *dest = tmp; start++; - dist--; + dest--; + } +} + +/// Translate winpty error code to libuv error. +/// +/// @param[in] winpty_errno Winpty error code returned by winpty_error_code +/// function. +/// +/// @returns Error code of libuv error. +int translate_winpty_error(int winpty_errno) +{ + if (winpty_errno <= 0) { + return winpty_errno; // If < 0 then it's already a libuv error. + } + + switch (winpty_errno) { + case WINPTY_ERROR_OUT_OF_MEMORY: return UV_ENOMEM; + case WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED: return UV_EAI_FAIL; + case WINPTY_ERROR_LOST_CONNECTION: return UV_ENOTCONN; + case WINPTY_ERROR_AGENT_EXE_MISSING: return UV_ENOENT; + case WINPTY_ERROR_UNSPECIFIED: return UV_UNKNOWN; + case WINPTY_ERROR_AGENT_DIED: return UV_ESRCH; + case WINPTY_ERROR_AGENT_TIMEOUT: return UV_ETIMEDOUT; + case WINPTY_ERROR_AGENT_CREATION_FAILED: return UV_EAI_FAIL; + default: return UV_UNKNOWN; } } -- cgit From 8c1782b84070d786cc1a7dfd6242a9ebc0923ad3 Mon Sep 17 00:00:00 2001 From: erw7 Date: Fri, 14 Apr 2017 22:35:56 +0900 Subject: pty_process_win: avoid quoting for cmd.exe --- src/nvim/os/pty_process_win.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src/nvim/os/pty_process_win.c') diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index 4bb5f64455..ef8a699c56 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -97,7 +97,8 @@ int pty_process_spawn(PtyProcess *ptyproc) } } - status = build_cmd_line(proc->argv, &cmd_line); + status = build_cmd_line(proc->argv, &cmd_line, + os_shell_is_cmdexe(proc->argv[0])); if (status != 0) { emsg = "Failed to convert cmd line form utf8 to utf16."; goto cleanup; @@ -258,7 +259,7 @@ static void pty_process_finish2(PtyProcess *ptyproc) /// /// @returns zero on success, or error code of MultiByteToWideChar function. /// -static int build_cmd_line(char **argv, wchar_t **cmd_line) +static int build_cmd_line(char **argv, wchar_t **cmd_line, bool is_cmdexe) FUNC_ATTR_NONNULL_ALL { size_t utf8_cmd_line_len = 0; @@ -267,10 +268,14 @@ static int build_cmd_line(char **argv, wchar_t **cmd_line) QUEUE_INIT(&args_q); while (*argv) { - size_t buf_len = strlen(*argv) * 2 + 3; + size_t buf_len = is_cmdexe ? (strlen(*argv) + 1) : (strlen(*argv) * 2 + 3); ArgNode *arg_node = xmalloc(sizeof(*arg_node)); arg_node->arg = xmalloc(buf_len); - quote_cmd_arg(arg_node->arg, buf_len, *argv); + if (is_cmdexe) { + xstrlcpy(arg_node->arg, *argv, buf_len); + } else { + quote_cmd_arg(arg_node->arg, buf_len, *argv); + } utf8_cmd_line_len += strlen(arg_node->arg); QUEUE_INIT(&arg_node->node); QUEUE_INSERT_TAIL(&args_q, &arg_node->node); -- cgit