1
0
forked from aniani/vim

patch 7.4.1274

Problem:    Cannot run a job.
Solution:   Add job_start(), job_status() and job_stop(). Currently only works
            for Unix.
This commit is contained in:
Bram Moolenaar
2016-02-07 14:27:38 +01:00
parent c5f98ee987
commit 835dc636a5
9 changed files with 611 additions and 104 deletions

View File

@@ -1,4 +1,4 @@
*channel.txt* For Vim version 7.4. Last change: 2016 Feb 05
*channel.txt* For Vim version 7.4. Last change: 2016 Feb 06
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -93,7 +93,7 @@ The default is zero, don't wait, which is useful if the server is supposed to
be running already. A negative number waits forever.
"timeout" is the time to wait for a request when blocking, using
ch_sendexpr(). Again in millisecons. The default si 2000 (2 seconds).
ch_sendexpr(). Again in milliseconds. The default is 2000 (2 seconds).
When "mode" is "json" the "msg" argument is the body of the received message,
converted to Vim types.
@@ -104,13 +104,13 @@ possible to receive a message after sending one.
The handler can be added or changed later: >
call ch_setcallback(handle, {callback})
When "callback is empty (zero or an empty string) the handler is removed.
When "callback" is empty (zero or an empty string) the handler is removed.
NOT IMPLEMENTED YET
The timeout can be changed later: >
call ch_settimeout(handle, {msec})
NOT IMPLEMENTED YET
*E906*
Once done with the channel, disconnect it like this: >
call ch_close(handle)

View File

@@ -1,4 +1,4 @@
*eval.txt* For Vim version 7.4. Last change: 2016 Feb 05
*eval.txt* For Vim version 7.4. Last change: 2016 Feb 07
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -37,7 +37,7 @@ done, the features in this document are not available. See |+eval| and
1.1 Variable types ~
*E712*
There are six types of variables:
There are eight types of variables:
Number A 32 or 64 bit signed number. |expr-number| *Number*
Examples: -123 0x10 0177
@@ -49,9 +49,6 @@ Float A floating point number. |floating-point-format| *Float*
String A NUL terminated string of 8-bit unsigned characters (bytes).
|expr-string| Examples: "ab\txx\"--" 'x-z''a,c'
Funcref A reference to a function |Funcref|.
Example: function("strlen")
List An ordered sequence of items |List|.
Example: [1, 2, ['a', 'b']]
@@ -59,6 +56,13 @@ Dictionary An associative, unordered array: Each entry has a key and a
value. |Dictionary|
Example: {'blue': "#0000ff", 'red': "#ff0000"}
Funcref A reference to a function |Funcref|.
Example: function("strlen")
Special v:false, v:true, v:none and v:null
Job Used for job control, see |job_start()|.
The Number and String types are converted automatically, depending on how they
are used.
@@ -95,15 +99,16 @@ Note that in the command >
"foo" is converted to 0, which means FALSE. To test for a non-empty string,
use empty(): >
:if !empty("foo")
< *E745* *E728* *E703* *E729* *E730* *E731*
List, Dictionary and Funcref types are not automatically converted.
<
*E745* *E728* *E703* *E729* *E730* *E731* *E908* *E910*
List, Dictionary, Funcref and Job types are not automatically converted.
*E805* *E806* *E808*
When mixing Number and Float the Number is converted to Float. Otherwise
there is no automatic conversion of Float. You can use str2float() for String
to Float, printf() for Float to String and float2nr() for Float to Number.
*E891* *E892* *E893* *E894*
*E891* *E892* *E893* *E894* *E907* *E911*
When expecting a Float a Number can also be used, but nothing else.
*E706* *sticky-type-checking*
@@ -864,7 +869,7 @@ These three can be repeated and mixed. Examples:
expr8 *expr8*
-----
expr8[expr1] item of String or |List| *expr-[]* *E111*
*E909*
If expr8 is a Number or String this results in a String that contains the
expr1'th single byte from expr8. expr8 is used as a String, expr1 as a
Number. This doesn't recognize multi-byte encodings, see |byteidx()| for
@@ -1947,6 +1952,9 @@ invert( {expr}) Number bitwise invert
isdirectory( {directory}) Number TRUE if {directory} is a directory
islocked( {expr}) Number TRUE if {expr} is locked
items( {dict}) List key-value pairs in {dict}
job_start({command} [, {options}]) Job start a job
job_status({job}) String get the status of a job
job_stop({job} [, {how}]) Number stop a job
join( {list} [, {sep}]) String join {list} items into one String
jsondecode( {string}) any decode JSON
jsonencode( {expr}) String encode JSON
@@ -2668,6 +2676,7 @@ confirm({msg} [, {choices} [, {default} [, {type}]]])
ch_close({handle}) *ch_close()*
Close channel {handle}. See |channel|.
{only available when compiled with the |+channel| feature}
ch_open({address} [, {argdict}]) *ch_open()*
Open a channel to {address}. See |channel|.
@@ -2677,7 +2686,7 @@ ch_open({address} [, {argdict}]) *ch_open()*
{address} has the form "hostname:port", e.g.,
"localhost:8765".
If {argdict} is given it must be a |Directory|. The optional
If {argdict} is given it must be a |Dictionary|. The optional
items are:
mode "raw" or "json".
Default "json".
@@ -2686,10 +2695,11 @@ ch_open({address} [, {argdict}]) *ch_open()*
Default: none.
waittime Specify connect timeout as milliseconds.
Negative means forever.
Default: 0.
Default: 0 (don't wait)
timeout Specify response read timeout value as
milliseconds.
Default: 2000.
{only available when compiled with the |+channel| feature}
ch_sendexpr({handle}, {expr} [, {callback}]) *ch_sendexpr()*
Send {expr} over JSON channel {handle}. See |channel-use|.
@@ -2704,10 +2714,14 @@ ch_sendexpr({handle}, {expr} [, {callback}]) *ch_sendexpr()*
function. It is called when the response is received. See
|channel-callback|.
{only available when compiled with the |+channel| feature}
ch_sendraw({handle}, {string} [, {callback}]) *ch_sendraw()*
Send {string} over raw channel {handle}. See |channel-raw|.
Works like |ch_sendexpr()|, but does not decode the response.
{only available when compiled with the |+channel| feature}
*copy()*
copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't
different from using {expr} directly.
@@ -2888,9 +2902,12 @@ diff_hlID({lnum}, {col}) *diff_hlID()*
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
items. A Number is empty when its value is zero.
|v:false|, |v:none| and |v:null| are empty, |v:true| is not.
- A |List| or |Dictionary| is empty when it does not have any
items.
- A Number and Float is empty when its value is zero.
- |v:false|, |v:none| and |v:null| are empty, |v:true| is not.
- A Job is empty when it failed to start.
For a long |List| this is much faster than comparing the
length with zero.
@@ -4286,6 +4303,73 @@ items({dict}) *items()*
order.
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
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
and further items are the arguments. All items are converted
to String. This works best on Unix.
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.
On Unix $PATH is used to search for the executable only when
the command does not contain a slash.
The job will use the same terminal as Vim. If it reads from
stdin the job and Vim will be fighting over input, that
doesn't work. Redirect stdin and stdout to avoid problems: >
let job = job_start(['sh', '-c', "myserver </dev/null >/dev/null"])
<
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)
{only available when compiled with the |+channel| feature}
job_status({job}) *job_status()*
Returns a String with the status of {job}:
"run" job is running
"fail" job failed to start
"dead" job died or was stopped after running
{only available when compiled with the |+channel| 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:
"hup" Unix: SIGHUP
"quit" Unix: SIGQUIT
"kill" Unix: SIGKILL (strongest way to stop)
number Unix: signal with that number
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.
{only available when compiled with the |+channel| feature}
join({list} [, {sep}]) *join()*
Join the items in {list} together into one String.
When {sep} is specified it is put in between the items. If
@@ -6692,6 +6776,7 @@ type({expr}) The result is a Number, depending on the type of {expr}:
Float: 5
Boolean: 6 (v:false and v:true)
None 7 (v:null and v:none)
Job 8
To avoid the magic numbers it should be used this way: >
:if type(myvar) == type(0)
:if type(myvar) == type("")

View File

@@ -451,6 +451,9 @@ static dict_T *dict_copy(dict_T *orig, int deep, int copyID);
static long dict_len(dict_T *d);
static char_u *dict2string(typval_T *tv, int copyID);
static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate);
#ifdef FEAT_JOB
static void job_free(job_T *job);
#endif
static char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID);
static char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID);
static char_u *string_quote(char_u *str, int function);
@@ -619,6 +622,11 @@ static void f_invert(typval_T *argvars, typval_T *rettv);
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_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);
#endif
static void f_join(typval_T *argvars, typval_T *rettv);
static void f_jsondecode(typval_T *argvars, typval_T *rettv);
static void f_jsonencode(typval_T *argvars, typval_T *rettv);
@@ -3062,10 +3070,11 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
{
switch (tv1->v_type)
{
case VAR_UNKNOWN:
case VAR_DICT:
case VAR_FUNC:
case VAR_SPECIAL:
case VAR_UNKNOWN:
case VAR_JOB:
break;
case VAR_LIST:
@@ -3844,6 +3853,7 @@ item_lock(typval_T *tv, int deep, int lock)
case VAR_FUNC:
case VAR_FLOAT:
case VAR_SPECIAL:
case VAR_JOB:
break;
case VAR_LIST:
@@ -5339,6 +5349,7 @@ eval_index(
return FAIL;
#endif
case VAR_SPECIAL:
case VAR_JOB:
if (verbose)
EMSG(_("E909: Cannot index a special variable"));
return FAIL;
@@ -5446,10 +5457,11 @@ eval_index(
switch (rettv->v_type)
{
case VAR_SPECIAL:
case VAR_UNKNOWN:
case VAR_FUNC:
case VAR_FLOAT:
case VAR_UNKNOWN:
case VAR_SPECIAL:
case VAR_JOB:
break; /* not evaluating, skipping over subscript */
case VAR_NUMBER:
@@ -6167,9 +6179,6 @@ tv_equal(
switch (tv1->v_type)
{
case VAR_UNKNOWN:
break;
case VAR_LIST:
++recursive_cnt;
r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, TRUE);
@@ -6190,11 +6199,6 @@ tv_equal(
case VAR_NUMBER:
return tv1->vval.v_number == tv2->vval.v_number;
#ifdef FEAT_FLOAT
case VAR_FLOAT:
return tv1->vval.v_float == tv2->vval.v_float;
#endif
case VAR_STRING:
s1 = get_tv_string_buf(tv1, buf1);
s2 = get_tv_string_buf(tv2, buf2);
@@ -6202,6 +6206,17 @@ tv_equal(
case VAR_SPECIAL:
return tv1->vval.v_number == tv2->vval.v_number;
case VAR_FLOAT:
#ifdef FEAT_FLOAT
return tv1->vval.v_float == tv2->vval.v_float;
#endif
case VAR_JOB:
#ifdef FEAT_JOB
return tv1->vval.v_job == tv2->vval.v_job;
#endif
case VAR_UNKNOWN:
break;
}
/* VAR_UNKNOWN can be the result of a invalid expression, let's say it
@@ -6924,7 +6939,7 @@ garbage_collect(void)
}
/*
* Free lists and dictionaries that are no longer referenced.
* Free lists, dictionaries and jobs that are no longer referenced.
*/
static int
free_unref_items(int copyID)
@@ -6969,6 +6984,7 @@ free_unref_items(int copyID)
}
ll = ll_next;
}
return did_free;
}
@@ -7694,6 +7710,40 @@ failret:
return OK;
}
#ifdef FEAT_JOB
static void
job_free(job_T *job)
{
/* TODO: free any handles */
vim_free(job);
}
static void
job_unref(job_T *job)
{
if (job != NULL && --job->jv_refcount <= 0)
job_free(job);
}
/*
* Allocate a job. Sets the refcount to one.
*/
static job_T *
job_alloc(void)
{
job_T *job;
job = (job_T *)alloc_clear(sizeof(job_T));
if (job != NULL)
{
job->jv_refcount = 1;
}
return job;
}
#endif
static char *
get_var_special_name(int nr)
{
@@ -7789,12 +7839,13 @@ echo_string(
case VAR_STRING:
case VAR_NUMBER:
case VAR_UNKNOWN:
case VAR_JOB:
*tofree = NULL;
r = get_tv_string_buf(tv, numbuf);
break;
#ifdef FEAT_FLOAT
case VAR_FLOAT:
#ifdef FEAT_FLOAT
*tofree = NULL;
vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv->vval.v_float);
r = numbuf;
@@ -7844,6 +7895,7 @@ tv2string(
case VAR_LIST:
case VAR_DICT:
case VAR_SPECIAL:
case VAR_JOB:
case VAR_UNKNOWN:
break;
}
@@ -8148,6 +8200,11 @@ static struct fst
{"isdirectory", 1, 1, f_isdirectory},
{"islocked", 1, 1, f_islocked},
{"items", 1, 1, f_items},
#ifdef FEAT_CHANNEL
{"job_start", 1, 2, f_job_start},
{"job_status", 1, 1, f_job_status},
{"job_stop", 1, 1, f_job_stop},
#endif
{"join", 1, 2, f_join},
{"jsondecode", 1, 1, f_jsondecode},
{"jsonencode", 1, 1, f_jsonencode},
@@ -10535,8 +10592,8 @@ f_empty(typval_T *argvars, typval_T *rettv)
case VAR_NUMBER:
n = argvars[0].vval.v_number == 0;
break;
#ifdef FEAT_FLOAT
case VAR_FLOAT:
#ifdef FEAT_FLOAT
n = argvars[0].vval.v_float == 0.0;
break;
#endif
@@ -10552,6 +10609,11 @@ f_empty(typval_T *argvars, typval_T *rettv)
n = argvars[0].vval.v_number != VVAL_TRUE;
break;
case VAR_JOB:
#ifdef FEAT_JOB
n = argvars[0].vval.v_job->jv_status != JOB_STARTED;
break;
#endif
case VAR_UNKNOWN:
EMSG2(_(e_intern2), "f_empty(UNKNOWN)");
n = TRUE;
@@ -13060,6 +13122,9 @@ f_has(typval_T *argvars, typval_T *rettv)
#ifdef FEAT_INS_EXPAND
"insert_expand",
#endif
#ifdef FEAT_JOB
"job",
#endif
#ifdef FEAT_JUMPLIST
"jumplist",
#endif
@@ -14188,6 +14253,161 @@ f_items(typval_T *argvars, typval_T *rettv)
dict_list(argvars, rettv, 2);
}
#ifdef FEAT_JOB
/*
* "job_start()" function
*/
static void
f_job_start(typval_T *argvars UNUSED, typval_T *rettv)
{
job_T *job;
char_u *cmd = NULL;
#if defined(UNIX)
# define USE_ARGV
char **argv = NULL;
int argc = 0;
#else
garray_T ga;
#endif
rettv->v_type = VAR_JOB;
job = job_alloc();
rettv->vval.v_job = job;
if (job == NULL)
return;
rettv->vval.v_job->jv_status = JOB_FAILED;
#ifndef USE_ARGV
ga_init2(&ga, 200);
#endif
if (argvars[0].v_type == VAR_STRING)
{
/* Command is a string. */
cmd = argvars[0].vval.v_string;
#ifdef USE_ARGV
if (mch_parse_cmd(cmd, FALSE, &argv, &argc) == FAIL)
return;
argv[argc] = NULL;
#endif
}
else if (argvars[0].v_type != VAR_LIST
|| argvars[0].vval.v_list == NULL
|| argvars[0].vval.v_list->lv_len < 1)
{
EMSG(_(e_invarg));
return;
}
else
{
list_T *l = argvars[0].vval.v_list;
listitem_T *li;
char_u *s;
#ifdef USE_ARGV
/* Pass argv[] to mch_call_shell(). */
argv = (char **)alloc(sizeof(char *) * (l->lv_len + 1));
if (argv == NULL)
return;
#endif
for (li = l->lv_first; li != NULL; li = li->li_next)
{
s = get_tv_string_chk(&li->li_tv);
if (s == NULL)
goto theend;
#ifdef USE_ARGV
argv[argc++] = (char *)s;
#else
if (li != l->lv_first)
{
s = vim_strsave_shellescape(s, FALSE, TRUE);
if (s == NULL)
goto theend;
}
ga_concat(&ga, s);
vim_free(s);
if (li->li_next != NULL)
ga_append(&ga, ' ');
#endif
}
#ifdef USE_ARGV
argv[argc] = NULL;
#else
cmd = ga.ga_data;
#endif
}
#ifdef USE_ARGV
mch_start_job(argv, job);
#else
mch_start_job(cmd, job);
#endif
theend:
#ifdef USE_ARGV
if (argv != NULL)
vim_free(argv);
#else
vim_free(ga.ga_data);
#endif
}
/*
* "job_status()" function
*/
static void
f_job_status(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
char *result;
if (argvars[0].v_type != VAR_JOB)
EMSG(_(e_invarg));
else
{
job_T *job = argvars[0].vval.v_job;
if (job->jv_status == JOB_ENDED)
/* No need to check, dead is dead. */
result = "dead";
else if (job->jv_status == JOB_FAILED)
result = "fail";
else
result = mch_job_status(job);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = vim_strsave((char_u *)result);
}
}
/*
* "job_stop()" function
*/
static void
f_job_stop(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
if (argvars[0].v_type != VAR_JOB)
EMSG(_(e_invarg));
else
{
char_u *arg;
if (argvars[1].v_type == VAR_UNKNOWN)
arg = (char_u *)"";
else
{
arg = get_tv_string_chk(&argvars[1]);
if (arg == NULL)
{
EMSG(_(e_invarg));
return;
}
}
if (mch_stop_job(argvars[0].vval.v_job, arg) == FAIL)
rettv->vval.v_number = 0;
else
rettv->vval.v_number = 1;
}
}
#endif
/*
* "join()" function
*/
@@ -14295,6 +14515,7 @@ f_len(typval_T *argvars, typval_T *rettv)
case VAR_SPECIAL:
case VAR_FLOAT:
case VAR_FUNC:
case VAR_JOB:
EMSG(_("E701: Invalid type for len()"));
break;
}
@@ -19658,6 +19879,9 @@ f_type(typval_T *argvars, typval_T *rettv)
else
n = 7;
break;
#ifdef FEAT_JOB
case VAR_JOB: n = 8; break;
#endif
case VAR_UNKNOWN:
EMSG2(_(e_intern2), "f_type(UNKNOWN)");
n = -1;
@@ -21024,10 +21248,13 @@ free_tv(typval_T *varp)
case VAR_DICT:
dict_unref(varp->vval.v_dict);
break;
case VAR_NUMBER:
#ifdef FEAT_FLOAT
case VAR_FLOAT:
case VAR_JOB:
#ifdef FEAT_JOB
job_unref(varp->vval.v_job);
break;
#endif
case VAR_NUMBER:
case VAR_FLOAT:
case VAR_UNKNOWN:
case VAR_SPECIAL:
break;
@@ -21065,11 +21292,17 @@ clear_tv(typval_T *varp)
case VAR_SPECIAL:
varp->vval.v_number = 0;
break;
#ifdef FEAT_FLOAT
case VAR_FLOAT:
#ifdef FEAT_FLOAT
varp->vval.v_float = 0.0;
break;
#endif
case VAR_JOB:
#ifdef FEAT_JOB
job_unref(varp->vval.v_job);
varp->vval.v_job = NULL;
#endif
break;
case VAR_UNKNOWN:
break;
}
@@ -21112,8 +21345,8 @@ get_tv_number_chk(typval_T *varp, int *denote)
{
case VAR_NUMBER:
return (long)(varp->vval.v_number);
#ifdef FEAT_FLOAT
case VAR_FLOAT:
#ifdef FEAT_FLOAT
EMSG(_("E805: Using a Float as a Number"));
break;
#endif
@@ -21134,6 +21367,11 @@ get_tv_number_chk(typval_T *varp, int *denote)
case VAR_SPECIAL:
return varp->vval.v_number == VVAL_TRUE ? 1 : 0;
break;
case VAR_JOB:
#ifdef FEAT_JOB
EMSG(_("E910: Using a Job as a Number"));
break;
#endif
case VAR_UNKNOWN:
EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)");
break;
@@ -21153,10 +21391,8 @@ get_tv_float(typval_T *varp)
{
case VAR_NUMBER:
return (float_T)(varp->vval.v_number);
#ifdef FEAT_FLOAT
case VAR_FLOAT:
return varp->vval.v_float;
#endif
case VAR_FUNC:
EMSG(_("E891: Using a Funcref as a Float"));
break;
@@ -21172,6 +21408,11 @@ get_tv_float(typval_T *varp)
case VAR_SPECIAL:
EMSG(_("E907: Using a special value as a Float"));
break;
case VAR_JOB:
# ifdef FEAT_JOB
EMSG(_("E911: Using a Job as a Float"));
break;
# endif
case VAR_UNKNOWN:
EMSG2(_(e_intern2), "get_tv_float(UNKNOWN)");
break;
@@ -21272,8 +21513,8 @@ get_tv_string_buf_chk(typval_T *varp, char_u *buf)
case VAR_DICT:
EMSG(_("E731: using Dictionary as a String"));
break;
#ifdef FEAT_FLOAT
case VAR_FLOAT:
#ifdef FEAT_FLOAT
EMSG(_(e_float_as_string));
break;
#endif
@@ -21284,6 +21525,24 @@ get_tv_string_buf_chk(typval_T *varp, char_u *buf)
case VAR_SPECIAL:
STRCPY(buf, get_var_special_name(varp->vval.v_number));
return buf;
case VAR_JOB:
#ifdef FEAT_JOB
{
job_T *job = varp->vval.v_job;
char *status = job->jv_status == JOB_FAILED ? "fail"
: job->jv_status == JOB_ENDED ? "dead"
: "run";
# ifdef UNIX
vim_snprintf((char *)buf, NUMBUFLEN,
"process %ld %s", (long)job->jv_pid, status);
# else
/* TODO */
vim_snprintf((char *)buf, NUMBUFLEN, "process ? %s", status);
# endif
return buf;
}
#endif
break;
case VAR_UNKNOWN:
EMSG(_("E908: using an invalid value as a String"));
break;
@@ -21903,10 +22162,16 @@ copy_tv(typval_T *from, typval_T *to)
case VAR_SPECIAL:
to->vval.v_number = from->vval.v_number;
break;
#ifdef FEAT_FLOAT
case VAR_FLOAT:
#ifdef FEAT_FLOAT
to->vval.v_float = from->vval.v_float;
break;
#endif
case VAR_JOB:
#ifdef FEAT_FLOAT
to->vval.v_job = from->vval.v_job;
++to->vval.v_job->jv_refcount;
break;
#endif
case VAR_STRING:
case VAR_FUNC:
@@ -21970,12 +22235,11 @@ item_copy(
switch (from->v_type)
{
case VAR_NUMBER:
#ifdef FEAT_FLOAT
case VAR_FLOAT:
#endif
case VAR_STRING:
case VAR_FUNC:
case VAR_SPECIAL:
case VAR_JOB:
copy_tv(from, to);
break;
case VAR_LIST:
@@ -24649,6 +24913,7 @@ write_viminfo_varlist(FILE *fp)
case VAR_UNKNOWN:
case VAR_FUNC:
case VAR_JOB:
continue;
}
fprintf(fp, "!%s\t%s\t", this_var->di_key, s);

View File

@@ -1255,12 +1255,19 @@
#endif
/*
* The Channel feature requires +eval.
* The +channel feature requires +eval.
*/
#if !defined(FEAT_EVAL) && defined(FEAT_CHANNEL)
# undef FEAT_CHANNEL
#endif
/*
* The +job feature requires Unix and +eval.
*/
#if defined(UNIX) && defined(FEAT_EVAL)
# define FEAT_JOB
#endif
/*
* +signs Allow signs to be displayed to the left of text lines.
* Adds the ":sign" command.

View File

@@ -3919,6 +3919,66 @@ wait4pid(pid_t child, waitstatus *status)
return wait_pid;
}
#if defined(FEAT_JOB) || !defined(USE_SYSTEM) || defined(PROTO)
int
mch_parse_cmd(char_u *cmd, int use_shcf, char ***argv, int *argc)
{
int i;
char_u *p;
int inquote;
/*
* Do this loop twice:
* 1: find number of arguments
* 2: separate them and build argv[]
*/
for (i = 0; i < 2; ++i)
{
p = cmd;
inquote = FALSE;
*argc = 0;
for (;;)
{
if (i == 1)
(*argv)[*argc] = (char *)p;
++*argc;
while (*p != NUL && (inquote || (*p != ' ' && *p != TAB)))
{
if (*p == '"')
inquote = !inquote;
++p;
}
if (*p == NUL)
break;
if (i == 1)
*p++ = NUL;
p = skipwhite(p);
}
if (*argv == NULL)
{
if (use_shcf)
{
/* Account for possible multiple args in p_shcf. */
p = p_shcf;
for (;;)
{
p = skiptowhite(p);
if (*p == NUL)
break;
++*argc;
p = skipwhite(p);
}
}
*argv = (char **)alloc((unsigned)((*argc + 4) * sizeof(char *)));
if (*argv == NULL) /* out of memory */
return FAIL;
}
}
return OK;
}
#endif
int
mch_call_shell(
char_u *cmd,
@@ -4046,7 +4106,7 @@ mch_call_shell(
# define EXEC_FAILED 122 /* Exit code when shell didn't execute. Don't use
127, some shells use that already */
char_u *newcmd = NULL;
char_u *newcmd;
pid_t pid;
pid_t wpid = 0;
pid_t wait_pid = 0;
@@ -4061,7 +4121,6 @@ mch_call_shell(
char_u *p_shcf_copy = NULL;
int i;
char_u *p;
int inquote;
int pty_master_fd = -1; /* for pty's */
# ifdef FEAT_GUI
int pty_slave_fd = -1;
@@ -4086,53 +4145,9 @@ mch_call_shell(
if (options & SHELL_COOKED)
settmode(TMODE_COOK); /* set to normal mode */
/*
* Do this loop twice:
* 1: find number of arguments
* 2: separate them and build argv[]
*/
for (i = 0; i < 2; ++i)
{
p = newcmd;
inquote = FALSE;
argc = 0;
for (;;)
{
if (i == 1)
argv[argc] = (char *)p;
++argc;
while (*p && (inquote || (*p != ' ' && *p != TAB)))
{
if (*p == '"')
inquote = !inquote;
++p;
}
if (*p == NUL)
break;
if (i == 1)
*p++ = NUL;
p = skipwhite(p);
}
if (argv == NULL)
{
/*
* Account for possible multiple args in p_shcf.
*/
p = p_shcf;
for (;;)
{
p = skiptowhite(p);
if (*p == NUL)
break;
++argc;
p = skipwhite(p);
}
if (mch_parse_cmd(newcmd, TRUE, &argv, &argc) == FAIL)
goto error;
argv = (char **)alloc((unsigned)((argc + 4) * sizeof(char *)));
if (argv == NULL) /* out of memory */
goto error;
}
}
if (cmd != NULL)
{
char_u *s;
@@ -5006,6 +5021,97 @@ error:
#endif /* USE_SYSTEM */
}
#if defined(FEAT_JOB) || defined(PROTO)
void
mch_start_job(char **argv, job_T *job)
{
pid_t pid = fork();
if (pid == -1) /* maybe we should use vfork() */
{
job->jv_status = JOB_FAILED;
}
else if (pid == 0)
{
/* child */
reset_signals(); /* handle signals normally */
# ifdef HAVE_SETSID
/* Create our own process group, so that the child and all its
* children can be kill()ed. Don't do this when using pipes,
* because stdin is not a tty, we would lose /dev/tty. */
(void)setsid();
# endif
/* 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;
}
}
char *
mch_job_status(job_T *job)
{
# ifdef HAVE_UNION_WAIT
union wait status;
# else
int status = -1;
# endif
pid_t wait_pid = 0;
# ifdef __NeXT__
wait_pid = wait4(job->jv_pid, &status, WNOHANG, (struct rusage *)0);
# else
wait_pid = waitpid(job->jv_pid, &status, WNOHANG);
# endif
if (wait_pid == -1)
{
/* process must have exited */
job->jv_status = JOB_ENDED;
return "dead";
}
if (wait_pid == 0)
return "run";
if (WIFEXITED(status))
{
/* LINTED avoid "bitwise operation on signed value" */
job->jv_exitval = WEXITSTATUS(status);
job->jv_status = JOB_ENDED;
return "dead";
}
return "run";
}
int
mch_stop_job(job_T *job, char_u *how)
{
int sig = -1;
if (STRCMP(how, "hup") == 0)
sig = SIGHUP;
else if (*how == NUL || STRCMP(how, "term") == 0)
sig = SIGTERM;
else if (STRCMP(how, "quit") == 0)
sig = SIGQUIT;
else if (STRCMP(how, "kill") == 0)
sig = SIGKILL;
else if (isdigit(*how))
sig = atoi((char *)how);
else
return FAIL;
kill(job->jv_pid, sig);
return OK;
}
#endif
/*
* Check for CTRL-C typed by reading all available characters.
* In cooked mode we should get SIGINT, no need to check.

View File

@@ -55,7 +55,11 @@ int mch_screenmode(char_u *arg);
int mch_get_shellsize(void);
void mch_set_shellsize(void);
void mch_new_shellsize(void);
int mch_parse_cmd(char_u *cmd, int use_shcf, char ***argv, int *argc);
int mch_call_shell(char_u *cmd, int options);
void mch_start_job(char **argv, job_T *job);
char *mch_job_status(job_T *job);
int mch_stop_job(job_T *job, char_u *how);
void mch_breakcheck(void);
int mch_expandpath(garray_T *gap, char_u *path, int flags);
int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u ***file, int flags);

View File

@@ -1110,17 +1110,19 @@ typedef double float_T;
typedef struct listvar_S list_T;
typedef struct dictvar_S dict_T;
typedef struct jobvar_S job_T;
typedef enum
{
VAR_UNKNOWN = 0,
VAR_NUMBER, /* "v_number" is used */
VAR_STRING, /* "v_string" is used */
VAR_FUNC, /* "v_string" is function name */
VAR_LIST, /* "v_list" is used */
VAR_DICT, /* "v_dict" is used */
VAR_FLOAT, /* "v_float" is used */
VAR_SPECIAL /* "v_number" is used */
VAR_NUMBER, /* "v_number" is used */
VAR_STRING, /* "v_string" is used */
VAR_FUNC, /* "v_string" is function name */
VAR_LIST, /* "v_list" is used */
VAR_DICT, /* "v_dict" is used */
VAR_FLOAT, /* "v_float" is used */
VAR_SPECIAL, /* "v_number" is used */
VAR_JOB /* "v_job" is used */
} vartype_T;
/*
@@ -1139,6 +1141,9 @@ typedef struct
char_u *v_string; /* string value (can be NULL!) */
list_T *v_list; /* list value (can be NULL!) */
dict_T *v_dict; /* dict value (can be NULL!) */
#ifdef FEAT_JOB
job_T *v_job; /* job value (can be NULL!) */
#endif
} vval;
} typval_T;
@@ -1204,7 +1209,6 @@ struct dictitem_S
char_u di_flags; /* flags (only used for variable) */
char_u di_key[1]; /* key (actually longer!) */
};
typedef struct dictitem_S dictitem_T;
#define DI_FLAGS_RO 1 /* "di_flags" value: read-only variable */
@@ -1228,6 +1232,30 @@ struct dictvar_S
dict_T *dv_used_prev; /* previous dict in used dicts list */
};
typedef enum
{
JOB_FAILED,
JOB_STARTED,
JOB_ENDED
} jobstatus_T;
/*
* Structure to hold info about a Job.
*/
struct jobvar_S
{
#ifdef UNIX
pid_t jv_pid;
int jv_exitval;
#endif
#ifdef WIN32
PROCESS_INFORMATION jf_pi;
#endif
jobstatus_T jv_status;
int jv_refcount; /* reference count */
};
/* structure used for explicit stack while garbage collecting hash tables */
typedef struct ht_stack_S
{

View File

@@ -8,8 +8,9 @@ endif
" This test requires the Python command to run the test server.
" This most likely only works on Unix and Windows.
if has('unix')
" We also need the pkill command to make sure the server can be stopped.
if !executable('python') || !executable('pkill')
" We also need the job feature or the pkill command to make sure the server
" can be stopped.
if !(executable('python') && (has('job') || executable('pkill')))
finish
endif
elseif has('win32')
@@ -27,7 +28,9 @@ func s:start_server()
" The Python program writes the port number in Xportnr.
call delete("Xportnr")
if has('win32')
if has('job')
let s:job = job_start("python test_channel.py")
elseif has('win32')
silent !start cmd /c start "test_channel" py test_channel.py
else
silent !python test_channel.py&
@@ -62,7 +65,9 @@ func s:start_server()
endfunc
func s:kill_server()
if has('win32')
if has('job')
call job_stop(s:job)
elseif has('win32')
call system('taskkill /IM py.exe /T /F /FI "WINDOWTITLE eq test_channel"')
else
call system("pkill -f test_channel.py")

View File

@@ -289,6 +289,11 @@ static char *(features[]) =
#else
"-insert_expand",
#endif
#ifdef FEAT_JOB
"+job",
#else
"-job",
#endif
#ifdef FEAT_JUMPLIST
"+jumplist",
#else
@@ -742,6 +747,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1274,
/**/
1273,
/**/