# HG changeset patch # User Christian Brabandt # Date 1456069505 -3600 # Node ID a0ffb1f3dedc65debb945c92fd4e5f974bb91f4b # Parent bf0ad2820b486a498364ba7df684f3c7b5270903 commit https://github.com/vim/vim/commit/65edff8f51e9e54f85407bdb9156ae8e3e1b76a1 Author: Bram Moolenaar Date: Sun Feb 21 16:40:11 2016 +0100 patch 7.4.1378 Problem: Can't change job settings after it started. Solution: Add job_setoptions() with the "stoponexit" flag. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -451,9 +451,6 @@ static dict_T *dict_copy(dict_T *orig, i 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); @@ -633,6 +630,7 @@ static void f_items(typval_T *argvars, t # ifdef FEAT_CHANNEL static void f_job_getchannel(typval_T *argvars, typval_T *rettv); # endif +static void f_job_setoptions(typval_T *argvars, typval_T *rettv); static void f_job_start(typval_T *argvars, typval_T *rettv); static void f_job_stop(typval_T *argvars, typval_T *rettv); static void f_job_status(typval_T *argvars, typval_T *rettv); @@ -7751,7 +7749,9 @@ channel_unref(channel_T *channel) } #endif -#ifdef FEAT_JOB +#if defined(FEAT_JOB) || defined(PROTO) +static job_T *first_job = NULL; + static void job_free(job_T *job) { @@ -7765,6 +7765,15 @@ job_free(job_T *job) } # endif mch_clear_job(job); + + if (job->jv_next != NULL) + job->jv_next->jv_prev = job->jv_prev; + if (job->jv_prev == NULL) + first_job = job->jv_next; + else + job->jv_prev->jv_next = job->jv_next; + + vim_free(job->jv_stoponexit); vim_free(job); } @@ -7776,7 +7785,7 @@ job_unref(job_T *job) } /* - * Allocate a job. Sets the refcount to one. + * Allocate a job. Sets the refcount to one and sets options default. */ static job_T * job_alloc(void) @@ -7785,10 +7794,45 @@ job_alloc(void) job = (job_T *)alloc_clear(sizeof(job_T)); if (job != NULL) + { job->jv_refcount = 1; + job->jv_stoponexit = vim_strsave((char_u *)"term"); + + if (first_job != NULL) + { + first_job->jv_prev = job; + job->jv_next = first_job; + } + first_job = job; + } return job; } + static void +job_set_options(job_T *job, jobopt_T *opt) +{ + if (opt->jo_set & JO_STOPONEXIT) + { + vim_free(job->jv_stoponexit); + if (opt->jo_stoponexit == NULL || *opt->jo_stoponexit == NUL) + job->jv_stoponexit = NULL; + else + job->jv_stoponexit = vim_strsave(opt->jo_stoponexit); + } +} + +/* + * Called when Vim is exiting: kill all jobs that have the "stoponexit" flag. + */ + void +job_stop_on_exit() +{ + job_T *job; + + for (job = first_job; job != NULL; job = job->jv_next) + if (job->jv_stoponexit != NULL && *job->jv_stoponexit != NUL) + mch_stop_job(job, job->jv_stoponexit); +} #endif static char * @@ -7797,9 +7841,9 @@ get_var_special_name(int nr) switch (nr) { case VVAL_FALSE: return "v:false"; - case VVAL_TRUE: return "v:true"; - case VVAL_NONE: return "v:none"; - case VVAL_NULL: return "v:null"; + case VVAL_TRUE: return "v:true"; + case VVAL_NONE: return "v:none"; + case VVAL_NULL: return "v:null"; } EMSG2(_(e_intern2), "get_var_special_name()"); return "42"; @@ -8260,6 +8304,7 @@ static struct fst # ifdef FEAT_CHANNEL {"job_getchannel", 1, 1, f_job_getchannel}, # endif + {"job_setoptions", 2, 2, f_job_setoptions}, {"job_start", 1, 2, f_job_start}, {"job_status", 1, 1, f_job_status}, {"job_stop", 1, 2, f_job_stop}, @@ -10050,6 +10095,19 @@ get_job_options(typval_T *tv, jobopt_T * opt->jo_set |= JO_ID; opt->jo_id = get_tv_number(item); } + else if (STRCMP(hi->hi_key, "stoponexit") == 0) + { + if (!(supported & JO_STOPONEXIT)) + break; + opt->jo_set |= JO_STOPONEXIT; + opt->jo_stoponexit = get_tv_string_buf_chk(item, + opt->jo_soe_buf); + if (opt->jo_stoponexit == NULL) + { + EMSG2(_(e_invarg2), "stoponexit"); + return FAIL; + } + } else break; --todo; @@ -14714,6 +14772,26 @@ f_items(typval_T *argvars, typval_T *ret } #ifdef FEAT_JOB +/* + * Get the job from the argument. + * Returns NULL if the job is invalid. + */ + static job_T * +get_job_arg(typval_T *tv) +{ + job_T *job; + + if (tv->v_type != VAR_JOB) + { + EMSG2(_(e_invarg2), get_tv_string(tv)); + return NULL; + } + job = tv->vval.v_job; + + if (job == NULL) + EMSG(_("E916: not a valid job")); + return job; +} # ifdef FEAT_CHANNEL /* @@ -14722,12 +14800,10 @@ f_items(typval_T *argvars, typval_T *ret static void f_job_getchannel(typval_T *argvars, typval_T *rettv) { - if (argvars[0].v_type != VAR_JOB) - EMSG(_(e_invarg)); - else - { - job_T *job = argvars[0].vval.v_job; - + job_T *job = get_job_arg(&argvars[0]); + + if (job != NULL) + { rettv->v_type = VAR_CHANNEL; rettv->vval.v_channel = job->jv_channel; if (job->jv_channel != NULL) @@ -14737,6 +14813,23 @@ f_job_getchannel(typval_T *argvars, typv # endif /* + * "job_setoptions()" function + */ + static void +f_job_setoptions(typval_T *argvars, typval_T *rettv UNUSED) +{ + job_T *job = get_job_arg(&argvars[0]); + jobopt_T opt; + + if (job == NULL) + return; + clear_job_options(&opt); + if (get_job_options(&argvars[1], &opt, JO_STOPONEXIT) == FAIL) + return; + job_set_options(job, &opt); +} + +/* * "job_start()" function */ static void @@ -14765,8 +14858,9 @@ f_job_start(typval_T *argvars UNUSED, ty clear_job_options(&opt); opt.jo_mode = MODE_NL; if (get_job_options(&argvars[1], &opt, - JO_MODE_ALL + JO_CB_ALL + JO_TIMEOUT_ALL) == FAIL) - return; + JO_MODE_ALL + JO_CB_ALL + JO_TIMEOUT_ALL + JO_STOPONEXIT) == FAIL) + return; + job_set_options(job, &opt); #ifndef USE_ARGV ga_init2(&ga, (int)sizeof(char*), 20); @@ -14870,14 +14964,11 @@ theend: static void f_job_status(typval_T *argvars, typval_T *rettv) { - char *result; - - if (argvars[0].v_type != VAR_JOB) - EMSG(_(e_invarg)); - else - { - job_T *job = argvars[0].vval.v_job; - + job_T *job = get_job_arg(&argvars[0]); + char *result; + + if (job != NULL) + { if (job->jv_status == JOB_ENDED) /* No need to check, dead is dead. */ result = "dead"; @@ -14896,9 +14987,9 @@ f_job_status(typval_T *argvars, typval_T static void f_job_stop(typval_T *argvars UNUSED, typval_T *rettv UNUSED) { - if (argvars[0].v_type != VAR_JOB) - EMSG(_(e_invarg)); - else + job_T *job = get_job_arg(&argvars[0]); + + if (job != NULL) { char_u *arg; @@ -14913,7 +15004,7 @@ f_job_stop(typval_T *argvars UNUSED, typ return; } } - if (mch_stop_job(argvars[0].vval.v_job, arg) == FAIL) + if (mch_stop_job(job, arg) == FAIL) rettv->vval.v_number = 0; else rettv->vval.v_number = 1; diff --git a/src/main.c b/src/main.c --- a/src/main.c +++ b/src/main.c @@ -1486,6 +1486,9 @@ getout(int exitval) windgoto((int)Rows - 1, 0); #endif +#ifdef FEAT_JOB + job_stop_on_exit(); +#endif #ifdef FEAT_LUA lua_end(); #endif diff --git a/src/proto/eval.pro b/src/proto/eval.pro --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -80,6 +80,7 @@ dictitem_T *dict_find(dict_T *d, char_u char_u *get_dict_string(dict_T *d, char_u *key, int save); long get_dict_number(dict_T *d, char_u *key); int channel_unref(channel_T *channel); +void job_stop_on_exit(void); int string2float(char_u *text, float_T *value); char_u *get_function_name(expand_T *xp, int idx); char_u *get_expr_name(expand_T *xp, int idx); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1253,6 +1253,8 @@ typedef enum */ struct jobvar_S { + job_T *jv_next; + job_T *jv_prev; #ifdef UNIX pid_t jv_pid; int jv_exitval; @@ -1262,6 +1264,7 @@ struct jobvar_S HANDLE jv_job_object; #endif jobstatus_T jv_status; + char_u *jv_stoponexit; /* allocated */ int jv_refcount; /* reference count */ channel_T *jv_channel; /* channel for I/O, reference counted */ @@ -1386,6 +1389,7 @@ struct channel_S { #define JO_ERR_TIMEOUT 0x0400 /* stderr timeouts */ #define JO_PART 0x0800 /* "part" */ #define JO_ID 0x1000 /* "id" */ +#define JO_STOPONEXIT 0x2000 /* "stoponexit" */ #define JO_ALL 0xffffff #define JO_MODE_ALL (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE) @@ -1412,6 +1416,8 @@ typedef struct int jo_err_timeout; int jo_part; int jo_id; + char_u jo_soe_buf[NUMBUFLEN]; + char_u *jo_stoponexit; } jobopt_T; diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -44,7 +44,8 @@ func s:run_server(testfunc, ...) try if has('job') - let s:job = job_start(cmd) + let s:job = job_start(cmd, {"stoponexit": "hup"}) + call job_setoptions(s:job, {"stoponexit": "kill"}) elseif has('win32') exe 'silent !start cmd /c start "test_channel" ' . cmd else diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -748,6 +748,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1378, +/**/ 1377, /**/ 1376,