diff options
-rw-r--r-- | src/nvim/os/pty_process_win.c | 273 | ||||
-rw-r--r-- | src/nvim/os/pty_process_win.h | 13 |
2 files changed, 173 insertions, 113 deletions
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; } diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h index 806857f130..59e0fad7f7 100644 --- a/src/nvim/os/pty_process_win.h +++ b/src/nvim/os/pty_process_win.h @@ -12,16 +12,17 @@ typedef struct pty_process { Process process; char *term_name; uint16_t width, height; - winpty_t *wp; + winpty_t *winpty_object; HANDLE finish_wait; HANDLE process_handle; uv_timer_t wait_eof_timer; } PtyProcess; -typedef struct arg_S { - char *arg; - QUEUE node; -} arg_T; +// Structure used by build_cmd_line() +typedef struct arg_node { + char *arg; // pointer to argument. + QUEUE node; // QUEUE structure. +} ArgNode; static inline PtyProcess pty_process_init(Loop *loop, void *data) { @@ -30,7 +31,7 @@ static inline PtyProcess pty_process_init(Loop *loop, void *data) rv.term_name = NULL; rv.width = 80; rv.height = 24; - rv.wp = NULL; + rv.winpty_object = NULL; rv.finish_wait = NULL; rv.process_handle = NULL; return rv; |