1
0
forked from aniani/vim

patch 7.4.1518

Problem:    Channel with disconnected in/out/err is not supported.
Solution:   Implement it for Unix.
This commit is contained in:
Bram Moolenaar
2016-03-08 18:27:21 +01:00
parent 367aabdbf7
commit f65333c9b5
6 changed files with 144 additions and 43 deletions

View File

@@ -10285,7 +10285,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
* Returns NULL if the handle is invalid. * Returns NULL if the handle is invalid.
*/ */
static channel_T * static channel_T *
get_channel_arg(typval_T *tv) get_channel_arg(typval_T *tv, int check_open)
{ {
channel_T *channel = NULL; channel_T *channel = NULL;
@@ -10304,7 +10304,7 @@ get_channel_arg(typval_T *tv)
return NULL; return NULL;
} }
if (channel == NULL || !channel_is_open(channel)) if (check_open && (channel == NULL || !channel_is_open(channel)))
{ {
EMSG(_("E906: not an open channel")); EMSG(_("E906: not an open channel"));
return NULL; return NULL;
@@ -10318,7 +10318,7 @@ get_channel_arg(typval_T *tv)
static void static void
f_ch_close(typval_T *argvars, typval_T *rettv UNUSED) 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) if (channel != NULL)
{ {
@@ -10333,7 +10333,7 @@ f_ch_close(typval_T *argvars, typval_T *rettv UNUSED)
static void static void
f_ch_getbufnr(typval_T *argvars, typval_T *rettv) 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; rettv->vval.v_number = -1;
if (channel != NULL) if (channel != NULL)
@@ -10361,7 +10361,7 @@ f_ch_getbufnr(typval_T *argvars, typval_T *rettv)
static void static void
f_ch_getjob(typval_T *argvars, typval_T *rettv) 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) if (channel != NULL)
{ {
@@ -10383,7 +10383,7 @@ f_ch_log(typval_T *argvars, typval_T *rettv UNUSED)
channel_T *channel = NULL; channel_T *channel = NULL;
if (argvars[1].v_type != VAR_UNKNOWN) 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); ch_log(channel, (char *)msg);
} }
@@ -10500,7 +10500,7 @@ common_channel_read(typval_T *argvars, typval_T *rettv, int raw)
== FAIL) == FAIL)
return; return;
channel = get_channel_arg(&argvars[0]); channel = get_channel_arg(&argvars[0], TRUE);
if (channel != NULL) if (channel != NULL)
{ {
if (opt.jo_set & JO_PART) if (opt.jo_set & JO_PART)
@@ -10570,7 +10570,7 @@ send_common(
channel_T *channel; channel_T *channel;
int part_send; int part_send;
channel = get_channel_arg(&argvars[0]); channel = get_channel_arg(&argvars[0], TRUE);
if (channel == NULL) if (channel == NULL)
return NULL; return NULL;
part_send = channel_part_send(channel); part_send = channel_part_send(channel);
@@ -10619,7 +10619,7 @@ ch_expr_common(typval_T *argvars, typval_T *rettv, int eval)
rettv->v_type = VAR_STRING; rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL; rettv->vval.v_string = NULL;
channel = get_channel_arg(&argvars[0]); channel = get_channel_arg(&argvars[0], TRUE);
if (channel == NULL) if (channel == NULL)
return; return;
part_send = channel_part_send(channel); part_send = channel_part_send(channel);
@@ -10736,7 +10736,7 @@ f_ch_setoptions(typval_T *argvars, typval_T *rettv UNUSED)
channel_T *channel; channel_T *channel;
jobopt_T opt; jobopt_T opt;
channel = get_channel_arg(&argvars[0]); channel = get_channel_arg(&argvars[0], TRUE);
if (channel == NULL) if (channel == NULL)
return; return;
clear_job_options(&opt); clear_job_options(&opt);
@@ -10752,17 +10752,14 @@ f_ch_setoptions(typval_T *argvars, typval_T *rettv UNUSED)
static void static void
f_ch_status(typval_T *argvars, typval_T *rettv) f_ch_status(typval_T *argvars, typval_T *rettv)
{ {
channel_T *channel;
/* return an empty string by default */ /* return an empty string by default */
rettv->v_type = VAR_STRING; 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; rettv->vval.v_string = NULL;
}
else channel = get_channel_arg(&argvars[0], FALSE);
rettv->vval.v_string = vim_strsave( rettv->vval.v_string = vim_strsave((char_u *)channel_status(channel));
(char_u *)channel_status(argvars[0].vval.v_channel));
} }
#endif #endif

View File

@@ -5045,11 +5045,17 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options UNUSED)
int fd_out[2]; /* for stdout */ int fd_out[2]; /* for stdout */
int fd_err[2]; /* for stderr */ int fd_err[2]; /* for stderr */
channel_T *channel = NULL; 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_in = options->jo_io[PART_IN] == JIO_FILE;
int use_file_for_out = options->jo_io[PART_OUT] == 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_file_for_err = options->jo_io[PART_ERR] == JIO_FILE;
int use_out_for_err = options->jo_io[PART_ERR] == JIO_OUT; 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 */ /* default is to fail */
job->jv_status = JOB_FAILED; job->jv_status = JOB_FAILED;
fd_in[0] = -1; fd_in[0] = -1;
@@ -5072,7 +5078,7 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options UNUSED)
goto failed; goto failed;
} }
} }
else if (pipe(fd_in) < 0) else if (!use_null_for_in && pipe(fd_in) < 0)
goto failed; goto failed;
if (use_file_for_out) if (use_file_for_out)
@@ -5086,7 +5092,7 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options UNUSED)
goto failed; goto failed;
} }
} }
else if (pipe(fd_out) < 0) else if (!use_null_for_out && pipe(fd_out) < 0)
goto failed; goto failed;
if (use_file_for_err) if (use_file_for_err)
@@ -5100,12 +5106,15 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options UNUSED)
goto failed; 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; goto failed;
if (!use_null_for_in || !use_null_for_out || !use_null_for_err)
{
channel = add_channel(); channel = add_channel();
if (channel == NULL) if (channel == NULL)
goto failed; goto failed;
}
# endif # endif
pid = fork(); /* maybe we should use vfork() */ pid = fork(); /* maybe we should use vfork() */
@@ -5117,6 +5126,10 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options UNUSED)
if (pid == 0) if (pid == 0)
{ {
# ifdef FEAT_CHANNEL
int null_fd = -1;
# endif
/* child */ /* child */
reset_signals(); /* handle signals normally */ reset_signals(); /* handle signals normally */
@@ -5131,15 +5144,31 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options UNUSED)
/* TODO: re-enable this when pipes connect without a channel */ /* TODO: re-enable this when pipes connect without a channel */
# ifdef FEAT_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 */ /* set up stdin for the child */
if (use_null_for_in)
{
close(0);
ignored = dup(null_fd);
}
else
{
if (!use_file_for_in) if (!use_file_for_in)
close(fd_in[1]); close(fd_in[1]);
close(0); close(0);
ignored = dup(fd_in[0]); ignored = dup(fd_in[0]);
close(fd_in[0]); close(fd_in[0]);
}
/* set up stderr for the child */ /* 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); close(2);
ignored = dup(fd_out[1]); ignored = dup(fd_out[1]);
@@ -5154,11 +5183,21 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options UNUSED)
} }
/* set up stdout for the child */ /* set up stdout for the child */
if (use_null_for_out)
{
close(0);
ignored = dup(null_fd);
}
else
{
if (!use_file_for_out) if (!use_file_for_out)
close(fd_out[0]); close(fd_out[0]);
close(1); close(1);
ignored = dup(fd_out[1]); ignored = dup(fd_out[1]);
close(fd_out[1]); close(fd_out[1]);
}
if (null_fd >= 0)
close(null_fd);
# endif # endif
/* See above for type of argv. */ /* See above for type of argv. */
@@ -5183,17 +5222,23 @@ mch_start_job(char **argv, job_T *job, jobopt_T *options UNUSED)
close(fd_out[1]); close(fd_out[1]);
if (!use_out_for_err && !use_file_for_err) if (!use_out_for_err && !use_file_for_err)
close(fd_err[1]); close(fd_err[1]);
if (channel != NULL)
{
channel_set_pipes(channel, channel_set_pipes(channel,
use_file_for_in ? INVALID_FD : fd_in[1], use_file_for_in || use_null_for_in
use_file_for_out ? INVALID_FD : fd_out[0], ? INVALID_FD : fd_in[1],
use_out_for_err || use_file_for_err 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]); ? INVALID_FD : fd_err[0]);
channel_set_job(channel, job, options); channel_set_job(channel, job, options);
# ifdef FEAT_GUI # ifdef FEAT_GUI
channel_gui_register(channel); channel_gui_register(channel);
# endif # endif
}
# endif # endif
/* success! */
return; return;
failed: ; failed: ;

View File

@@ -1417,8 +1417,8 @@ struct channel_S {
#define JO_TIMEOUT_ALL (JO_TIMEOUT + JO_OUT_TIMEOUT + JO_ERR_TIMEOUT) #define JO_TIMEOUT_ALL (JO_TIMEOUT + JO_OUT_TIMEOUT + JO_ERR_TIMEOUT)
typedef enum { typedef enum {
JIO_PIPE, /* default */
JIO_NULL, JIO_NULL,
JIO_PIPE,
JIO_FILE, JIO_FILE,
JIO_BUFFER, JIO_BUFFER,
JIO_OUT JIO_OUT

View File

@@ -784,6 +784,58 @@ func Test_pipe_io_one_buffer()
endtry endtry
endfunc 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 = '' let s:unletResponse = ''

View File

@@ -10,7 +10,12 @@ import sys
if __name__ == "__main__": if __name__ == "__main__":
if len(sys.argv) > 1: if len(sys.argv) > 1:
if sys.argv[1].startswith("err"):
print(sys.argv[1], file=sys.stderr)
sys.stderr.flush()
else:
print(sys.argv[1]) print(sys.argv[1])
sys.stdout.flush()
while True: while True:
typed = sys.stdin.readline() typed = sys.stdin.readline()

View File

@@ -743,6 +743,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1518,
/**/ /**/
1517, 1517,
/**/ /**/