diff options
author | Bram Moolenaar <Bram@vim.org> | 2016-02-07 14:27:38 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2016-02-07 14:27:38 +0100 |
commit | 835dc636a5350f610b62f110227d2363b5b2880a (patch) | |
tree | d14dbbe08553b530c8fd593844a34c244a05677d /src | |
parent | c5f98ee987ae0c369867cf6cc581c766d3c0226d (diff) | |
download | vim-835dc636a5350f610b62f110227d2363b5b2880a.zip |
patch 7.4.1274
Problem: Cannot run a job.
Solution: Add job_start(), job_status() and job_stop(). Currently only works
for Unix.
Diffstat (limited to 'src')
-rw-r--r-- | src/eval.c | 313 | ||||
-rw-r--r-- | src/feature.h | 9 | ||||
-rw-r--r-- | src/os_unix.c | 202 | ||||
-rw-r--r-- | src/proto/os_unix.pro | 4 | ||||
-rw-r--r-- | src/structs.h | 44 | ||||
-rw-r--r-- | src/testdir/test_channel.vim | 13 | ||||
-rw-r--r-- | src/version.c | 7 |
7 files changed, 507 insertions, 85 deletions
diff --git a/src/eval.c b/src/eval.c index a50fdbf27..833c41c04 100644 --- a/src/eval.c +++ b/src/eval.c @@ -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_JOB: +#ifdef FEAT_JOB + job_unref(varp->vval.v_job); + break; +#endif case VAR_NUMBER: -#ifdef FEAT_FLOAT case VAR_FLOAT: -#endif 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,11 +22162,17 @@ 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: if (from->vval.v_string == NULL) @@ -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); diff --git a/src/feature.h b/src/feature.h index ca81d8972..19425ed35 100644 --- a/src/feature.h +++ b/src/feature.h @@ -1255,13 +1255,20 @@ #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. */ diff --git a/src/os_unix.c b/src/os_unix.c index 2dbf74f8e..a0e5ed0bc 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -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. diff --git a/src/proto/os_unix.pro b/src/proto/os_unix.pro index 8442dea7a..bc250c87e 100644 --- a/src/proto/os_unix.pro +++ b/src/proto/os_unix.pro @@ -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); diff --git a/src/structs.h b/src/structs.h index d10547acf..5a2d6fdc4 100644 --- a/src/structs.h +++ b/src/structs.h @@ -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 { diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim index 46bd36431..94ad81cca 100644 --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -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") diff --git a/src/version.c b/src/version.c index 9d0258033..ddd733dc2 100644 --- a/src/version.c +++ b/src/version.c @@ -289,6 +289,11 @@ static char *(features[]) = #else "-insert_expand", #endif +#ifdef FEAT_JOB + "+job", +#else + "-job", +#endif #ifdef FEAT_JUMPLIST "+jumplist", #else @@ -743,6 +748,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1274, +/**/ 1273, /**/ 1272, |