Hi Bram! On Sun, Feb 7, 2016 at 10:27 PM, Bram Moolenaar <[email protected]> wrote: > > 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 -- [email protected] -- 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 /// > > -- > -- > You received this message from the "vim_dev" maillist. > Do not top-post! Type your reply below the text you are replying to. > For more information, visit http://www.vim.org/maillist.php > > --- > You received this message because you are subscribed to the Google Groups > "vim_dev" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > For more options, visit https://groups.google.com/d/optout.
This is a really big news :) Do you have a plan to add more channel functions, like * Output to a spawned process's stdin * Input from a spawned process's stdout / stderr I think it is useful if Vim has more communication APIs. -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
