Patch 7.4.1274
Problem: Cannot run a job.
Solution: Add job_start(), job_status() and job_stop(). Currently only works
for Unix.
Files: eval.c, structs.h, runtime/doc/eval.txt, src/os_unix.c,
src/proto/
os_unix.pro, src/feature.h, src/version.c,
src/testdir/test_channel.vim
*** ../vim-7.4.1274/src/eval.c 2016-02-07 00:00:30.576064995 +0100
--- src/eval.c 2016-02-07 13:59:00.784058601 +0100
***************
*** 451,456 ****
--- 451,459 ----
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,624 ****
--- 622,632 ----
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,3071 ****
{
switch (tv1->v_type)
{
case VAR_DICT:
case VAR_FUNC:
case VAR_SPECIAL:
! case VAR_UNKNOWN:
break;
case VAR_LIST:
--- 3070,3080 ----
{
switch (tv1->v_type)
{
+ case VAR_UNKNOWN:
case VAR_DICT:
case VAR_FUNC:
case VAR_SPECIAL:
! case VAR_JOB:
break;
case VAR_LIST:
***************
*** 3844,3849 ****
--- 3853,3859 ----
case VAR_FUNC:
case VAR_FLOAT:
case VAR_SPECIAL:
+ case VAR_JOB:
break;
case VAR_LIST:
***************
*** 5339,5344 ****
--- 5349,5355 ----
return FAIL;
#endif
case VAR_SPECIAL:
+ case VAR_JOB:
if (verbose)
EMSG(_("E909: Cannot index a special variable"));
return FAIL;
***************
*** 5446,5455 ****
switch (rettv->v_type)
{
! case VAR_SPECIAL:
case VAR_FUNC:
case VAR_FLOAT:
! case VAR_UNKNOWN:
break; /* not evaluating, skipping over subscript */
case VAR_NUMBER:
--- 5457,5467 ----
switch (rettv->v_type)
{
! case VAR_UNKNOWN:
case VAR_FUNC:
case VAR_FLOAT:
! case VAR_SPECIAL:
! case VAR_JOB:
break; /* not evaluating, skipping over subscript */
case VAR_NUMBER:
***************
*** 6167,6175 ****
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);
--- 6179,6184 ----
***************
*** 6190,6200 ****
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);
--- 6199,6204 ----
***************
*** 6202,6207 ****
--- 6206,6222 ----
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,6930 ****
}
/*
! * Free lists and dictionaries that are no longer referenced.
*/
static int
free_unref_items(int copyID)
--- 6939,6945 ----
}
/*
! * Free lists, dictionaries and jobs that are no longer referenced.
*/
static int
free_unref_items(int copyID)
***************
*** 6969,6974 ****
--- 6984,6990 ----
}
ll = ll_next;
}
+
return did_free;
}
***************
*** 7694,7699 ****
--- 7710,7749 ----
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,7800 ****
case VAR_STRING:
case VAR_NUMBER:
case VAR_UNKNOWN:
*tofree = NULL;
r = get_tv_string_buf(tv, numbuf);
break;
- #ifdef FEAT_FLOAT
case VAR_FLOAT:
*tofree = NULL;
vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv->vval.v_float);
r = numbuf;
--- 7839,7851 ----
case VAR_STRING:
case VAR_NUMBER:
case VAR_UNKNOWN:
+ case VAR_JOB:
*tofree = NULL;
r = get_tv_string_buf(tv, numbuf);
break;
case VAR_FLOAT:
+ #ifdef FEAT_FLOAT
*tofree = NULL;
vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv->vval.v_float);
r = numbuf;
***************
*** 7844,7849 ****
--- 7895,7901 ----
case VAR_LIST:
case VAR_DICT:
case VAR_SPECIAL:
+ case VAR_JOB:
case VAR_UNKNOWN:
break;
}
***************
*** 8148,8153 ****
--- 8200,8210 ----
{"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,10542 ****
case VAR_NUMBER:
n = argvars[0].vval.v_number == 0;
break;
- #ifdef FEAT_FLOAT
case VAR_FLOAT:
n = argvars[0].vval.v_float == 0.0;
break;
#endif
--- 10592,10599 ----
case VAR_NUMBER:
n = argvars[0].vval.v_number == 0;
break;
case VAR_FLOAT:
+ #ifdef FEAT_FLOAT
n = argvars[0].vval.v_float == 0.0;
break;
#endif
***************
*** 10552,10557 ****
--- 10609,10619 ----
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,13065 ****
--- 13122,13130 ----
#ifdef FEAT_INS_EXPAND
"insert_expand",
#endif
+ #ifdef FEAT_JOB
+ "job",
+ #endif
#ifdef FEAT_JUMPLIST
"jumplist",
#endif
***************
*** 14188,14193 ****
--- 14253,14413 ----
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,14300 ****
--- 14515,14521 ----
case VAR_SPECIAL:
case VAR_FLOAT:
case VAR_FUNC:
+ case VAR_JOB:
EMSG(_("E701: Invalid type for len()"));
break;
}
***************
*** 19658,19663 ****
--- 19879,19887 ----
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,21033 ****
case VAR_DICT:
dict_unref(varp->vval.v_dict);
break;
case VAR_NUMBER:
- #ifdef FEAT_FLOAT
case VAR_FLOAT:
- #endif
case VAR_UNKNOWN:
case VAR_SPECIAL:
break;
--- 21248,21260 ----
case VAR_DICT:
dict_unref(varp->vval.v_dict);
break;
+ 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,21075 ****
case VAR_SPECIAL:
varp->vval.v_number = 0;
break;
- #ifdef FEAT_FLOAT
case VAR_FLOAT:
varp->vval.v_float = 0.0;
break;
#endif
case VAR_UNKNOWN:
break;
}
--- 21292,21308 ----
case VAR_SPECIAL:
varp->vval.v_number = 0;
break;
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,21119 ****
{
case VAR_NUMBER:
return (long)(varp->vval.v_number);
- #ifdef FEAT_FLOAT
case VAR_FLOAT:
EMSG(_("E805: Using a Float as a Number"));
break;
#endif
--- 21345,21352 ----
{
case VAR_NUMBER:
return (long)(varp->vval.v_number);
case VAR_FLOAT:
+ #ifdef FEAT_FLOAT
EMSG(_("E805: Using a Float as a Number"));
break;
#endif
***************
*** 21134,21139 ****
--- 21367,21377 ----
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,21162 ****
{
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;
--- 21391,21398 ----
***************
*** 21172,21177 ****
--- 21408,21418 ----
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,21279 ****
case VAR_DICT:
EMSG(_("E731: using Dictionary as a String"));
break;
- #ifdef FEAT_FLOAT
case VAR_FLOAT:
EMSG(_(e_float_as_string));
break;
#endif
--- 21513,21520 ----
case VAR_DICT:
EMSG(_("E731: using Dictionary as a String"));
break;
case VAR_FLOAT:
+ #ifdef FEAT_FLOAT
EMSG(_(e_float_as_string));
break;
#endif
***************
*** 21284,21289 ****
--- 21525,21548 ----
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,21913 ****
case VAR_SPECIAL:
to->vval.v_number = from->vval.v_number;
break;
- #ifdef FEAT_FLOAT
case VAR_FLOAT:
to->vval.v_float = from->vval.v_float;
break;
#endif
case VAR_STRING:
case VAR_FUNC:
if (from->vval.v_string == NULL)
--- 22162,22178 ----
case VAR_SPECIAL:
to->vval.v_number = from->vval.v_number;
break;
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:
if (from->vval.v_string == NULL)
***************
*** 21970,21981 ****
switch (from->v_type)
{
case VAR_NUMBER:
- #ifdef FEAT_FLOAT
case VAR_FLOAT:
- #endif
case VAR_STRING:
case VAR_FUNC:
case VAR_SPECIAL:
copy_tv(from, to);
break;
case VAR_LIST:
--- 22235,22245 ----
switch (from->v_type)
{
case VAR_NUMBER:
case VAR_FLOAT:
case VAR_STRING:
case VAR_FUNC:
case VAR_SPECIAL:
+ case VAR_JOB:
copy_tv(from, to);
break;
case VAR_LIST:
***************
*** 24649,24654 ****
--- 24913,24919 ----
case VAR_UNKNOWN:
case VAR_FUNC:
+ case VAR_JOB:
continue;
}
fprintf(fp, "!%s\t%s\t", this_var->di_key, s);
*** ../vim-7.4.1274/src/structs.h 2016-02-06 18:09:53.067952958 +0100
--- src/structs.h 2016-02-07 13:56:50.617415037 +0100
***************
*** 1110,1126 ****
typedef struct listvar_S list_T;
typedef struct dictvar_S dict_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 */
} vartype_T;
/*
--- 1110,1128 ----
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_JOB /* "v_job" is used */
} vartype_T;
/*
***************
*** 1139,1144 ****
--- 1141,1149 ----
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,1210 ****
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 */
--- 1209,1214 ----
***************
*** 1228,1233 ****
--- 1232,1261 ----
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
{
*** ../vim-7.4.1274/runtime/doc/eval.txt 2016-02-07 14:23:21.744830821 +0100
--- runtime/doc/eval.txt 2016-02-07 14:10:33.428839814 +0100
***************
*** 59,64 ****
--- 56,68 ----
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.
***************
*** 1922,1927 ****
--- 1952,1960 ----
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
***************
*** 4200,4205 ****
--- 4303,4375 ----
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
*** ../vim-7.4.1274/src/os_unix.c 2016-02-07 14:23:21.744830821 +0100
--- src/os_unix.c 2016-02-07 13:46:32.215854387 +0100
***************
*** 3919,3924 ****
--- 3919,3984 ----
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,4052 ****
# define EXEC_FAILED 122 /* Exit code when shell didn't execute. Don't use
127, some shells use that already */
! char_u *newcmd = NULL;
pid_t pid;
pid_t wpid = 0;
pid_t wait_pid = 0;
--- 4106,4112 ----
# define EXEC_FAILED 122 /* Exit code when shell didn't execute. Don't use
127, some shells use that already */
! char_u *newcmd;
pid_t pid;
pid_t wpid = 0;
pid_t wait_pid = 0;
***************
*** 4061,4067 ****
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;
--- 4121,4126 ----
***************
*** 4086,4138 ****
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);
! }
- argv = (char **)alloc((unsigned)((argc + 4) * sizeof(char *)));
- if (argv == NULL) /* out of memory */
- goto error;
- }
- }
if (cmd != NULL)
{
char_u *s;
--- 4145,4153 ----
if (options & SHELL_COOKED)
settmode(TMODE_COOK); /* set to normal mode */
! if (mch_parse_cmd(newcmd, TRUE, &argv, &argc) == FAIL)
! goto error;
if (cmd != NULL)
{
char_u *s;
***************
*** 5006,5011 ****
--- 5021,5117 ----
#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.
*** ../vim-7.4.1274/src/proto/
os_unix.pro 2016-02-07 14:23:21.744830821 +0100
--- src/proto/
os_unix.pro 2016-02-07 13:42:04.306643148 +0100
***************
*** 55,61 ****
--- 55,65 ----
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);
*** ../vim-7.4.1274/src/feature.h 2016-02-07 14:23:21.744830821 +0100
--- src/feature.h 2016-02-06 22:07:35.019457345 +0100
***************
*** 1255,1267 ****
#endif
/*
! * The Channel feature requires +eval.
*/
#if !defined(FEAT_EVAL) && defined(FEAT_CHANNEL)
# undef FEAT_CHANNEL
#endif
/*
* +signs Allow signs to be displayed to the left of text lines.
* Adds the ":sign" command.
*/
--- 1255,1274 ----
#endif
/*
! * 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.
*/
*** ../vim-7.4.1274/src/version.c 2016-02-07 14:23:21.744830821 +0100
--- src/version.c 2016-02-07 14:15:33.405713009 +0100
***************
*** 289,294 ****
--- 289,299 ----
#else
"-insert_expand",
#endif
+ #ifdef FEAT_JOB
+ "+job",
+ #else
+ "-job",
+ #endif
#ifdef FEAT_JUMPLIST
"+jumplist",
#else
*** ../vim-7.4.1274/src/testdir/test_channel.vim 2016-02-07 14:23:21.744830821 +0100
--- src/testdir/test_channel.vim 2016-02-07 14:08:57.777836779 +0100
***************
*** 8,15 ****
" 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')
finish
endif
elseif has('win32')
--- 8,16 ----
" 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 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,33 ****
" The Python program writes the port number in Xportnr.
call delete("Xportnr")
! if has('win32')
silent !start cmd /c start "test_channel" py test_channel.py
else
silent !python test_channel.py&
--- 28,36 ----
" The Python program writes the port number in Xportnr.
call delete("Xportnr")
! 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,68 ****
endfunc
func s:kill_server()
! if has('win32')
call system('taskkill /IM py.exe /T /F /FI "WINDOWTITLE eq test_channel"')
else
call system("pkill -f test_channel.py")
--- 65,73 ----
endfunc
func s:kill_server()
! 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")
*** ../vim-7.4.1274/src/version.c 2016-02-07 14:23:21.744830821 +0100
--- src/version.c 2016-02-07 14:15:33.405713009 +0100
***************
*** 744,745 ****
--- 749,752 ----
{ /* Add new patch number below this line */
+ /**/
+ 1274,
/**/
--
hundred-and-one symptoms of being an internet addict:
167. You have more than 200 websites bookmarked.
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language --
http://www.Zimbu.org ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///