# HG changeset patch # User Christian Brabandt # Date 1457458205 -3600 # Node ID d0717262d80242e315f17f2ad1aac9f8536cc12f # Parent 41802f51dbb41c195d886677393f65215a9e8586 commit https://github.com/vim/vim/commit/f65333c9b59654a70f2a07200f65c93dfcaa49b3 Author: Bram Moolenaar Date: Tue Mar 8 18:27:21 2016 +0100 patch 7.4.1518 Problem: Channel with disconnected in/out/err is not supported. Solution: Implement it for Unix. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -10285,7 +10285,7 @@ get_job_options(typval_T *tv, jobopt_T * * Returns NULL if the handle is invalid. */ static channel_T * -get_channel_arg(typval_T *tv) +get_channel_arg(typval_T *tv, int check_open) { channel_T *channel = NULL; @@ -10304,7 +10304,7 @@ get_channel_arg(typval_T *tv) return NULL; } - if (channel == NULL || !channel_is_open(channel)) + if (check_open && (channel == NULL || !channel_is_open(channel))) { EMSG(_("E906: not an open channel")); return NULL; @@ -10318,7 +10318,7 @@ get_channel_arg(typval_T *tv) static void f_ch_close(typval_T *argvars, typval_T *rettv UNUSED) { - channel_T *channel = get_channel_arg(&argvars[0]); + channel_T *channel = get_channel_arg(&argvars[0], TRUE); if (channel != NULL) { @@ -10333,7 +10333,7 @@ f_ch_close(typval_T *argvars, typval_T * static void f_ch_getbufnr(typval_T *argvars, typval_T *rettv) { - channel_T *channel = get_channel_arg(&argvars[0]); + channel_T *channel = get_channel_arg(&argvars[0], TRUE); rettv->vval.v_number = -1; if (channel != NULL) @@ -10361,7 +10361,7 @@ f_ch_getbufnr(typval_T *argvars, typval_ static void f_ch_getjob(typval_T *argvars, typval_T *rettv) { - channel_T *channel = get_channel_arg(&argvars[0]); + channel_T *channel = get_channel_arg(&argvars[0], TRUE); if (channel != NULL) { @@ -10383,7 +10383,7 @@ f_ch_log(typval_T *argvars, typval_T *re channel_T *channel = NULL; if (argvars[1].v_type != VAR_UNKNOWN) - channel = get_channel_arg(&argvars[1]); + channel = get_channel_arg(&argvars[1], TRUE); ch_log(channel, (char *)msg); } @@ -10500,7 +10500,7 @@ common_channel_read(typval_T *argvars, t == FAIL) return; - channel = get_channel_arg(&argvars[0]); + channel = get_channel_arg(&argvars[0], TRUE); if (channel != NULL) { if (opt.jo_set & JO_PART) @@ -10570,7 +10570,7 @@ send_common( channel_T *channel; int part_send; - channel = get_channel_arg(&argvars[0]); + channel = get_channel_arg(&argvars[0], TRUE); if (channel == NULL) return NULL; part_send = channel_part_send(channel); @@ -10619,7 +10619,7 @@ ch_expr_common(typval_T *argvars, typval rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - channel = get_channel_arg(&argvars[0]); + channel = get_channel_arg(&argvars[0], TRUE); if (channel == NULL) return; part_send = channel_part_send(channel); @@ -10736,7 +10736,7 @@ f_ch_setoptions(typval_T *argvars, typva channel_T *channel; jobopt_T opt; - channel = get_channel_arg(&argvars[0]); + channel = get_channel_arg(&argvars[0], TRUE); if (channel == NULL) return; clear_job_options(&opt); @@ -10752,17 +10752,14 @@ f_ch_setoptions(typval_T *argvars, typva static void f_ch_status(typval_T *argvars, typval_T *rettv) { + channel_T *channel; + /* return an empty string by default */ rettv->v_type = VAR_STRING; - - if (argvars[0].v_type != VAR_CHANNEL) - { - EMSG2(_(e_invarg2), get_tv_string(&argvars[0])); - rettv->vval.v_string = NULL; - } - else - rettv->vval.v_string = vim_strsave( - (char_u *)channel_status(argvars[0].vval.v_channel)); + rettv->vval.v_string = NULL; + + channel = get_channel_arg(&argvars[0], FALSE); + rettv->vval.v_string = vim_strsave((char_u *)channel_status(channel)); } #endif diff --git a/src/os_unix.c b/src/os_unix.c --- a/src/os_unix.c +++ b/src/os_unix.c @@ -5045,11 +5045,17 @@ mch_start_job(char **argv, job_T *job, j int fd_out[2]; /* for stdout */ int fd_err[2]; /* for stderr */ channel_T *channel = NULL; + int use_null_for_in = options->jo_io[PART_IN] == JIO_NULL; + int use_null_for_out = options->jo_io[PART_OUT] == JIO_NULL; + int use_null_for_err = options->jo_io[PART_ERR] == JIO_NULL; int use_file_for_in = options->jo_io[PART_IN] == JIO_FILE; int use_file_for_out = options->jo_io[PART_OUT] == JIO_FILE; int use_file_for_err = options->jo_io[PART_ERR] == JIO_FILE; int use_out_for_err = options->jo_io[PART_ERR] == JIO_OUT; + if (use_out_for_err && use_null_for_out) + use_null_for_err = TRUE; + /* default is to fail */ job->jv_status = JOB_FAILED; fd_in[0] = -1; @@ -5072,7 +5078,7 @@ mch_start_job(char **argv, job_T *job, j goto failed; } } - else if (pipe(fd_in) < 0) + else if (!use_null_for_in && pipe(fd_in) < 0) goto failed; if (use_file_for_out) @@ -5086,7 +5092,7 @@ mch_start_job(char **argv, job_T *job, j goto failed; } } - else if (pipe(fd_out) < 0) + else if (!use_null_for_out && pipe(fd_out) < 0) goto failed; if (use_file_for_err) @@ -5100,12 +5106,15 @@ mch_start_job(char **argv, job_T *job, j goto failed; } } - else if (!use_out_for_err && pipe(fd_err) < 0) + else if (!use_out_for_err && !use_null_for_err && pipe(fd_err) < 0) goto failed; - channel = add_channel(); - if (channel == NULL) - goto failed; + if (!use_null_for_in || !use_null_for_out || !use_null_for_err) + { + channel = add_channel(); + if (channel == NULL) + goto failed; + } # endif pid = fork(); /* maybe we should use vfork() */ @@ -5117,6 +5126,10 @@ mch_start_job(char **argv, job_T *job, j if (pid == 0) { +# ifdef FEAT_CHANNEL + int null_fd = -1; +# endif + /* child */ reset_signals(); /* handle signals normally */ @@ -5131,15 +5144,31 @@ mch_start_job(char **argv, job_T *job, j /* TODO: re-enable this when pipes connect without a channel */ # ifdef FEAT_CHANNEL + if (use_null_for_in || use_null_for_out || use_null_for_err) + null_fd = open("/dev/null", O_RDWR | O_EXTRA, 0); + /* set up stdin for the child */ - if (!use_file_for_in) - close(fd_in[1]); - close(0); - ignored = dup(fd_in[0]); - close(fd_in[0]); + if (use_null_for_in) + { + close(0); + ignored = dup(null_fd); + } + else + { + if (!use_file_for_in) + close(fd_in[1]); + close(0); + ignored = dup(fd_in[0]); + close(fd_in[0]); + } /* set up stderr for the child */ - if (use_out_for_err) + if (use_null_for_err) + { + close(2); + ignored = dup(null_fd); + } + else if (use_out_for_err) { close(2); ignored = dup(fd_out[1]); @@ -5154,11 +5183,21 @@ mch_start_job(char **argv, job_T *job, j } /* set up stdout for the child */ - if (!use_file_for_out) - close(fd_out[0]); - close(1); - ignored = dup(fd_out[1]); - close(fd_out[1]); + if (use_null_for_out) + { + close(0); + ignored = dup(null_fd); + } + else + { + if (!use_file_for_out) + close(fd_out[0]); + close(1); + ignored = dup(fd_out[1]); + close(fd_out[1]); + } + if (null_fd >= 0) + close(null_fd); # endif /* See above for type of argv. */ @@ -5183,17 +5222,23 @@ mch_start_job(char **argv, job_T *job, j close(fd_out[1]); if (!use_out_for_err && !use_file_for_err) close(fd_err[1]); - channel_set_pipes(channel, - use_file_for_in ? INVALID_FD : fd_in[1], - use_file_for_out ? INVALID_FD : fd_out[0], - use_out_for_err || use_file_for_err + if (channel != NULL) + { + channel_set_pipes(channel, + use_file_for_in || use_null_for_in + ? INVALID_FD : fd_in[1], + use_file_for_out || use_null_for_out + ? INVALID_FD : fd_out[0], + use_out_for_err || use_file_for_err || use_null_for_err ? INVALID_FD : fd_err[0]); - channel_set_job(channel, job, options); + channel_set_job(channel, job, options); # ifdef FEAT_GUI - channel_gui_register(channel); + channel_gui_register(channel); # endif -# endif - + } +# endif + + /* success! */ return; failed: ; diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1417,8 +1417,8 @@ struct channel_S { #define JO_TIMEOUT_ALL (JO_TIMEOUT + JO_OUT_TIMEOUT + JO_ERR_TIMEOUT) typedef enum { + JIO_PIPE, /* default */ JIO_NULL, - JIO_PIPE, JIO_FILE, JIO_BUFFER, JIO_OUT diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -784,6 +784,58 @@ func Test_pipe_io_one_buffer() endtry endfunc +func Test_pipe_null() + if !has('job') + return + endif + " TODO: implement this for MS-Windows + if !has('unix') + return + endif + call ch_log('Test_pipe_null()') + + " We cannot check that no I/O works, we only check that the job starts + " properly. + let job = job_start(s:python . " test_channel_pipe.py something", + \ {'in-io': 'null'}) + call assert_equal("run", job_status(job)) + try + call assert_equal('something', ch_read(job)) + finally + call job_stop(job) + endtry + + let job = job_start(s:python . " test_channel_pipe.py err-out", + \ {'out-io': 'null'}) + call assert_equal("run", job_status(job)) + try + call assert_equal('err-out', ch_read(job, {"part": "err"})) + finally + call job_stop(job) + endtry + + let job = job_start(s:python . " test_channel_pipe.py something", + \ {'err-io': 'null'}) + call assert_equal("run", job_status(job)) + try + call assert_equal('something', ch_read(job)) + finally + call job_stop(job) + endtry + + let job = job_start(s:python . " test_channel_pipe.py something", + \ {'out-io': 'null', 'err-io': 'out'}) + call assert_equal("run", job_status(job)) + call job_stop(job) + + let job = job_start(s:python . " test_channel_pipe.py something", + \ {'in-io': 'null', 'out-io': 'null', 'err-io': 'null'}) + call assert_equal("run", job_status(job)) + call assert_equal('channel fail', string(job_getchannel(job))) + call assert_equal('fail', ch_status(job)) + call job_stop(job) +endfunc + """""""""" let s:unletResponse = '' diff --git a/src/testdir/test_channel_pipe.py b/src/testdir/test_channel_pipe.py --- a/src/testdir/test_channel_pipe.py +++ b/src/testdir/test_channel_pipe.py @@ -10,7 +10,12 @@ import sys if __name__ == "__main__": if len(sys.argv) > 1: - print(sys.argv[1]) + if sys.argv[1].startswith("err"): + print(sys.argv[1], file=sys.stderr) + sys.stderr.flush() + else: + print(sys.argv[1]) + sys.stdout.flush() while True: typed = sys.stdin.readline() diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -744,6 +744,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1518, +/**/ 1517, /**/ 1516,