diff options
Diffstat (limited to 'src/nvim/channel.c')
-rw-r--r-- | src/nvim/channel.c | 241 |
1 files changed, 129 insertions, 112 deletions
diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 60af11e94b..30243a3102 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -12,11 +12,11 @@ #include "nvim/msgpack_rpc/server.h" #include "nvim/os/shell.h" #ifdef WIN32 -# include "nvim/os/pty_conpty_win.h" # include "nvim/os/os_win_console.h" +# include "nvim/os/pty_conpty_win.h" #endif -#include "nvim/path.h" #include "nvim/ascii.h" +#include "nvim/path.h" static bool did_stdio = false; @@ -32,13 +32,9 @@ static uint64_t next_chan_id = CHAN_STDERR+1; /// Teardown the module void channel_teardown(void) { - if (!channels) { - return; - } - Channel *channel; - map_foreach_value(channels, channel, { + map_foreach_value(&channels, channel, { channel_close(channel->id, kChannelPartAll, NULL); }); } @@ -70,7 +66,7 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error) if (part == kChannelPartRpc || part == kChannelPartAll) { close_main = true; if (chan->is_rpc) { - rpc_close(chan); + rpc_close(chan); } else if (part == kChannelPartRpc) { *error = (const char *)e_invstream; return false; @@ -82,68 +78,68 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error) } switch (chan->streamtype) { - case kChannelStreamSocket: - if (!close_main) { - *error = (const char *)e_invstream; - return false; - } - stream_may_close(&chan->stream.socket); - break; + case kChannelStreamSocket: + if (!close_main) { + *error = (const char *)e_invstream; + return false; + } + stream_may_close(&chan->stream.socket); + break; - case kChannelStreamProc: - proc = (Process *)&chan->stream.proc; - if (part == kChannelPartStdin || close_main) { - stream_may_close(&proc->in); - } - if (part == kChannelPartStdout || close_main) { - stream_may_close(&proc->out); - } - if (part == kChannelPartStderr || part == kChannelPartAll) { - stream_may_close(&proc->err); - } - if (proc->type == kProcessTypePty && part == kChannelPartAll) { - pty_process_close_master(&chan->stream.pty); - } + case kChannelStreamProc: + proc = (Process *)&chan->stream.proc; + if (part == kChannelPartStdin || close_main) { + stream_may_close(&proc->in); + } + if (part == kChannelPartStdout || close_main) { + stream_may_close(&proc->out); + } + if (part == kChannelPartStderr || part == kChannelPartAll) { + stream_may_close(&proc->err); + } + if (proc->type == kProcessTypePty && part == kChannelPartAll) { + pty_process_close_master(&chan->stream.pty); + } - break; + break; - case kChannelStreamStdio: - if (part == kChannelPartStdin || close_main) { - stream_may_close(&chan->stream.stdio.in); - } - if (part == kChannelPartStdout || close_main) { - stream_may_close(&chan->stream.stdio.out); - } - if (part == kChannelPartStderr) { - *error = (const char *)e_invstream; - return false; - } - break; + case kChannelStreamStdio: + if (part == kChannelPartStdin || close_main) { + stream_may_close(&chan->stream.stdio.in); + } + if (part == kChannelPartStdout || close_main) { + stream_may_close(&chan->stream.stdio.out); + } + if (part == kChannelPartStderr) { + *error = (const char *)e_invstream; + return false; + } + break; - case kChannelStreamStderr: - if (part != kChannelPartAll && part != kChannelPartStderr) { - *error = (const char *)e_invstream; - return false; - } - if (!chan->stream.err.closed) { - chan->stream.err.closed = true; - // Don't close on exit, in case late error messages - if (!exiting) { - fclose(stderr); - } - channel_decref(chan); + case kChannelStreamStderr: + if (part != kChannelPartAll && part != kChannelPartStderr) { + *error = (const char *)e_invstream; + return false; + } + if (!chan->stream.err.closed) { + chan->stream.err.closed = true; + // Don't close on exit, in case late error messages + if (!exiting) { + fclose(stderr); } - break; + channel_decref(chan); + } + break; - case kChannelStreamInternal: - if (!close_main) { - *error = (const char *)e_invstream; - return false; - } - break; + case kChannelStreamInternal: + if (!close_main) { + *error = (const char *)e_invstream; + return false; + } + break; - default: - abort(); + default: + abort(); } return true; @@ -152,7 +148,6 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error) /// Initializes the module void channel_init(void) { - channels = pmap_new(uint64_t)(); channel_alloc(kChannelStreamStderr); rpc_init(); } @@ -177,7 +172,7 @@ Channel *channel_alloc(ChannelStreamType type) chan->exit_status = -1; chan->streamtype = type; assert(chan->id <= VARNUMBER_MAX); - pmap_put(uint64_t)(channels, chan->id, chan); + pmap_put(uint64_t)(&channels, chan->id, chan); return chan; } @@ -245,11 +240,15 @@ static void free_channel_event(void **argv) rpc_free(chan); } + if (chan->streamtype == kChannelStreamProc) { + process_free(&chan->stream.proc); + } + callback_reader_free(&chan->on_data); callback_reader_free(&chan->on_stderr); callback_free(&chan->on_exit); - pmap_del(uint64_t)(channels, chan->id); + pmap_del(uint64_t)(&channels, chan->id); multiqueue_free(chan->events); xfree(chan); } @@ -259,7 +258,7 @@ static void channel_destroy_early(Channel *chan) if ((chan->id != --next_chan_id)) { abort(); } - pmap_del(uint64_t)(channels, chan->id); + pmap_del(uint64_t)(&channels, chan->id); chan->id = 0; if ((--chan->refcount != 0)) { @@ -289,6 +288,9 @@ static void close_cb(Stream *stream, void *data) /// `on_stdout` is ignored /// @param[in] detach True if the job should not be killed when nvim exits, /// ignored if `pty` is true +/// @param[in] stdin_mode Stdin mode. Either kChannelStdinPipe to open a +/// channel for stdin or kChannelStdinNull to leave +/// stdin disconnected. /// @param[in] cwd Initial working directory for the job. Nvim's working /// directory if `cwd` is NULL /// @param[in] pty_width Width of the pty, ignored if `pty` is false @@ -299,12 +301,10 @@ static void close_cb(Stream *stream, void *data) /// < 0 if the job can't start /// /// @returns [allocated] channel -Channel *channel_job_start(char **argv, CallbackReader on_stdout, - CallbackReader on_stderr, Callback on_exit, - bool pty, bool rpc, bool overlapped, bool detach, - const char *cwd, - uint16_t pty_width, uint16_t pty_height, - dict_T *env, varnumber_T *status_out) +Channel *channel_job_start(char **argv, CallbackReader on_stdout, CallbackReader on_stderr, + Callback on_exit, bool pty, bool rpc, bool overlapped, bool detach, + ChannelStdinMode stdin_mode, const char *cwd, uint16_t pty_width, + uint16_t pty_height, dict_T *env, varnumber_T *status_out) { assert(cwd == NULL || os_isdir_executable(cwd)); @@ -345,7 +345,7 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, proc->overlapped = overlapped; char *cmd = xstrdup(proc->argv[0]); - bool has_out, has_err; + bool has_in, has_out, has_err; if (proc->type == kProcessTypePty) { has_out = true; has_err = false; @@ -353,7 +353,17 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, has_out = rpc || callback_reader_set(chan->on_data); has_err = callback_reader_set(chan->on_stderr); } - int status = process_spawn(proc, true, has_out, has_err); + + switch (stdin_mode) { + case kChannelStdinPipe: + has_in = true; + break; + case kChannelStdinNull: + has_in = false; + break; + } + + int status = process_spawn(proc, has_in, has_out, has_err); if (status) { EMSG3(_(e_jobspawn), os_strerror(status), cmd); xfree(cmd); @@ -369,7 +379,9 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, tv_dict_free(proc->env); } - wstream_init(&proc->in, 0); + if (has_in) { + wstream_init(&proc->in, 0); + } if (has_out) { rstream_init(&proc->out, 0); } @@ -395,8 +407,7 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, } -uint64_t channel_connect(bool tcp, const char *address, - bool rpc, CallbackReader on_output, +uint64_t channel_connect(bool tcp, const char *address, bool rpc, CallbackReader on_output, int timeout, const char **error) { Channel *channel; @@ -456,8 +467,7 @@ void channel_from_connection(SocketWatcher *watcher) /// Creates an API channel from stdin/stdout. This is used when embedding /// Neovim -uint64_t channel_from_stdio(bool rpc, CallbackReader on_output, - const char **error) +uint64_t channel_from_stdio(bool rpc, CallbackReader on_output, const char **error) FUNC_ATTR_NONNULL_ALL { if (!headless_mode && !embedded_mode) { @@ -500,8 +510,8 @@ uint64_t channel_from_stdio(bool rpc, CallbackReader on_output, } /// @param data will be consumed -size_t channel_send(uint64_t id, char *data, size_t len, - bool data_owned, const char **error) +size_t channel_send(uint64_t id, char *data, size_t len, bool data_owned, const char **error) + FUNC_ATTR_NONNULL_ALL { Channel *chan = find_channel(id); size_t written = 0; @@ -569,22 +579,20 @@ static inline list_T *buffer_to_tv_list(const char *const buf, const size_t len) return l; } -void on_channel_data(Stream *stream, RBuffer *buf, size_t count, - void *data, bool eof) +void on_channel_data(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof) { Channel *chan = data; on_channel_output(stream, chan, buf, count, eof, &chan->on_data); } -void on_job_stderr(Stream *stream, RBuffer *buf, size_t count, - void *data, bool eof) +void on_job_stderr(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof) { Channel *chan = data; on_channel_output(stream, chan, buf, count, eof, &chan->on_stderr); } -static void on_channel_output(Stream *stream, Channel *chan, RBuffer *buf, - size_t count, bool eof, CallbackReader *reader) +static void on_channel_output(Stream *stream, Channel *chan, RBuffer *buf, size_t count, bool eof, + CallbackReader *reader) { // stub variable, to keep reading consistent with the order of events, only // consider the count parameter. @@ -683,9 +691,7 @@ static void channel_process_exit_cb(Process *proc, int status, void *data) { Channel *chan = data; if (chan->term) { - char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN]; - snprintf(msg, sizeof msg, "\r\n[Process exited %d]", proc->status); - terminal_close(chan->term, msg); + terminal_close(chan->term, status); } // If process did not exit, we only closed the handle of a detached process. @@ -839,32 +845,43 @@ Dictionary channel_info(uint64_t id) const char *stream_desc, *mode_desc; switch (chan->streamtype) { - case kChannelStreamProc: - stream_desc = "job"; - if (chan->stream.proc.type == kProcessTypePty) { - const char *name = pty_process_tty_name(&chan->stream.pty); - PUT(info, "pty", STRING_OBJ(cstr_to_string(name))); + case kChannelStreamProc: { + stream_desc = "job"; + if (chan->stream.proc.type == kProcessTypePty) { + const char *name = pty_process_tty_name(&chan->stream.pty); + PUT(info, "pty", STRING_OBJ(cstr_to_string(name))); + } + + char **p = chan->stream.proc.argv; + Array argv = ARRAY_DICT_INIT; + if (p != NULL) { + while (*p != NULL) { + ADD(argv, STRING_OBJ(cstr_to_string(*p))); + p++; } - break; + } + PUT(info, "argv", ARRAY_OBJ(argv)); + break; + } - case kChannelStreamStdio: - stream_desc = "stdio"; - break; + case kChannelStreamStdio: + stream_desc = "stdio"; + break; - case kChannelStreamStderr: - stream_desc = "stderr"; - break; + case kChannelStreamStderr: + stream_desc = "stderr"; + break; - case kChannelStreamInternal: - PUT(info, "internal", BOOLEAN_OBJ(true)); - FALLTHROUGH; + case kChannelStreamInternal: + PUT(info, "internal", BOOLEAN_OBJ(true)); + FALLTHROUGH; - case kChannelStreamSocket: - stream_desc = "socket"; - break; + case kChannelStreamSocket: + stream_desc = "socket"; + break; - default: - abort(); + default: + abort(); } PUT(info, "stream", STRING_OBJ(cstr_to_string(stream_desc))); @@ -886,7 +903,7 @@ Array channel_all_info(void) { Channel *channel; Array ret = ARRAY_DICT_INIT; - map_foreach_value(channels, channel, { + map_foreach_value(&channels, channel, { ADD(ret, DICTIONARY_OBJ(channel_info(channel->id))); }); return ret; |