diff options
-rw-r--r-- | runtime/doc/eval.txt | 3 | ||||
-rw-r--r-- | src/nvim/channel.c | 23 | ||||
-rw-r--r-- | src/nvim/channel.h | 4 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 26 | ||||
-rw-r--r-- | test/functional/core/job_spec.lua | 6 |
5 files changed, 52 insertions, 10 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 9e9b828c5a..8c360b1778 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -5637,6 +5637,9 @@ jobstart({cmd}[, {opts}]) *jobstart()* before invoking `on_stderr`. |channel-buffered| stdout_buffered: (boolean) Collect data until EOF (stream closed) before invoking `on_stdout`. |channel-buffered| + stdin: (string) Either "pipe" (default) to connect the + job's stdin to a channel or "null" to disconnect + stdin. width: (number) Width of the `pty` terminal. {opts} is passed as |self| dictionary to the callback; the diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 60af11e94b..a0db1bcdfd 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -289,6 +289,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 @@ -302,7 +305,7 @@ static void close_cb(Stream *stream, void *data) 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, + ChannelStdinMode stdin_mode, const char *cwd, uint16_t pty_width, uint16_t pty_height, dict_T *env, varnumber_T *status_out) { @@ -345,7 +348,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 +356,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 +382,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); } diff --git a/src/nvim/channel.h b/src/nvim/channel.h index 9d26852ce5..df858e1602 100644 --- a/src/nvim/channel.h +++ b/src/nvim/channel.h @@ -28,6 +28,10 @@ typedef enum { kChannelPartAll } ChannelPart; +typedef enum { + kChannelStdinPipe, + kChannelStdinNull, +} ChannelStdinMode; typedef struct { Stream in; diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 3cda7ec5d4..8960cac9c5 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -5182,6 +5182,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool pty = false; bool clear_env = false; bool overlapped = false; + ChannelStdinMode stdin_mode = kChannelStdinPipe; CallbackReader on_stdout = CALLBACK_READER_INIT, on_stderr = CALLBACK_READER_INIT; Callback on_exit = CALLBACK_NONE; @@ -5196,6 +5197,17 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) clear_env = tv_dict_get_number(job_opts, "clear_env") != 0; overlapped = tv_dict_get_number(job_opts, "overlapped") != 0; + char *s = tv_dict_get_string(job_opts, "stdin", false); + if (s) { + if (!strncmp(s, "null", NUMBUFLEN)) { + stdin_mode = kChannelStdinNull; + } else if (!strncmp(s, "pipe", NUMBUFLEN)) { + // Nothing to do, default value + } else { + EMSG3(_(e_invargNval), "stdin", s); + } + } + if (pty && rpc) { EMSG2(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set"); shell_free_argv(argv); @@ -5252,8 +5264,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) env = create_environment(job_env, clear_env, pty, term_name); Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty, - rpc, overlapped, detach, cwd, width, height, - env, &rettv->vval.v_number); + rpc, overlapped, detach, stdin_mode, cwd, + width, height, env, &rettv->vval.v_number); if (chan) { channel_create_event(chan, NULL); } @@ -7733,8 +7745,9 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) Channel *chan = channel_job_start(argv, CALLBACK_READER_INIT, CALLBACK_READER_INIT, CALLBACK_NONE, - false, true, false, false, NULL, 0, 0, - NULL, &rettv->vval.v_number); + false, true, false, false, + kChannelStdinPipe, NULL, 0, 0, NULL, + &rettv->vval.v_number); if (chan) { channel_create_event(chan, NULL); } @@ -10850,10 +10863,11 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) const bool rpc = false; const bool overlapped = false; const bool detach = false; + ChannelStdinMode stdin_mode = kChannelStdinPipe; uint16_t term_width = MAX(0, curwin->w_width_inner - win_col_off(curwin)); Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, - pty, rpc, overlapped, detach, cwd, - term_width, curwin->w_height_inner, + pty, rpc, overlapped, detach, stdin_mode, + cwd, term_width, curwin->w_height_inner, env, &rettv->vval.v_number); if (rettv->vval.v_number <= 0) { return; diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 34ab90d760..c4745e636f 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -348,6 +348,12 @@ describe('jobs', function() eq(false, pcall(function() nvim('command', 'call jobsend(j, ["some data"])') end)) + + command("let g:job_opts.stdin = 'null'") + nvim('command', "let j = jobstart(['cat', '-'], g:job_opts)") + eq(false, pcall(function() + nvim('command', 'call jobsend(j, ["some data"])') + end)) end) it('disallows jobsend on a non-existent job', function() |