forked from aniani/vim
patch 7.4.1310
Problem: Jobs don't open a channel. Solution: Create pipes and add them to the channel. Add ch_logfile(). Only Unix for now.
This commit is contained in:
parent
00af60bbb6
commit
6463ca229c
@ -1,4 +1,4 @@
|
||||
*eval.txt* For Vim version 7.4. Last change: 2016 Feb 07
|
||||
*eval.txt* For Vim version 7.4. Last change: 2016 Feb 13
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@ -1416,7 +1416,7 @@ v:exception The value of the exception most recently caught and not
|
||||
|
||||
*v:false* *false-variable*
|
||||
v:false A Number with value zero. Used to put "false" in JSON. See
|
||||
|jsonencode()|.
|
||||
|json_encode()|.
|
||||
When used as a string this evaluates to "false". >
|
||||
echo v:false
|
||||
< false ~
|
||||
@ -1556,7 +1556,7 @@ v:mouse_col Column number for a mouse click obtained with |getchar()|.
|
||||
|
||||
*v:none* *none-variable*
|
||||
v:none An empty String. Used to put an empty item in JSON. See
|
||||
|jsonencode()|.
|
||||
|json_encode()|.
|
||||
When used as a number this evaluates to zero.
|
||||
When used as a string this evaluates to "none". >
|
||||
echo v:none
|
||||
@ -1564,7 +1564,7 @@ v:none An empty String. Used to put an empty item in JSON. See
|
||||
|
||||
*v:null* *null-variable*
|
||||
v:null An empty String. Used to put "null" in JSON. See
|
||||
|jsonencode()|.
|
||||
|json_encode()|.
|
||||
When used as a number this evaluates to zero.
|
||||
When used as a string this evaluates to "null". >
|
||||
echo v:null
|
||||
@ -1737,7 +1737,7 @@ v:throwpoint The point where the exception most recently caught and not
|
||||
|
||||
*v:true* *true-variable*
|
||||
v:true A Number with value one. Used to put "true" in JSON. See
|
||||
|jsonencode()|.
|
||||
|json_encode()|.
|
||||
When used as a string this evaluates to "true". >
|
||||
echo v:true
|
||||
< true ~
|
||||
@ -1816,7 +1816,9 @@ call( {func}, {arglist} [, {dict}])
|
||||
any call {func} with arguments {arglist}
|
||||
ceil( {expr}) Float round {expr} up
|
||||
ch_close( {handle}) none close a channel
|
||||
ch_logfile( {fname} [, {mode}]) none start logging channel activity
|
||||
ch_open( {address} [, {argdict})] Number open a channel to {address}
|
||||
ch_readraw( {handle}) String read from channel {handle}
|
||||
ch_sendexpr( {handle}, {expr} [, {callback}])
|
||||
any send {expr} over JSON channel {handle}
|
||||
ch_sendraw( {handle}, {string} [, {callback}])
|
||||
@ -1980,9 +1982,9 @@ mapcheck( {name}[, {mode} [, {abbr}]])
|
||||
String check for mappings matching {name}
|
||||
match( {expr}, {pat}[, {start}[, {count}]])
|
||||
Number position where {pat} matches in {expr}
|
||||
matchadd( {group}, {pattern}[, {priority}[, {id}]])
|
||||
matchadd( {group}, {pattern}[, {priority}[, {id} [, {dict}]]])
|
||||
Number highlight {pattern} with {group}
|
||||
matchaddpos( {group}, {list}[, {priority}[, {id}]])
|
||||
matchaddpos( {group}, {pos}[, {priority}[, {id}[, {dict}]]])
|
||||
Number highlight positions with {group}
|
||||
matcharg( {nr}) List arguments of |:match|
|
||||
matchdelete( {id}) Number delete match identified by {id}
|
||||
@ -2274,7 +2276,7 @@ assert_fails({cmd} [, {error}]) *assert_fails()*
|
||||
assert_false({actual} [, {msg}]) *assert_false()*
|
||||
When {actual} is not false an error message is added to
|
||||
|v:errors|, like with |assert_equal()|.
|
||||
A value is false when it is zero. When "{actual}" is not a
|
||||
A value is false when it is zero. When {actual} is not a
|
||||
number the assert fails.
|
||||
When {msg} is omitted an error in the form "Expected False but
|
||||
got {actual}" is produced.
|
||||
@ -2676,10 +2678,16 @@ confirm({msg} [, {choices} [, {default} [, {type}]]])
|
||||
don't fit, a vertical layout is used anyway. For some systems
|
||||
the horizontal layout is always used.
|
||||
|
||||
ch_close({handle}) *ch_close()*
|
||||
ch_close({handle}) *ch_close()*
|
||||
Close channel {handle}. See |channel|.
|
||||
{only available when compiled with the |+channel| feature}
|
||||
|
||||
ch_logfile( {fname} [, {mode}]) *ch_logfile()*
|
||||
Start logging channel activity to {fname}.
|
||||
When {mode} is omitted or "a" append to the file.
|
||||
When {mode} is "w" start with an empty file.
|
||||
When {fname} is an empty string: stop logging.
|
||||
|
||||
ch_open({address} [, {argdict}]) *ch_open()*
|
||||
Open a channel to {address}. See |channel|.
|
||||
Returns the channel handle on success. Returns a negative
|
||||
@ -2703,7 +2711,13 @@ ch_open({address} [, {argdict}]) *ch_open()*
|
||||
Default: 2000.
|
||||
{only available when compiled with the |+channel| feature}
|
||||
|
||||
ch_sendexpr({handle}, {expr} [, {callback}]) *ch_sendexpr()*
|
||||
ch_readraw({handle}) *ch_readraw()*
|
||||
Read from channel {handle} and return the received message.
|
||||
This uses the channel timeout. When there is nothing to read
|
||||
within that time an empty string is returned.
|
||||
TODO: depends on channel mode.
|
||||
|
||||
ch_sendexpr({handle}, {expr} [, {callback}]) *ch_sendexpr()*
|
||||
Send {expr} over channel {handle}. The {expr} is encoded
|
||||
according to the type of channel. The function cannot be used
|
||||
with a raw channel. See |channel-use|. *E912*
|
||||
@ -2844,9 +2858,11 @@ deepcopy({expr}[, {noref}]) *deepcopy()* *E698*
|
||||
different from using {expr} directly.
|
||||
When {expr} is a |List| a full copy is created. This means
|
||||
that the original |List| can be changed without changing the
|
||||
copy, and vice versa. When an item is a |List|, a copy for it
|
||||
is made, recursively. Thus changing an item in the copy does
|
||||
not change the contents of the original |List|.
|
||||
copy, and vice versa. When an item is a |List| or
|
||||
|Dictionary|, a copy for it is made, recursively. Thus
|
||||
changing an item in the copy does not change the contents of
|
||||
the original |List|.
|
||||
A |Dictionary| is copied in a similar way as a |List|.
|
||||
When {noref} is omitted or zero a contained |List| or
|
||||
|Dictionary| is only copied once. All references point to
|
||||
this single copy. With {noref} set to 1 every occurrence of a
|
||||
@ -2907,6 +2923,14 @@ diff_hlID({lnum}, {col}) *diff_hlID()*
|
||||
The highlight ID can be used with |synIDattr()| to obtain
|
||||
syntax information about the highlighting.
|
||||
|
||||
*disable_char_avail_for_testing()*
|
||||
disable_char_avail_for_testing({expr})
|
||||
When {expr} is 1 the internal char_avail() function will
|
||||
return FALSE. When {expr} is 0 the char_avail() function will
|
||||
function normally.
|
||||
Only use this for a test where typeahead causes the test not
|
||||
to work. E.g., to trigger the CursorMovedI autocommand event.
|
||||
|
||||
empty({expr}) *empty()*
|
||||
Return the Number 1 if {expr} is empty, zero otherwise.
|
||||
- A |List| or |Dictionary| is empty when it does not have any
|
||||
@ -3937,7 +3961,7 @@ glob2regpat({expr}) *glob2regpat()*
|
||||
empty string.
|
||||
|
||||
*globpath()*
|
||||
globpath({path}, {expr} [, {nosuf} [, {list} [, {allinks}]]])
|
||||
globpath({path}, {expr} [, {nosuf} [, {list} [, {alllinks}]]])
|
||||
Perform glob() on all directories in {path} and concatenate
|
||||
the results. Example: >
|
||||
:echo globpath(&rtp, "syntax/c.vim")
|
||||
@ -3963,7 +3987,7 @@ globpath({path}, {expr} [, {nosuf} [, {list} [, {allinks}]]])
|
||||
they are separated by <NL> characters. Example: >
|
||||
:echo globpath(&rtp, "syntax/c.vim", 0, 1)
|
||||
<
|
||||
{allinks} is used as with |glob()|.
|
||||
{alllinks} is used as with |glob()|.
|
||||
|
||||
The "**" item can be used to search in a directory tree.
|
||||
For example, to find all "README.txt" files in the directories
|
||||
@ -4314,22 +4338,25 @@ job_start({command} [, {options}]) *job_start()*
|
||||
Start a job and return a Job object. Unlike |system()| and
|
||||
|:!cmd| this does not wait for the job to finish.
|
||||
|
||||
{command} can be a string. This works best on MS-Windows. On
|
||||
{command} can be a String. This works best on MS-Windows. On
|
||||
Unix it is split up in white-separated parts to be passed to
|
||||
execvp(). Arguments in double quotes can contain white space.
|
||||
|
||||
{command} can be a list, where the first item is the executable
|
||||
{command} can be a List, where the first item is the executable
|
||||
and further items are the arguments. All items are converted
|
||||
to String. This works best on Unix.
|
||||
|
||||
On MS-Windows, job_start() makes a GUI application hidden. If
|
||||
want to show it, Use |:!start| instead.
|
||||
|
||||
The command is executed directly, not through a shell, the
|
||||
'shell' option is not used. To use the shell: >
|
||||
let job = job_start(["/bin/sh", "-c", "echo hello"])
|
||||
< Or: >
|
||||
let job = job_start('/bin/sh -c "echo hello"')
|
||||
< However, the status of the job will now be the status of the
|
||||
shell, and stopping the job means stopping the shell and the
|
||||
command may continue to run.
|
||||
< Note that this will start two processes, the shell and the
|
||||
command it executes. If you don't want this use the "exec"
|
||||
shell command.
|
||||
|
||||
On Unix $PATH is used to search for the executable only when
|
||||
the command does not contain a slash.
|
||||
@ -4342,12 +4369,10 @@ job_start({command} [, {options}]) *job_start()*
|
||||
The returned Job object can be used to get the status with
|
||||
|job_status()| and stop the job with |job_stop()|.
|
||||
|
||||
{options} must be a Dictionary. It can contain these optional
|
||||
items:
|
||||
killonexit When non-zero kill the job when Vim
|
||||
exits. (default: 0, don't kill)
|
||||
{options} must be a Dictionary. It can contain many optional
|
||||
items, see |job-options|.
|
||||
|
||||
{only available when compiled with the |+channel| feature}
|
||||
{only available when compiled with the |+job| feature}
|
||||
|
||||
job_status({job}) *job_status()*
|
||||
Returns a String with the status of {job}:
|
||||
@ -4355,27 +4380,40 @@ job_status({job}) *job_status()*
|
||||
"fail" job failed to start
|
||||
"dead" job died or was stopped after running
|
||||
|
||||
{only available when compiled with the |+channel| feature}
|
||||
{only available when compiled with the |+job| feature}
|
||||
|
||||
job_stop({job} [, {how}]) *job_stop()*
|
||||
Stop the {job}. This can also be used to signal the job.
|
||||
|
||||
When {how} is omitted or is "term" the job will be terminated
|
||||
normally. For Unix SIGTERM is sent.
|
||||
Other values:
|
||||
normally. For Unix SIGTERM is sent. For MS-Windows
|
||||
CTRL_BREAK will be sent. This goes to the process group, thus
|
||||
children may also be affected.
|
||||
|
||||
Other values for Unix:
|
||||
"hup" Unix: SIGHUP
|
||||
"quit" Unix: SIGQUIT
|
||||
"kill" Unix: SIGKILL (strongest way to stop)
|
||||
number Unix: signal with that number
|
||||
|
||||
Other values for MS-Windows:
|
||||
"int" Windows: CTRL_C
|
||||
"kill" Windows: terminate process forcedly
|
||||
Others Windows: CTRL_BREAK
|
||||
|
||||
On Unix the signal is sent to the process group. This means
|
||||
that when the job is "sh -c command" it affects both the shell
|
||||
and the command.
|
||||
|
||||
The result is a Number: 1 if the operation could be executed,
|
||||
0 if "how" is not supported on the system.
|
||||
Note that even when the operation was executed, whether the
|
||||
job was actually stopped needs to be checked with
|
||||
job_status().
|
||||
The operation will even be done when the job wasn't running.
|
||||
The status of the job isn't checked, the operation will even
|
||||
be done when Vim thinks the job isn't running.
|
||||
|
||||
{only available when compiled with the |+channel| feature}
|
||||
{only available when compiled with the |+job| feature}
|
||||
|
||||
join({list} [, {sep}]) *join()*
|
||||
Join the items in {list} together into one String.
|
||||
@ -4773,7 +4811,7 @@ match({expr}, {pat}[, {start}[, {count}]]) *match()*
|
||||
done like 'magic' is set and 'cpoptions' is empty.
|
||||
|
||||
*matchadd()* *E798* *E799* *E801*
|
||||
matchadd({group}, {pattern}[, {priority}[, {id} [, {dict}]]])
|
||||
matchadd({group}, {pattern}[, {priority}[, {id}[, {dict}]]])
|
||||
Defines a pattern to be highlighted in the current window (a
|
||||
"match"). It will be highlighted with {group}. Returns an
|
||||
identification number (ID), which can be used to delete the
|
||||
@ -4809,7 +4847,7 @@ matchadd({group}, {pattern}[, {priority}[, {id} [, {dict}]]])
|
||||
highlighted matches. The dict can have the following members:
|
||||
|
||||
conceal Special character to show instead of the
|
||||
match (only for |hl-Conceal| highlighed
|
||||
match (only for |hl-Conceal| highlighted
|
||||
matches, see |:syn-cchar|)
|
||||
|
||||
The number of matches is not limited, as it is the case with
|
||||
@ -6808,7 +6846,7 @@ type({expr}) The result is a Number, depending on the type of {expr}:
|
||||
:if type(myvar) == type({})
|
||||
:if type(myvar) == type(0.0)
|
||||
:if type(myvar) == type(v:false)
|
||||
:if type(myvar) == type(v:none
|
||||
:if type(myvar) == type(v:none)
|
||||
|
||||
undofile({name}) *undofile()*
|
||||
Return the name of the undo file that would be used for a file
|
||||
|
795
src/channel.c
795
src/channel.c
File diff suppressed because it is too large
Load Diff
78
src/eval.c
78
src/eval.c
@ -503,8 +503,10 @@ static void f_call(typval_T *argvars, typval_T *rettv);
|
||||
static void f_ceil(typval_T *argvars, typval_T *rettv);
|
||||
#endif
|
||||
#ifdef FEAT_CHANNEL
|
||||
static void f_ch_open(typval_T *argvars, typval_T *rettv);
|
||||
static void f_ch_close(typval_T *argvars, typval_T *rettv);
|
||||
static void f_ch_logfile(typval_T *argvars, typval_T *rettv);
|
||||
static void f_ch_open(typval_T *argvars, typval_T *rettv);
|
||||
static void f_ch_readraw(typval_T *argvars, typval_T *rettv);
|
||||
static void f_ch_sendexpr(typval_T *argvars, typval_T *rettv);
|
||||
static void f_ch_sendraw(typval_T *argvars, typval_T *rettv);
|
||||
#endif
|
||||
@ -624,6 +626,7 @@ static void f_isdirectory(typval_T *argvars, typval_T *rettv);
|
||||
static void f_islocked(typval_T *argvars, typval_T *rettv);
|
||||
static void f_items(typval_T *argvars, typval_T *rettv);
|
||||
#ifdef FEAT_JOB
|
||||
static void f_job_getchannel(typval_T *argvars, typval_T *rettv);
|
||||
static void f_job_start(typval_T *argvars, typval_T *rettv);
|
||||
static void f_job_stop(typval_T *argvars, typval_T *rettv);
|
||||
static void f_job_status(typval_T *argvars, typval_T *rettv);
|
||||
@ -7720,6 +7723,8 @@ failret:
|
||||
static void
|
||||
job_free(job_T *job)
|
||||
{
|
||||
if (job->jv_channel >= 0)
|
||||
channel_close(job->jv_channel);
|
||||
mch_clear_job(job);
|
||||
vim_free(job);
|
||||
}
|
||||
@ -8083,7 +8088,9 @@ static struct fst
|
||||
#endif
|
||||
#ifdef FEAT_CHANNEL
|
||||
{"ch_close", 1, 1, f_ch_close},
|
||||
{"ch_logfile", 1, 2, f_ch_logfile},
|
||||
{"ch_open", 1, 2, f_ch_open},
|
||||
{"ch_readraw", 1, 2, f_ch_readraw},
|
||||
{"ch_sendexpr", 2, 3, f_ch_sendexpr},
|
||||
{"ch_sendraw", 2, 3, f_ch_sendraw},
|
||||
#endif
|
||||
@ -8207,6 +8214,7 @@ static struct fst
|
||||
{"islocked", 1, 1, f_islocked},
|
||||
{"items", 1, 1, f_items},
|
||||
#ifdef FEAT_JOB
|
||||
{"job_getchannel", 1, 1, f_job_getchannel},
|
||||
{"job_start", 1, 2, f_job_start},
|
||||
{"job_status", 1, 1, f_job_status},
|
||||
{"job_stop", 1, 2, f_job_stop},
|
||||
@ -9788,7 +9796,7 @@ get_channel_arg(typval_T *tv)
|
||||
}
|
||||
ch_idx = tv->vval.v_number;
|
||||
|
||||
if (!channel_is_open(ch_idx))
|
||||
if (!channel_can_write_to(ch_idx))
|
||||
{
|
||||
EMSGN(_("E906: not an open channel"), ch_idx);
|
||||
return -1;
|
||||
@ -9824,6 +9832,32 @@ get_callback(typval_T *arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* "ch_logfile()" function
|
||||
*/
|
||||
static void
|
||||
f_ch_logfile(typval_T *argvars, typval_T *rettv UNUSED)
|
||||
{
|
||||
char_u *fname;
|
||||
char_u *opt = (char_u *)"";
|
||||
char_u buf[NUMBUFLEN];
|
||||
FILE *file = NULL;
|
||||
|
||||
fname = get_tv_string(&argvars[0]);
|
||||
if (argvars[1].v_type == VAR_STRING)
|
||||
opt = get_tv_string_buf(&argvars[1], buf);
|
||||
if (*fname != NUL)
|
||||
{
|
||||
file = fopen((char *)fname, *opt == 'w' ? "w" : "a");
|
||||
if (file == NULL)
|
||||
{
|
||||
EMSG2(_(e_notopen), fname);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ch_logfile(file);
|
||||
}
|
||||
|
||||
/*
|
||||
* "ch_open()" function
|
||||
*/
|
||||
@ -9913,6 +9947,27 @@ f_ch_open(typval_T *argvars, typval_T *rettv)
|
||||
rettv->vval.v_number = ch_idx;
|
||||
}
|
||||
|
||||
/*
|
||||
* "ch_readraw()" function
|
||||
*/
|
||||
static void
|
||||
f_ch_readraw(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
int ch_idx;
|
||||
|
||||
/* return an empty string by default */
|
||||
rettv->v_type = VAR_STRING;
|
||||
rettv->vval.v_string = NULL;
|
||||
|
||||
ch_idx = get_channel_arg(&argvars[0]);
|
||||
if (ch_idx < 0)
|
||||
{
|
||||
EMSG(_(e_invarg));
|
||||
return;
|
||||
}
|
||||
rettv->vval.v_string = channel_read_block(ch_idx);
|
||||
}
|
||||
|
||||
/*
|
||||
* common for "sendexpr()" and "sendraw()"
|
||||
* Returns the channel index if the caller should read the response.
|
||||
@ -14299,6 +14354,23 @@ f_items(typval_T *argvars, typval_T *rettv)
|
||||
}
|
||||
|
||||
#ifdef FEAT_JOB
|
||||
/*
|
||||
* "job_getchannel()" function
|
||||
*/
|
||||
static void
|
||||
f_job_getchannel(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
if (argvars[0].v_type != VAR_JOB)
|
||||
EMSG(_(e_invarg));
|
||||
else
|
||||
{
|
||||
job_T *job = argvars[0].vval.v_job;
|
||||
|
||||
rettv->v_type = VAR_NUMBER;
|
||||
rettv->vval.v_number = job->jv_channel;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "job_start()" function
|
||||
*/
|
||||
@ -14401,7 +14473,7 @@ theend:
|
||||
* "job_status()" function
|
||||
*/
|
||||
static void
|
||||
f_job_status(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
|
||||
f_job_status(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
char *result;
|
||||
|
||||
|
@ -1780,10 +1780,10 @@ process_message(void)
|
||||
#ifdef FEAT_CHANNEL
|
||||
if (msg.message == WM_NETBEANS)
|
||||
{
|
||||
int channel_idx = channel_socket2idx((sock_T)msg.wParam);
|
||||
int channel_idx = channel_fd2idx((sock_T)msg.wParam);
|
||||
|
||||
if (channel_idx >= 0)
|
||||
channel_read(channel_idx);
|
||||
channel_read(channel_idx, FALSE, "process_message");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
151
src/os_unix.c
151
src/os_unix.c
@ -3982,6 +3982,42 @@ mch_parse_cmd(char_u *cmd, int use_shcf, char ***argv, int *argc)
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(USE_SYSTEM) || defined(FEAT_JOB)
|
||||
static void
|
||||
set_child_environment(void)
|
||||
{
|
||||
# ifdef HAVE_SETENV
|
||||
char envbuf[50];
|
||||
# else
|
||||
static char envbuf_Rows[20];
|
||||
static char envbuf_Columns[20];
|
||||
# endif
|
||||
|
||||
/* Simulate to have a dumb terminal (for now) */
|
||||
# ifdef HAVE_SETENV
|
||||
setenv("TERM", "dumb", 1);
|
||||
sprintf((char *)envbuf, "%ld", Rows);
|
||||
setenv("ROWS", (char *)envbuf, 1);
|
||||
sprintf((char *)envbuf, "%ld", Rows);
|
||||
setenv("LINES", (char *)envbuf, 1);
|
||||
sprintf((char *)envbuf, "%ld", Columns);
|
||||
setenv("COLUMNS", (char *)envbuf, 1);
|
||||
# else
|
||||
/*
|
||||
* Putenv does not copy the string, it has to remain valid.
|
||||
* Use a static array to avoid losing allocated memory.
|
||||
*/
|
||||
putenv("TERM=dumb");
|
||||
sprintf(envbuf_Rows, "ROWS=%ld", Rows);
|
||||
putenv(envbuf_Rows);
|
||||
sprintf(envbuf_Rows, "LINES=%ld", Rows);
|
||||
putenv(envbuf_Rows);
|
||||
sprintf(envbuf_Columns, "COLUMNS=%ld", Columns);
|
||||
putenv(envbuf_Columns);
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
@ -4134,12 +4170,6 @@ mch_call_shell(
|
||||
int fd_toshell[2]; /* for pipes */
|
||||
int fd_fromshell[2];
|
||||
int pipe_error = FALSE;
|
||||
# ifdef HAVE_SETENV
|
||||
char envbuf[50];
|
||||
# else
|
||||
static char envbuf_Rows[20];
|
||||
static char envbuf_Columns[20];
|
||||
# endif
|
||||
int did_settmode = FALSE; /* settmode(TMODE_RAW) called */
|
||||
|
||||
newcmd = vim_strsave(p_sh);
|
||||
@ -4349,28 +4379,7 @@ mch_call_shell(
|
||||
# endif
|
||||
}
|
||||
# endif
|
||||
/* Simulate to have a dumb terminal (for now) */
|
||||
# ifdef HAVE_SETENV
|
||||
setenv("TERM", "dumb", 1);
|
||||
sprintf((char *)envbuf, "%ld", Rows);
|
||||
setenv("ROWS", (char *)envbuf, 1);
|
||||
sprintf((char *)envbuf, "%ld", Rows);
|
||||
setenv("LINES", (char *)envbuf, 1);
|
||||
sprintf((char *)envbuf, "%ld", Columns);
|
||||
setenv("COLUMNS", (char *)envbuf, 1);
|
||||
# else
|
||||
/*
|
||||
* Putenv does not copy the string, it has to remain valid.
|
||||
* Use a static array to avoid losing allocated memory.
|
||||
*/
|
||||
putenv("TERM=dumb");
|
||||
sprintf(envbuf_Rows, "ROWS=%ld", Rows);
|
||||
putenv(envbuf_Rows);
|
||||
sprintf(envbuf_Rows, "LINES=%ld", Rows);
|
||||
putenv(envbuf_Rows);
|
||||
sprintf(envbuf_Columns, "COLUMNS=%ld", Columns);
|
||||
putenv(envbuf_Columns);
|
||||
# endif
|
||||
set_child_environment();
|
||||
|
||||
/*
|
||||
* stderr is only redirected when using the GUI, so that a
|
||||
@ -5030,13 +5039,34 @@ error:
|
||||
void
|
||||
mch_start_job(char **argv, job_T *job)
|
||||
{
|
||||
pid_t pid = fork();
|
||||
pid_t pid;
|
||||
int fd_in[2]; /* for stdin */
|
||||
int fd_out[2]; /* for stdout */
|
||||
int fd_err[2]; /* for stderr */
|
||||
int ch_idx;
|
||||
|
||||
if (pid == -1) /* maybe we should use vfork() */
|
||||
/* default is to fail */
|
||||
job->jv_status = JOB_FAILED;
|
||||
fd_in[0] = -1;
|
||||
fd_out[0] = -1;
|
||||
fd_err[0] = -1;
|
||||
|
||||
/* Open pipes for stdin, stdout, stderr. */
|
||||
if ((pipe(fd_in) < 0) || (pipe(fd_out) < 0) ||(pipe(fd_err) < 0))
|
||||
goto failed;
|
||||
|
||||
ch_idx = add_channel();
|
||||
if (ch_idx < 0)
|
||||
goto failed;
|
||||
|
||||
pid = fork(); /* maybe we should use vfork() */
|
||||
if (pid == -1)
|
||||
{
|
||||
job->jv_status = JOB_FAILED;
|
||||
/* failed to fork */
|
||||
goto failed;
|
||||
}
|
||||
else if (pid == 0)
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
/* child */
|
||||
reset_signals(); /* handle signals normally */
|
||||
@ -5048,17 +5078,62 @@ mch_start_job(char **argv, job_T *job)
|
||||
(void)setsid();
|
||||
# endif
|
||||
|
||||
set_child_environment();
|
||||
|
||||
/* set up stdin for the child */
|
||||
close(fd_in[1]);
|
||||
close(0);
|
||||
ignored = dup(fd_in[0]);
|
||||
close(fd_in[0]);
|
||||
|
||||
/* set up stdout for the child */
|
||||
close(fd_out[0]);
|
||||
close(1);
|
||||
ignored = dup(fd_out[1]);
|
||||
close(fd_out[1]);
|
||||
|
||||
/* set up stderr for the child */
|
||||
close(fd_err[0]);
|
||||
close(2);
|
||||
ignored = dup(fd_err[1]);
|
||||
close(fd_err[1]);
|
||||
|
||||
/* See above for type of argv. */
|
||||
execvp(argv[0], argv);
|
||||
|
||||
perror("executing job failed");
|
||||
_exit(EXEC_FAILED); /* exec failed, return failure code */
|
||||
}
|
||||
else
|
||||
|
||||
/* parent */
|
||||
job->jv_pid = pid;
|
||||
job->jv_status = JOB_STARTED;
|
||||
job->jv_channel = ch_idx;
|
||||
|
||||
/* child stdin, stdout and stderr */
|
||||
close(fd_in[0]);
|
||||
close(fd_out[1]);
|
||||
close(fd_err[1]);
|
||||
channel_set_pipes(ch_idx, fd_in[1], fd_out[0], fd_err[0]);
|
||||
channel_set_job(ch_idx, job);
|
||||
|
||||
return;
|
||||
|
||||
failed:
|
||||
if (fd_in[0] >= 0)
|
||||
{
|
||||
/* parent */
|
||||
job->jv_pid = pid;
|
||||
job->jv_status = JOB_STARTED;
|
||||
close(fd_in[0]);
|
||||
close(fd_in[1]);
|
||||
}
|
||||
if (fd_out[0] >= 0)
|
||||
{
|
||||
close(fd_out[0]);
|
||||
close(fd_out[1]);
|
||||
}
|
||||
if (fd_err[0] >= 0)
|
||||
{
|
||||
close(fd_err[0]);
|
||||
close(fd_err[1]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5104,8 +5179,8 @@ mch_job_status(job_T *job)
|
||||
int
|
||||
mch_stop_job(job_T *job, char_u *how)
|
||||
{
|
||||
int sig = -1;
|
||||
pid_t job_pid;
|
||||
int sig = -1;
|
||||
pid_t job_pid;
|
||||
|
||||
if (STRCMP(how, "hup") == 0)
|
||||
sig = SIGHUP;
|
||||
|
@ -1,22 +1,27 @@
|
||||
/* channel.c */
|
||||
void ch_logfile(FILE *file);
|
||||
int add_channel(void);
|
||||
void channel_gui_register_all(void);
|
||||
int channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void));
|
||||
void channel_set_pipes(int idx, int in, int out, int err);
|
||||
void channel_set_job(int idx, job_T *job);
|
||||
void channel_set_json_mode(int idx, ch_mode_T ch_mode);
|
||||
void channel_set_timeout(int idx, int timeout);
|
||||
void channel_set_callback(int idx, char_u *callback);
|
||||
void channel_set_req_callback(int idx, char_u *callback, int id);
|
||||
char_u *channel_get(int idx);
|
||||
int channel_collapse(int idx);
|
||||
int channel_can_write_to(int idx);
|
||||
int channel_is_open(int idx);
|
||||
void channel_close(int idx);
|
||||
int channel_save(int idx, char_u *buf, int len);
|
||||
char_u *channel_peek(int idx);
|
||||
void channel_clear(int idx);
|
||||
int channel_get_id(void);
|
||||
void channel_read(int idx);
|
||||
void channel_read(int idx, int use_stderr, char *func);
|
||||
char_u *channel_read_block(int idx);
|
||||
int channel_read_json_block(int ch_idx, int id, typval_T **rettv);
|
||||
int channel_socket2idx(sock_T fd);
|
||||
int channel_fd2idx(sock_T fd);
|
||||
int channel_send(int idx, char_u *buf, char *fun);
|
||||
int channel_poll_setup(int nfd_in, void *fds_in);
|
||||
int channel_poll_check(int ret_in, void *fds_in);
|
||||
|
@ -1110,7 +1110,12 @@ typedef double float_T;
|
||||
|
||||
typedef struct listvar_S list_T;
|
||||
typedef struct dictvar_S dict_T;
|
||||
|
||||
typedef struct jobvar_S job_T;
|
||||
typedef struct readq_S readq_T;
|
||||
typedef struct jsonq_S jsonq_T;
|
||||
typedef struct cbq_S cbq_T;
|
||||
typedef struct channel_S channel_T;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
@ -1255,8 +1260,92 @@ struct jobvar_S
|
||||
jobstatus_T jv_status;
|
||||
|
||||
int jv_refcount; /* reference count */
|
||||
int jv_channel; /* channel for I/O */
|
||||
};
|
||||
|
||||
/*
|
||||
* Structures to hold info about a Channel.
|
||||
*/
|
||||
struct readq_S
|
||||
{
|
||||
char_u *buffer;
|
||||
readq_T *next;
|
||||
readq_T *prev;
|
||||
};
|
||||
|
||||
struct jsonq_S
|
||||
{
|
||||
typval_T *value;
|
||||
jsonq_T *next;
|
||||
jsonq_T *prev;
|
||||
};
|
||||
|
||||
struct cbq_S
|
||||
{
|
||||
char_u *callback;
|
||||
int seq_nr;
|
||||
cbq_T *next;
|
||||
cbq_T *prev;
|
||||
};
|
||||
|
||||
/* mode for a channel */
|
||||
typedef enum
|
||||
{
|
||||
MODE_RAW = 0,
|
||||
MODE_JSON,
|
||||
MODE_JS
|
||||
} ch_mode_T;
|
||||
|
||||
struct channel_S {
|
||||
sock_T ch_sock; /* the socket, -1 for a closed channel */
|
||||
|
||||
#ifdef UNIX
|
||||
# define CHANNEL_PIPES
|
||||
int ch_in; /* stdin of the job, -1 if not used */
|
||||
int ch_out; /* stdout of the job, -1 if not used */
|
||||
int ch_err; /* stderr of the job, -1 if not used */
|
||||
|
||||
# if defined(UNIX) && !defined(HAVE_SELECT)
|
||||
int ch_sock_idx; /* used by channel_poll_setup() */
|
||||
int ch_in_idx; /* used by channel_poll_setup() */
|
||||
int ch_out_idx; /* used by channel_poll_setup() */
|
||||
int ch_err_idx; /* used by channel_poll_setup() */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
readq_T ch_head; /* dummy node, header for circular queue */
|
||||
|
||||
int ch_error; /* When TRUE an error was reported. Avoids
|
||||
* giving pages full of error messages when
|
||||
* the other side has exited, only mention the
|
||||
* first error until the connection works
|
||||
* again. */
|
||||
#ifdef FEAT_GUI_X11
|
||||
XtInputId ch_inputHandler; /* Cookie for input */
|
||||
#endif
|
||||
#ifdef FEAT_GUI_GTK
|
||||
gint ch_inputHandler; /* Cookie for input */
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
int ch_inputHandler; /* simply ret.value of WSAAsyncSelect() */
|
||||
#endif
|
||||
|
||||
void (*ch_close_cb)(void); /* callback for when channel is closed */
|
||||
|
||||
int ch_block_id; /* ID that channel_read_json_block() is
|
||||
waiting for */
|
||||
char_u *ch_callback; /* function to call when a msg is not handled */
|
||||
cbq_T ch_cb_head; /* dummy node for pre-request callbacks */
|
||||
|
||||
ch_mode_T ch_mode;
|
||||
jsonq_T ch_json_head; /* dummy node, header for circular queue */
|
||||
|
||||
int ch_timeout; /* request timeout in msec */
|
||||
|
||||
job_T *ch_job; /* job that uses this channel */
|
||||
};
|
||||
|
||||
|
||||
/* structure used for explicit stack while garbage collecting hash tables */
|
||||
typedef struct ht_stack_S
|
||||
{
|
||||
@ -2729,11 +2818,3 @@ struct js_reader
|
||||
void *js_cookie; /* can be used by js_fill */
|
||||
};
|
||||
typedef struct js_reader js_read_T;
|
||||
|
||||
/* mode for a channel */
|
||||
typedef enum
|
||||
{
|
||||
MODE_RAW = 0,
|
||||
MODE_JSON,
|
||||
MODE_JS
|
||||
} ch_mode_T;
|
||||
|
@ -273,3 +273,20 @@ func Test_connect_waittime()
|
||||
call assert_true(reltimefloat(elapsed) < (has('unix') ? 1.0 : 3.0))
|
||||
endif
|
||||
endfunc
|
||||
|
||||
func Test_pipe()
|
||||
if !has('job') || !has('unix')
|
||||
return
|
||||
endif
|
||||
let job = job_start("python test_channel_pipe.py")
|
||||
call assert_equal("run", job_status(job))
|
||||
try
|
||||
let handle = job_getchannel(job)
|
||||
call ch_sendraw(handle, "echo something\n", 0)
|
||||
call assert_equal("something\n", ch_readraw(handle))
|
||||
let reply = ch_sendraw(handle, "quit\n")
|
||||
call assert_equal("Goodbye!\n", reply)
|
||||
finally
|
||||
call job_stop(job)
|
||||
endtry
|
||||
endfunc
|
||||
|
24
src/testdir/test_channel_pipe.py
Normal file
24
src/testdir/test_channel_pipe.py
Normal file
@ -0,0 +1,24 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Server that will communicate over stdin/stderr
|
||||
#
|
||||
# This requires Python 2.6 or later.
|
||||
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
print(sys.argv[1])
|
||||
|
||||
while True:
|
||||
typed = sys.stdin.readline()
|
||||
if typed.startswith("quit"):
|
||||
print("Goodbye!")
|
||||
sys.stdout.flush()
|
||||
break
|
||||
if typed.startswith("echo"):
|
||||
print(typed[5:-1])
|
||||
sys.stdout.flush()
|
||||
|
@ -747,6 +747,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1310,
|
||||
/**/
|
||||
1309,
|
||||
/**/
|
||||
|
Loading…
x
Reference in New Issue
Block a user