Patch 7.4.1539
Problem: Too much code in eval.c.
Solution: Move job and channel code to channel.c.
Files: src/eval.c, src/channel.c, src/proto/channel.pro,
src/proto/eval.pro
*** ../vim-7.4.1538/src/eval.c 2016-03-11 22:52:00.734438114 +0100
--- src/eval.c 2016-03-12 13:33:22.054745522 +0100
***************
*** 836,849 ****
static int handle_subscript(char_u **arg, typval_T *rettv, int evaluate, int
verbose);
static typval_T *alloc_string_tv(char_u *string);
static void init_tv(typval_T *varp);
- static long get_tv_number(typval_T *varp);
#ifdef FEAT_FLOAT
static float_T get_tv_float(typval_T *varp);
#endif
static linenr_T get_tv_lnum(typval_T *argvars);
static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf);
- static char_u *get_tv_string(typval_T *varp);
- static char_u *get_tv_string_buf(typval_T *varp, char_u *buf);
static dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload);
static dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname,
int no_autoload);
static hashtab_T *find_var_ht(char_u *name, char_u **varname);
--- 836,846 ----
***************
*** 7735,7863 ****
}
#if defined(FEAT_JOB_CHANNEL) || defined(PROTO)
- /*
- * Decrement the reference count on "channel" and maybe free it when it goes
- * down to zero. Don't free it if there is a pending action.
- * Returns TRUE when the channel is no longer referenced.
- */
- int
- channel_unref(channel_T *channel)
- {
- if (channel != NULL && --channel->ch_refcount <= 0)
- return channel_may_free(channel);
- return FALSE;
- }
-
- static job_T *first_job = NULL;
-
- static void
- job_free(job_T *job)
- {
- ch_log(job->jv_channel, "Freeing job");
- if (job->jv_channel != NULL)
- {
- /* The link from the channel to the job doesn't count as a reference,
- * thus don't decrement the refcount of the job. The reference from
- * the job to the channel does count the refrence, decrement it and
- * NULL the reference. We don't set ch_job_killed, unreferencing the
- * job doesn't mean it stops running. */
- job->jv_channel->ch_job = NULL;
- channel_unref(job->jv_channel);
- }
- 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->jv_exit_cb);
- vim_free(job);
- }
-
- static void
- job_unref(job_T *job)
- {
- if (job != NULL && --job->jv_refcount <= 0)
- {
- /* Do not free the job when it has not ended yet and there is a
- * "stoponexit" flag or an exit callback. */
- if (job->jv_status != JOB_STARTED
- || (job->jv_stoponexit == NULL && job->jv_exit_cb == NULL))
- {
- job_free(job);
- }
- else if (job->jv_channel != NULL)
- {
- /* Do remove the link to the channel, otherwise it hangs
- * around until Vim exits. See job_free() for refcount. */
- job->jv_channel->ch_job = NULL;
- channel_unref(job->jv_channel);
- job->jv_channel = NULL;
- }
- }
- }
-
- /*
- * Allocate a job. Sets the refcount to one and sets options default.
- */
- static job_T *
- job_alloc(void)
- {
- job_T *job;
-
- 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);
- }
- if (opt->jo_set & JO_EXIT_CB)
- {
- vim_free(job->jv_exit_cb);
- if (opt->jo_exit_cb == NULL || *opt->jo_exit_cb == NUL)
- job->jv_exit_cb = NULL;
- else
- job->jv_exit_cb = vim_strsave(opt->jo_exit_cb);
- }
- }
-
- /*
- * 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_status == JOB_STARTED && job->jv_stoponexit != NULL)
- mch_stop_job(job, job->jv_stoponexit);
- }
#endif
static char *
--- 7732,7737 ----
***************
*** 9650,9656 ****
rettv->vval.v_number = (buf != NULL && buf->b_ml.ml_mfp != NULL);
}
! static buf_T *
buflist_find_by_name(char_u *name, int curtab_only)
{
int save_magic;
--- 9524,9530 ----
rettv->vval.v_number = (buf != NULL && buf->b_ml.ml_mfp != NULL);
}
! buf_T *
buflist_find_by_name(char_u *name, int curtab_only)
{
int save_magic;
***************
*** 9941,10337 ****
}
#endif
- #if defined(FEAT_JOB_CHANNEL)
- /*
- * Get a callback from "arg". It can be a Funcref or a function name.
- * When "arg" is zero return an empty string.
- * Return NULL for an invalid argument.
- */
- static char_u *
- get_callback(typval_T *arg)
- {
- if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING)
- return arg->vval.v_string;
- if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0)
- return (char_u *)"";
- EMSG(_("E999: Invalid callback argument"));
- return NULL;
- }
-
- static int
- handle_mode(typval_T *item, jobopt_T *opt, ch_mode_T *modep, int jo)
- {
- char_u *val = get_tv_string(item);
-
- opt->jo_set |= jo;
- if (STRCMP(val, "nl") == 0)
- *modep = MODE_NL;
- else if (STRCMP(val, "raw") == 0)
- *modep = MODE_RAW;
- else if (STRCMP(val, "js") == 0)
- *modep = MODE_JS;
- else if (STRCMP(val, "json") == 0)
- *modep = MODE_JSON;
- else
- {
- EMSG2(_(e_invarg2), val);
- return FAIL;
- }
- return OK;
- }
-
- static int
- handle_io(typval_T *item, int part, jobopt_T *opt)
- {
- char_u *val = get_tv_string(item);
-
- opt->jo_set |= JO_OUT_IO << (part - PART_OUT);
- if (STRCMP(val, "null") == 0)
- opt->jo_io[part] = JIO_NULL;
- else if (STRCMP(val, "pipe") == 0)
- opt->jo_io[part] = JIO_PIPE;
- else if (STRCMP(val, "file") == 0)
- opt->jo_io[part] = JIO_FILE;
- else if (STRCMP(val, "buffer") == 0)
- opt->jo_io[part] = JIO_BUFFER;
- else if (STRCMP(val, "out") == 0 && part == PART_ERR)
- opt->jo_io[part] = JIO_OUT;
- else
- {
- EMSG2(_(e_invarg2), val);
- return FAIL;
- }
- return OK;
- }
-
- static void
- clear_job_options(jobopt_T *opt)
- {
- vim_memset(opt, 0, sizeof(jobopt_T));
- }
-
- /*
- * Get the PART_ number from the first character of an option name.
- */
- static int
- part_from_char(int c)
- {
- return c == 'i' ? PART_IN : c == 'o' ? PART_OUT: PART_ERR;
- }
-
- /*
- * Get the option entries from the dict in "tv", parse them and put the result
- * in "opt".
- * Only accept options in "supported".
- * If an option value is invalid return FAIL.
- */
- static int
- get_job_options(typval_T *tv, jobopt_T *opt, int supported)
- {
- typval_T *item;
- char_u *val;
- dict_T *dict;
- int todo;
- hashitem_T *hi;
- int part;
-
- opt->jo_set = 0;
- if (tv->v_type == VAR_UNKNOWN)
- return OK;
- if (tv->v_type != VAR_DICT)
- {
- EMSG(_(e_invarg));
- return FAIL;
- }
- dict = tv->vval.v_dict;
- if (dict == NULL)
- return OK;
-
- todo = (int)dict->dv_hashtab.ht_used;
- for (hi = dict->dv_hashtab.ht_array; todo > 0; ++hi)
- if (!HASHITEM_EMPTY(hi))
- {
- item = &HI2DI(hi)->di_tv;
-
- if (STRCMP(hi->hi_key, "mode") == 0)
- {
- if (!(supported & JO_MODE))
- break;
- if (handle_mode(item, opt, &opt->jo_mode, JO_MODE) == FAIL)
- return FAIL;
- }
- else if (STRCMP(hi->hi_key, "in-mode") == 0)
- {
- if (!(supported & JO_IN_MODE))
- break;
- if (handle_mode(item, opt, &opt->jo_in_mode, JO_IN_MODE)
- == FAIL)
- return FAIL;
- }
- else if (STRCMP(hi->hi_key, "out-mode") == 0)
- {
- if (!(supported & JO_OUT_MODE))
- break;
- if (handle_mode(item, opt, &opt->jo_out_mode, JO_OUT_MODE)
- == FAIL)
- return FAIL;
- }
- else if (STRCMP(hi->hi_key, "err-mode") == 0)
- {
- if (!(supported & JO_ERR_MODE))
- break;
- if (handle_mode(item, opt, &opt->jo_err_mode, JO_ERR_MODE)
- == FAIL)
- return FAIL;
- }
- else if (STRCMP(hi->hi_key, "in-io") == 0
- || STRCMP(hi->hi_key, "out-io") == 0
- || STRCMP(hi->hi_key, "err-io") == 0)
- {
- if (!(supported & JO_OUT_IO))
- break;
- if (handle_io(item, part_from_char(*hi->hi_key), opt) == FAIL)
- return FAIL;
- }
- else if (STRCMP(hi->hi_key, "in-name") == 0
- || STRCMP(hi->hi_key, "out-name") == 0
- || STRCMP(hi->hi_key, "err-name") == 0)
- {
- part = part_from_char(*hi->hi_key);
-
- if (!(supported & JO_OUT_IO))
- break;
- opt->jo_set |= JO_OUT_NAME << (part - PART_OUT);
- opt->jo_io_name[part] =
- get_tv_string_buf_chk(item, opt->jo_io_name_buf[part]);
- }
- else if (STRCMP(hi->hi_key, "in-buf") == 0
- || STRCMP(hi->hi_key, "out-buf") == 0
- || STRCMP(hi->hi_key, "err-buf") == 0)
- {
- part = part_from_char(*hi->hi_key);
-
- if (!(supported & JO_OUT_IO))
- break;
- opt->jo_set |= JO_OUT_BUF << (part - PART_OUT);
- opt->jo_io_buf[part] = get_tv_number(item);
- if (opt->jo_io_buf[part] <= 0)
- {
- EMSG2(_(e_invarg2), get_tv_string(item));
- return FAIL;
- }
- if (buflist_findnr(opt->jo_io_buf[part]) == NULL)
- {
- EMSGN(_(e_nobufnr), (long)opt->jo_io_buf[part]);
- return FAIL;
- }
- }
- else if (STRCMP(hi->hi_key, "in-top") == 0
- || STRCMP(hi->hi_key, "in-bot") == 0)
- {
- linenr_T *lp;
-
- if (!(supported & JO_OUT_IO))
- break;
- if (hi->hi_key[3] == 't')
- {
- lp = &opt->jo_in_top;
- opt->jo_set |= JO_IN_TOP;
- }
- else
- {
- lp = &opt->jo_in_bot;
- opt->jo_set |= JO_IN_BOT;
- }
- *lp = get_tv_number(item);
- if (*lp < 0)
- {
- EMSG2(_(e_invarg2), get_tv_string(item));
- return FAIL;
- }
- }
- else if (STRCMP(hi->hi_key, "channel") == 0)
- {
- if (!(supported & JO_OUT_IO))
- break;
- opt->jo_set |= JO_CHANNEL;
- if (item->v_type != VAR_CHANNEL)
- {
- EMSG2(_(e_invarg2), "channel");
- return FAIL;
- }
- opt->jo_channel = item->vval.v_channel;
- }
- else if (STRCMP(hi->hi_key, "callback") == 0)
- {
- if (!(supported & JO_CALLBACK))
- break;
- opt->jo_set |= JO_CALLBACK;
- opt->jo_callback = get_callback(item);
- if (opt->jo_callback == NULL)
- {
- EMSG2(_(e_invarg2), "callback");
- return FAIL;
- }
- }
- else if (STRCMP(hi->hi_key, "out-cb") == 0)
- {
- if (!(supported & JO_OUT_CALLBACK))
- break;
- opt->jo_set |= JO_OUT_CALLBACK;
- opt->jo_out_cb = get_callback(item);
- if (opt->jo_out_cb == NULL)
- {
- EMSG2(_(e_invarg2), "out-cb");
- return FAIL;
- }
- }
- else if (STRCMP(hi->hi_key, "err-cb") == 0)
- {
- if (!(supported & JO_ERR_CALLBACK))
- break;
- opt->jo_set |= JO_ERR_CALLBACK;
- opt->jo_err_cb = get_callback(item);
- if (opt->jo_err_cb == NULL)
- {
- EMSG2(_(e_invarg2), "err-cb");
- return FAIL;
- }
- }
- else if (STRCMP(hi->hi_key, "close-cb") == 0)
- {
- if (!(supported & JO_CLOSE_CALLBACK))
- break;
- opt->jo_set |= JO_CLOSE_CALLBACK;
- opt->jo_close_cb = get_callback(item);
- if (opt->jo_close_cb == NULL)
- {
- EMSG2(_(e_invarg2), "close-cb");
- return FAIL;
- }
- }
- else if (STRCMP(hi->hi_key, "waittime") == 0)
- {
- if (!(supported & JO_WAITTIME))
- break;
- opt->jo_set |= JO_WAITTIME;
- opt->jo_waittime = get_tv_number(item);
- }
- else if (STRCMP(hi->hi_key, "timeout") == 0)
- {
- if (!(supported & JO_TIMEOUT))
- break;
- opt->jo_set |= JO_TIMEOUT;
- opt->jo_timeout = get_tv_number(item);
- }
- else if (STRCMP(hi->hi_key, "out-timeout") == 0)
- {
- if (!(supported & JO_OUT_TIMEOUT))
- break;
- opt->jo_set |= JO_OUT_TIMEOUT;
- opt->jo_out_timeout = get_tv_number(item);
- }
- else if (STRCMP(hi->hi_key, "err-timeout") == 0)
- {
- if (!(supported & JO_ERR_TIMEOUT))
- break;
- opt->jo_set |= JO_ERR_TIMEOUT;
- opt->jo_err_timeout = get_tv_number(item);
- }
- else if (STRCMP(hi->hi_key, "part") == 0)
- {
- if (!(supported & JO_PART))
- break;
- opt->jo_set |= JO_PART;
- val = get_tv_string(item);
- if (STRCMP(val, "err") == 0)
- opt->jo_part = PART_ERR;
- else
- {
- EMSG2(_(e_invarg2), val);
- return FAIL;
- }
- }
- else if (STRCMP(hi->hi_key, "id") == 0)
- {
- if (!(supported & JO_ID))
- break;
- 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 if (STRCMP(hi->hi_key, "exit-cb") == 0)
- {
- if (!(supported & JO_EXIT_CB))
- break;
- opt->jo_set |= JO_EXIT_CB;
- opt->jo_exit_cb = get_tv_string_buf_chk(item, opt->jo_ecb_buf);
- if (opt->jo_exit_cb == NULL)
- {
- EMSG2(_(e_invarg2), "exit-cb");
- return FAIL;
- }
- }
- else
- break;
- --todo;
- }
- if (todo > 0)
- {
- EMSG2(_(e_invarg2), hi->hi_key);
- return FAIL;
- }
-
- return OK;
- }
- #endif
-
#ifdef FEAT_JOB_CHANNEL
/*
- * Get the channel from the argument.
- * Returns NULL if the handle is invalid.
- */
- static channel_T *
- get_channel_arg(typval_T *tv, int check_open)
- {
- channel_T *channel = NULL;
-
- if (tv->v_type == VAR_JOB)
- {
- if (tv->vval.v_job != NULL)
- channel = tv->vval.v_job->jv_channel;
- }
- else if (tv->v_type == VAR_CHANNEL)
- {
- channel = tv->vval.v_channel;
- }
- else
- {
- EMSG2(_(e_invarg2), get_tv_string(tv));
- return NULL;
- }
-
- if (check_open && (channel == NULL || !channel_is_open(channel)))
- {
- EMSG(_("E906: not an open channel"));
- return NULL;
- }
- return channel;
- }
-
- /*
* "ch_close()" function
*/
static void
--- 9815,9822 ----
***************
*** 10366,10681 ****
part = PART_OUT;
else if (STRCMP(what, "in") == 0)
part = PART_IN;
! else
! part = PART_SOCK;
! if (channel->ch_part[part].ch_buffer != NULL)
! rettv->vval.v_number = channel->ch_part[part].ch_buffer->b_fnum;
! }
! }
!
! /*
! * "ch_getjob()" function
! */
! static void
! f_ch_getjob(typval_T *argvars, typval_T *rettv)
! {
! channel_T *channel = get_channel_arg(&argvars[0], TRUE);
!
! if (channel != NULL)
! {
! rettv->v_type = VAR_JOB;
! rettv->vval.v_job = channel->ch_job;
! if (channel->ch_job != NULL)
! ++channel->ch_job->jv_refcount;
! }
! }
!
! /*
! * "ch_log()" function
! */
! static void
! f_ch_log(typval_T *argvars, typval_T *rettv UNUSED)
! {
! char_u *msg = get_tv_string(&argvars[0]);
! channel_T *channel = NULL;
!
! if (argvars[1].v_type != VAR_UNKNOWN)
! channel = get_channel_arg(&argvars[1], TRUE);
!
! ch_log(channel, (char *)msg);
! }
!
! /*
! * "ch_logfile()" function
! */
! static void
! f_ch_logfile(typval_T *argvars, typval_T *rettv UNUSED)
! {
! char_u *fname;
! char_u *opt = (char_u *)"";
! char_u buf[NUMBUFLEN];
! FILE *file = NULL;
!
! fname = get_tv_string(&argvars[0]);
! if (argvars[1].v_type == VAR_STRING)
! opt = get_tv_string_buf(&argvars[1], buf);
! if (*fname != NUL)
! {
! file = fopen((char *)fname, *opt == 'w' ? "w" : "a");
! if (file == NULL)
! {
! EMSG2(_(e_notopen), fname);
! return;
! }
! }
! ch_logfile(file);
! }
!
! /*
! * "ch_open()" function
! */
! static void
! f_ch_open(typval_T *argvars, typval_T *rettv)
! {
! char_u *address;
! char_u *p;
! char *rest;
! int port;
! jobopt_T opt;
! channel_T *channel;
!
! /* default: fail */
! rettv->v_type = VAR_CHANNEL;
! rettv->vval.v_channel = NULL;
!
! address = get_tv_string(&argvars[0]);
! if (argvars[1].v_type != VAR_UNKNOWN
! && (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL))
! {
! EMSG(_(e_invarg));
! return;
! }
!
! /* parse address */
! p = vim_strchr(address, ':');
! if (p == NULL)
! {
! EMSG2(_(e_invarg2), address);
! return;
! }
! *p++ = NUL;
! port = strtol((char *)p, &rest, 10);
! if (*address == NUL || port <= 0 || *rest != NUL)
! {
! p[-1] = ':';
! EMSG2(_(e_invarg2), address);
! return;
! }
!
! /* parse options */
! clear_job_options(&opt);
! opt.jo_mode = MODE_JSON;
! opt.jo_timeout = 2000;
! if (get_job_options(&argvars[1], &opt,
! JO_MODE_ALL + JO_CB_ALL + JO_WAITTIME + JO_TIMEOUT_ALL) == FAIL)
! return;
! if (opt.jo_timeout < 0)
! {
! EMSG(_(e_invarg));
! return;
}
- channel = channel_open((char *)address, port, opt.jo_waittime, NULL);
if (channel != NULL)
{
! rettv->vval.v_channel = channel;
! opt.jo_set = JO_ALL;
! channel_set_options(channel, &opt);
}
}
/*
! * Common for ch_read() and ch_readraw().
*/
static void
! common_channel_read(typval_T *argvars, typval_T *rettv, int raw)
{
! channel_T *channel;
! int part;
! jobopt_T opt;
! int mode;
! int timeout;
! int id = -1;
! typval_T *listtv = NULL;
!
! /* return an empty string by default */
! rettv->v_type = VAR_STRING;
! rettv->vval.v_string = NULL;
! clear_job_options(&opt);
! if (get_job_options(&argvars[1], &opt, JO_TIMEOUT + JO_PART + JO_ID)
! == FAIL)
! return;
! channel = get_channel_arg(&argvars[0], TRUE);
! if (channel != NULL)
! {
! if (opt.jo_set & JO_PART)
! part = opt.jo_part;
! else
! part = channel_part_read(channel);
! mode = channel_get_mode(channel, part);
! timeout = channel_get_timeout(channel, part);
! if (opt.jo_set & JO_TIMEOUT)
! timeout = opt.jo_timeout;
!
! if (raw || mode == MODE_RAW || mode == MODE_NL)
! rettv->vval.v_string = channel_read_block(channel, part, timeout);
! else
! {
! if (opt.jo_set & JO_ID)
! id = opt.jo_id;
! channel_read_json_block(channel, part, timeout, id, &listtv);
! if (listtv != NULL)
! {
! *rettv = *listtv;
! vim_free(listtv);
! }
! else
! {
! rettv->v_type = VAR_SPECIAL;
! rettv->vval.v_number = VVAL_NONE;
! }
! }
! }
}
/*
! * "ch_read()" function
*/
static void
! f_ch_read(typval_T *argvars, typval_T *rettv)
{
! common_channel_read(argvars, rettv, FALSE);
}
/*
! * "ch_readraw()" function
*/
static void
! f_ch_readraw(typval_T *argvars, typval_T *rettv)
{
! common_channel_read(argvars, rettv, TRUE);
}
/*
! * common for "sendexpr()" and "sendraw()"
! * Returns the channel if the caller should read the response.
! * Sets "part_read" to the the read fd.
! * Otherwise returns NULL.
! */
! static channel_T *
! send_common(
! typval_T *argvars,
! char_u *text,
! int id,
! int eval,
! jobopt_T *opt,
! char *fun,
! int *part_read)
{
! channel_T *channel;
! int part_send;
!
! channel = get_channel_arg(&argvars[0], TRUE);
! if (channel == NULL)
! return NULL;
! part_send = channel_part_send(channel);
! *part_read = channel_part_read(channel);
!
! clear_job_options(opt);
! if (get_job_options(&argvars[2], opt, JO_CALLBACK + JO_TIMEOUT) == FAIL)
! return NULL;
!
! /* Set the callback. An empty callback means no callback and not reading
! * the response. With "ch_evalexpr()" and "ch_evalraw()" a callback is not
! * allowed. */
! if (opt->jo_callback != NULL && *opt->jo_callback != NUL)
! {
! if (eval)
! {
! EMSG2(_("E917: Cannot use a callback with %s()"), fun);
! return NULL;
! }
! channel_set_req_callback(channel, part_send, opt->jo_callback, id);
! }
!
! if (channel_send(channel, part_send, text, fun) == OK
! && opt->jo_callback == NULL)
! return channel;
! return NULL;
}
/*
! * common for "ch_evalexpr()" and "ch_sendexpr()"
*/
static void
! ch_expr_common(typval_T *argvars, typval_T *rettv, int eval)
{
! char_u *text;
! typval_T *listtv;
! channel_T *channel;
! int id;
! ch_mode_T ch_mode;
! int part_send;
! int part_read;
! jobopt_T opt;
! int timeout;
!
! /* return an empty string by default */
! rettv->v_type = VAR_STRING;
! rettv->vval.v_string = NULL;
!
! channel = get_channel_arg(&argvars[0], TRUE);
! if (channel == NULL)
! return;
! part_send = channel_part_send(channel);
!
! ch_mode = channel_get_mode(channel, part_send);
! if (ch_mode == MODE_RAW || ch_mode == MODE_NL)
! {
! EMSG(_("E912: cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl
channel"));
! return;
! }
!
! id = channel_get_id();
! text = json_encode_nr_expr(id, &argvars[1],
! ch_mode == MODE_JS ? JSON_JS : 0);
! if (text == NULL)
! return;
!
! channel = send_common(argvars, text, id, eval, &opt,
! eval ? "ch_evalexpr" : "ch_sendexpr", &part_read);
! vim_free(text);
! if (channel != NULL && eval)
! {
! if (opt.jo_set & JO_TIMEOUT)
! timeout = opt.jo_timeout;
! else
! timeout = channel_get_timeout(channel, part_read);
! timeout = channel_get_timeout(channel, part_read);
! if (channel_read_json_block(channel, part_read, timeout, id, &listtv)
! == OK)
! {
! list_T *list = listtv->vval.v_list;
!
! /* Move the item from the list and then change the type to
! * avoid the value being freed. */
! *rettv = list->lv_last->li_tv;
! list->lv_last->li_tv.v_type = VAR_NUMBER;
! free_tv(listtv);
! }
! }
}
/*
--- 9851,9937 ----
part = PART_OUT;
else if (STRCMP(what, "in") == 0)
part = PART_IN;
! else
! part = PART_SOCK;
! if (channel->ch_part[part].ch_buffer != NULL)
! rettv->vval.v_number = channel->ch_part[part].ch_buffer->b_fnum;
}
+ }
+
+ /*
+ * "ch_getjob()" function
+ */
+ static void
+ f_ch_getjob(typval_T *argvars, typval_T *rettv)
+ {
+ channel_T *channel = get_channel_arg(&argvars[0], TRUE);
if (channel != NULL)
{
! rettv->v_type = VAR_JOB;
! rettv->vval.v_job = channel->ch_job;
! if (channel->ch_job != NULL)
! ++channel->ch_job->jv_refcount;
}
}
/*
! * "ch_log()" function
*/
static void
! f_ch_log(typval_T *argvars, typval_T *rettv UNUSED)
{
! char_u *msg = get_tv_string(&argvars[0]);
! channel_T *channel = NULL;
! if (argvars[1].v_type != VAR_UNKNOWN)
! channel = get_channel_arg(&argvars[1], TRUE);
! ch_log(channel, (char *)msg);
}
/*
! * "ch_logfile()" function
*/
static void
! f_ch_logfile(typval_T *argvars, typval_T *rettv UNUSED)
{
! char_u *fname;
! char_u *opt = (char_u *)"";
! char_u buf[NUMBUFLEN];
!
! fname = get_tv_string(&argvars[0]);
! if (argvars[1].v_type == VAR_STRING)
! opt = get_tv_string_buf(&argvars[1], buf);
! ch_logfile(fname, opt);
}
/*
! * "ch_open()" function
*/
static void
! f_ch_open(typval_T *argvars, typval_T *rettv)
{
! rettv->v_type = VAR_CHANNEL;
! rettv->vval.v_channel = channel_open_func(argvars);
}
/*
! * "ch_read()" function
! */
! static void
! f_ch_read(typval_T *argvars, typval_T *rettv)
{
! common_channel_read(argvars, rettv, FALSE);
}
/*
! * "ch_readraw()" function
*/
static void
! f_ch_readraw(typval_T *argvars, typval_T *rettv)
{
! common_channel_read(argvars, rettv, TRUE);
}
/*
***************
*** 10697,10732 ****
}
/*
- * common for "ch_evalraw()" and "ch_sendraw()"
- */
- static void
- ch_raw_common(typval_T *argvars, typval_T *rettv, int eval)
- {
- char_u buf[NUMBUFLEN];
- char_u *text;
- channel_T *channel;
- int part_read;
- jobopt_T opt;
- int timeout;
-
- /* return an empty string by default */
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
-
- text = get_tv_string_buf(&argvars[1], buf);
- channel = send_common(argvars, text, 0, eval, &opt,
- eval ? "ch_evalraw" : "ch_sendraw", &part_read);
- if (channel != NULL && eval)
- {
- if (opt.jo_set & JO_TIMEOUT)
- timeout = opt.jo_timeout;
- else
- timeout = channel_get_timeout(channel, part_read);
- rettv->vval.v_string = channel_read_block(channel, part_read, timeout);
- }
- }
-
- /*
* "ch_evalraw()" function
*/
static void
--- 9953,9958 ----
***************
*** 15138,15387 ****
static void
f_job_start(typval_T *argvars, 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
- jobopt_T opt;
- int part;
-
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;
!
! /* Default mode is NL. */
! 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
! + JO_STOPONEXIT + JO_EXIT_CB + JO_OUT_IO) == FAIL)
! return;
!
! /* Check that when io is "file" that there is a file name. */
! for (part = PART_OUT; part <= PART_IN; ++part)
! if ((opt.jo_set & (JO_OUT_IO << (part - PART_OUT)))
! && opt.jo_io[part] == JIO_FILE
! && (!(opt.jo_set & (JO_OUT_NAME << (part - PART_OUT)))
! || *opt.jo_io_name[part] == NUL))
! {
! EMSG(_("E920: -io file requires -name to be set"));
! return;
! }
!
! if ((opt.jo_set & JO_IN_IO) && opt.jo_io[PART_IN] == JIO_BUFFER)
! {
! buf_T *buf = NULL;
!
! /* check that we can find the buffer before starting the job */
! if (opt.jo_set & JO_IN_BUF)
! {
! buf = buflist_findnr(opt.jo_io_buf[PART_IN]);
! if (buf == NULL)
! EMSGN(_(e_nobufnr), (long)opt.jo_io_buf[PART_IN]);
! }
! else if (!(opt.jo_set & JO_IN_NAME))
! {
! EMSG(_("E915: in-io buffer requires in-buf or in-name to be set"));
! }
! else
! buf = buflist_find_by_name(opt.jo_io_name[PART_IN], FALSE);
! if (buf == NULL)
! return;
! if (buf->b_ml.ml_mfp == NULL)
! {
! char_u numbuf[NUMBUFLEN];
! char_u *s;
!
! if (opt.jo_set & JO_IN_BUF)
! {
! sprintf((char *)numbuf, "%d", opt.jo_io_buf[PART_IN]);
! s = numbuf;
! }
! else
! s = opt.jo_io_name[PART_IN];
! EMSG2(_("E918: buffer must be loaded: %s"), s);
! return;
! }
! job->jv_in_buf = buf;
! }
!
! job_set_options(job, &opt);
!
! #ifndef USE_ARGV
! ga_init2(&ga, (int)sizeof(char*), 20);
! #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
! /* Only escape when needed, double quotes are not always allowed. */
! if (li != l->lv_first && vim_strpbrk(s, (char_u *)" \t\"") != NULL)
! {
! s = vim_strsave_shellescape(s, FALSE, TRUE);
! if (s == NULL)
! goto theend;
! ga_concat(&ga, s);
! vim_free(s);
! }
! else
! ga_concat(&ga, 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
! if (ch_log_active())
! {
! garray_T ga;
! int i;
!
! ga_init2(&ga, (int)sizeof(char), 200);
! for (i = 0; i < argc; ++i)
! {
! if (i > 0)
! ga_concat(&ga, (char_u *)" ");
! ga_concat(&ga, (char_u *)argv[i]);
! }
! ch_logs(NULL, "Starting job: %s", (char *)ga.ga_data);
! ga_clear(&ga);
! }
! mch_start_job(argv, job, &opt);
! #else
! ch_logs(NULL, "Starting job: %s", (char *)cmd);
! mch_start_job((char *)cmd, job, &opt);
! #endif
!
! /* If the channel is reading from a buffer, write lines now. */
! if (job->jv_channel != NULL)
! channel_write_in(job->jv_channel);
!
! theend:
! #ifdef USE_ARGV
! vim_free(argv);
! #else
! vim_free(ga.ga_data);
! #endif
! }
!
! /*
! * Get the status of "job" and invoke the exit callback when needed.
! * The returned string is not allocated.
! */
! static char *
! job_status(job_T *job)
! {
! char *result;
!
! 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);
! if (job->jv_status == JOB_ENDED)
! ch_log(job->jv_channel, "Job ended");
! if (job->jv_status == JOB_ENDED && job->jv_exit_cb != NULL)
! {
! typval_T argv[3];
! typval_T rettv;
! int dummy;
!
! /* invoke the exit callback; make sure the refcount is > 0 */
! ++job->jv_refcount;
! argv[0].v_type = VAR_JOB;
! argv[0].vval.v_job = job;
! argv[1].v_type = VAR_NUMBER;
! argv[1].vval.v_number = job->jv_exitval;
! call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb),
! &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL);
! clear_tv(&rettv);
! --job->jv_refcount;
! }
! if (job->jv_status == JOB_ENDED && job->jv_refcount == 0)
! {
! /* The job was already unreferenced, now that it ended it can be
! * freed. Careful: caller must not use "job" after this! */
! job_free(job);
! }
! }
! return result;
! }
!
! /*
! * Called once in a while: check if any jobs with an "exit-cb" have ended.
! */
! void
! job_check_ended(void)
! {
! static time_t last_check = 0;
! time_t now;
! job_T *job;
! job_T *next;
!
! /* Only do this once in 10 seconds. */
! now = time(NULL);
! if (last_check + 10 < now)
! {
! last_check = now;
! for (job = first_job; job != NULL; job = next)
! {
! next = job->jv_next;
! if (job->jv_status == JOB_STARTED && job->jv_exit_cb != NULL)
! job_status(job); /* may free "job" */
! }
! }
}
/*
--- 14364,14371 ----
static void
f_job_start(typval_T *argvars, typval_T *rettv)
{
rettv->v_type = VAR_JOB;
! rettv->vval.v_job = job_start(argvars);
}
/*
***************
*** 15405,15442 ****
* "job_stop()" function
*/
static void
! f_job_stop(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
job_T *job = get_job_arg(&argvars[0]);
if (job != NULL)
! {
! 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;
! }
! }
! ch_logs(job->jv_channel, "Stopping job with '%s'", (char *)arg);
! if (mch_stop_job(job, arg) == FAIL)
! rettv->vval.v_number = 0;
! else
! {
! rettv->vval.v_number = 1;
! /* Assume that "hup" does not kill the job. */
! if (job->jv_channel != NULL && STRCMP(arg, "hup") != 0)
! job->jv_channel->ch_job_killed = TRUE;
! }
! /* We don't try freeing the job, obviously the caller still has a
! * reference to it. */
! }
}
#endif
--- 14389,14400 ----
* "job_stop()" function
*/
static void
! f_job_stop(typval_T *argvars, typval_T *rettv)
{
job_T *job = get_job_arg(&argvars[0]);
if (job != NULL)
! rettv->vval.v_number = job_stop(job, argvars);
}
#endif
***************
*** 22475,22481 ****
* caller of incompatible types: it sets *denote to TRUE if "denote"
* is not NULL or returns -1 otherwise.
*/
! static long
get_tv_number(typval_T *varp)
{
int error = FALSE;
--- 21433,21439 ----
* caller of incompatible types: it sets *denote to TRUE if "denote"
* is not NULL or returns -1 otherwise.
*/
! long
get_tv_number(typval_T *varp)
{
int error = FALSE;
***************
*** 22626,22632 ****
* get_tv_string_chk() and get_tv_string_buf_chk() are similar, but return
* NULL on error.
*/
! static char_u *
get_tv_string(typval_T *varp)
{
static char_u mybuf[NUMBUFLEN];
--- 21584,21590 ----
* get_tv_string_chk() and get_tv_string_buf_chk() are similar, but return
* NULL on error.
*/
! char_u *
get_tv_string(typval_T *varp)
{
static char_u mybuf[NUMBUFLEN];
***************
*** 22634,22640 ****
return get_tv_string_buf(varp, mybuf);
}
! static char_u *
get_tv_string_buf(typval_T *varp, char_u *buf)
{
char_u *res = get_tv_string_buf_chk(varp, buf);
--- 21592,21598 ----
return get_tv_string_buf(varp, mybuf);
}
! char_u *
get_tv_string_buf(typval_T *varp, char_u *buf)
{
char_u *res = get_tv_string_buf_chk(varp, buf);
*** ../vim-7.4.1538/src/channel.c 2016-03-11 22:52:00.726438199 +0100
--- src/channel.c 2016-03-12 13:33:47.438483356 +0100
***************
*** 97,107 ****
#endif
void
! ch_logfile(FILE *file)
{
if (log_fd != NULL)
fclose(log_fd);
log_fd = file;
if (log_fd != NULL)
{
fprintf(log_fd, "==== start log session ====\n");
--- 97,120 ----
#endif
void
! ch_logfile(char_u *fname, char_u *opt)
{
+ FILE *file = NULL;
+
if (log_fd != NULL)
fclose(log_fd);
+
+ if (*fname != NUL)
+ {
+ file = fopen((char *)fname, *opt == 'w' ? "w" : "a");
+ if (file == NULL)
+ {
+ EMSG2(_(e_notopen), fname);
+ return;
+ }
+ }
log_fd = file;
+
if (log_fd != NULL)
{
fprintf(log_fd, "==== start log session ====\n");
***************
*** 360,366 ****
* killed.
* Return TRUE if the channel was freed.
*/
! int
channel_may_free(channel_T *channel)
{
if (!channel_still_useful(channel))
--- 373,379 ----
* killed.
* Return TRUE if the channel was freed.
*/
! static int
channel_may_free(channel_T *channel)
{
if (!channel_still_useful(channel))
***************
*** 372,377 ****
--- 385,403 ----
}
/*
+ * Decrement the reference count on "channel" and maybe free it when it goes
+ * down to zero. Don't free it if there is a pending action.
+ * Returns TRUE when the channel is no longer referenced.
+ */
+ int
+ channel_unref(channel_T *channel)
+ {
+ if (channel != NULL && --channel->ch_refcount <= 0)
+ return channel_may_free(channel);
+ return FALSE;
+ }
+
+ /*
* Close a channel and free all its resources.
*/
void
***************
*** 820,825 ****
--- 846,910 ----
return channel;
}
+ /*
+ * Implements ch_open().
+ */
+ channel_T *
+ channel_open_func(typval_T *argvars)
+ {
+ char_u *address;
+ char_u *p;
+ char *rest;
+ int port;
+ jobopt_T opt;
+ channel_T *channel;
+
+ address = get_tv_string(&argvars[0]);
+ if (argvars[1].v_type != VAR_UNKNOWN
+ && (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL))
+ {
+ EMSG(_(e_invarg));
+ return NULL;
+ }
+
+ /* parse address */
+ p = vim_strchr(address, ':');
+ if (p == NULL)
+ {
+ EMSG2(_(e_invarg2), address);
+ return NULL;
+ }
+ *p++ = NUL;
+ port = strtol((char *)p, &rest, 10);
+ if (*address == NUL || port <= 0 || *rest != NUL)
+ {
+ p[-1] = ':';
+ EMSG2(_(e_invarg2), address);
+ return NULL;
+ }
+
+ /* parse options */
+ clear_job_options(&opt);
+ opt.jo_mode = MODE_JSON;
+ opt.jo_timeout = 2000;
+ if (get_job_options(&argvars[1], &opt,
+ JO_MODE_ALL + JO_CB_ALL + JO_WAITTIME + JO_TIMEOUT_ALL) == FAIL)
+ return NULL;
+ if (opt.jo_timeout < 0)
+ {
+ EMSG(_(e_invarg));
+ return NULL;
+ }
+
+ channel = channel_open((char *)address, port, opt.jo_waittime, NULL);
+ if (channel != NULL)
+ {
+ opt.jo_set = JO_ALL;
+ channel_set_options(channel, &opt);
+ }
+ return channel;
+ }
+
static void
may_close_part(sock_T *fd)
{
***************
*** 2307,2312 ****
--- 2392,2453 ----
return FAIL;
}
+ /*
+ * Common for ch_read() and ch_readraw().
+ */
+ void
+ common_channel_read(typval_T *argvars, typval_T *rettv, int raw)
+ {
+ channel_T *channel;
+ int part;
+ jobopt_T opt;
+ int mode;
+ int timeout;
+ int id = -1;
+ typval_T *listtv = NULL;
+
+ /* return an empty string by default */
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+
+ clear_job_options(&opt);
+ if (get_job_options(&argvars[1], &opt, JO_TIMEOUT + JO_PART + JO_ID)
+ == FAIL)
+ return;
+
+ channel = get_channel_arg(&argvars[0], TRUE);
+ if (channel != NULL)
+ {
+ if (opt.jo_set & JO_PART)
+ part = opt.jo_part;
+ else
+ part = channel_part_read(channel);
+ mode = channel_get_mode(channel, part);
+ timeout = channel_get_timeout(channel, part);
+ if (opt.jo_set & JO_TIMEOUT)
+ timeout = opt.jo_timeout;
+
+ if (raw || mode == MODE_RAW || mode == MODE_NL)
+ rettv->vval.v_string = channel_read_block(channel, part, timeout);
+ else
+ {
+ if (opt.jo_set & JO_ID)
+ id = opt.jo_id;
+ channel_read_json_block(channel, part, timeout, id, &listtv);
+ if (listtv != NULL)
+ {
+ *rettv = *listtv;
+ vim_free(listtv);
+ }
+ else
+ {
+ rettv->v_type = VAR_SPECIAL;
+ rettv->vval.v_number = VVAL_NONE;
+ }
+ }
+ }
+ }
+
# if defined(WIN32) || defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK) \
|| defined(PROTO)
/*
***************
*** 2412,2417 ****
--- 2553,2698 ----
return OK;
}
+ /*
+ * Common for "ch_sendexpr()" and "ch_sendraw()".
+ * Returns the channel if the caller should read the response.
+ * Sets "part_read" to the the read fd.
+ * Otherwise returns NULL.
+ */
+ channel_T *
+ send_common(
+ typval_T *argvars,
+ char_u *text,
+ int id,
+ int eval,
+ jobopt_T *opt,
+ char *fun,
+ int *part_read)
+ {
+ channel_T *channel;
+ int part_send;
+
+ channel = get_channel_arg(&argvars[0], TRUE);
+ if (channel == NULL)
+ return NULL;
+ part_send = channel_part_send(channel);
+ *part_read = channel_part_read(channel);
+
+ clear_job_options(opt);
+ if (get_job_options(&argvars[2], opt, JO_CALLBACK + JO_TIMEOUT) == FAIL)
+ return NULL;
+
+ /* Set the callback. An empty callback means no callback and not reading
+ * the response. With "ch_evalexpr()" and "ch_evalraw()" a callback is not
+ * allowed. */
+ if (opt->jo_callback != NULL && *opt->jo_callback != NUL)
+ {
+ if (eval)
+ {
+ EMSG2(_("E917: Cannot use a callback with %s()"), fun);
+ return NULL;
+ }
+ channel_set_req_callback(channel, part_send, opt->jo_callback, id);
+ }
+
+ if (channel_send(channel, part_send, text, fun) == OK
+ && opt->jo_callback == NULL)
+ return channel;
+ return NULL;
+ }
+
+ /*
+ * common for "ch_evalexpr()" and "ch_sendexpr()"
+ */
+ void
+ ch_expr_common(typval_T *argvars, typval_T *rettv, int eval)
+ {
+ char_u *text;
+ typval_T *listtv;
+ channel_T *channel;
+ int id;
+ ch_mode_T ch_mode;
+ int part_send;
+ int part_read;
+ jobopt_T opt;
+ int timeout;
+
+ /* return an empty string by default */
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+
+ channel = get_channel_arg(&argvars[0], TRUE);
+ if (channel == NULL)
+ return;
+ part_send = channel_part_send(channel);
+
+ ch_mode = channel_get_mode(channel, part_send);
+ if (ch_mode == MODE_RAW || ch_mode == MODE_NL)
+ {
+ EMSG(_("E912: cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl
channel"));
+ return;
+ }
+
+ id = channel_get_id();
+ text = json_encode_nr_expr(id, &argvars[1],
+ ch_mode == MODE_JS ? JSON_JS : 0);
+ if (text == NULL)
+ return;
+
+ channel = send_common(argvars, text, id, eval, &opt,
+ eval ? "ch_evalexpr" : "ch_sendexpr", &part_read);
+ vim_free(text);
+ if (channel != NULL && eval)
+ {
+ if (opt.jo_set & JO_TIMEOUT)
+ timeout = opt.jo_timeout;
+ else
+ timeout = channel_get_timeout(channel, part_read);
+ timeout = channel_get_timeout(channel, part_read);
+ if (channel_read_json_block(channel, part_read, timeout, id, &listtv)
+ == OK)
+ {
+ list_T *list = listtv->vval.v_list;
+
+ /* Move the item from the list and then change the type to
+ * avoid the value being freed. */
+ *rettv = list->lv_last->li_tv;
+ list->lv_last->li_tv.v_type = VAR_NUMBER;
+ free_tv(listtv);
+ }
+ }
+ }
+
+ /*
+ * common for "ch_evalraw()" and "ch_sendraw()"
+ */
+ void
+ ch_raw_common(typval_T *argvars, typval_T *rettv, int eval)
+ {
+ char_u buf[NUMBUFLEN];
+ char_u *text;
+ channel_T *channel;
+ int part_read;
+ jobopt_T opt;
+ int timeout;
+
+ /* return an empty string by default */
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+
+ text = get_tv_string_buf(&argvars[1], buf);
+ channel = send_common(argvars, text, 0, eval, &opt,
+ eval ? "ch_evalraw" : "ch_sendraw", &part_read);
+ if (channel != NULL && eval)
+ {
+ if (opt.jo_set & JO_TIMEOUT)
+ timeout = opt.jo_timeout;
+ else
+ timeout = channel_get_timeout(channel, part_read);
+ rettv->vval.v_string = channel_read_block(channel, part_read, timeout);
+ }
+ }
+
# if (defined(UNIX) && !defined(HAVE_SELECT)) || defined(PROTO)
/*
* Add open channels to the poll struct.
***************
*** 2695,2698 ****
--- 2976,3757 ----
return channel->ch_part[part].ch_timeout;
}
+ /*
+ * Get a callback from "arg". It can be a Funcref or a function name.
+ * When "arg" is zero return an empty string.
+ * Return NULL for an invalid argument.
+ */
+ static char_u *
+ get_callback(typval_T *arg)
+ {
+ if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING)
+ return arg->vval.v_string;
+ if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0)
+ return (char_u *)"";
+ EMSG(_("E999: Invalid callback argument"));
+ return NULL;
+ }
+
+ static int
+ handle_mode(typval_T *item, jobopt_T *opt, ch_mode_T *modep, int jo)
+ {
+ char_u *val = get_tv_string(item);
+
+ opt->jo_set |= jo;
+ if (STRCMP(val, "nl") == 0)
+ *modep = MODE_NL;
+ else if (STRCMP(val, "raw") == 0)
+ *modep = MODE_RAW;
+ else if (STRCMP(val, "js") == 0)
+ *modep = MODE_JS;
+ else if (STRCMP(val, "json") == 0)
+ *modep = MODE_JSON;
+ else
+ {
+ EMSG2(_(e_invarg2), val);
+ return FAIL;
+ }
+ return OK;
+ }
+
+ static int
+ handle_io(typval_T *item, int part, jobopt_T *opt)
+ {
+ char_u *val = get_tv_string(item);
+
+ opt->jo_set |= JO_OUT_IO << (part - PART_OUT);
+ if (STRCMP(val, "null") == 0)
+ opt->jo_io[part] = JIO_NULL;
+ else if (STRCMP(val, "pipe") == 0)
+ opt->jo_io[part] = JIO_PIPE;
+ else if (STRCMP(val, "file") == 0)
+ opt->jo_io[part] = JIO_FILE;
+ else if (STRCMP(val, "buffer") == 0)
+ opt->jo_io[part] = JIO_BUFFER;
+ else if (STRCMP(val, "out") == 0 && part == PART_ERR)
+ opt->jo_io[part] = JIO_OUT;
+ else
+ {
+ EMSG2(_(e_invarg2), val);
+ return FAIL;
+ }
+ return OK;
+ }
+
+ void
+ clear_job_options(jobopt_T *opt)
+ {
+ vim_memset(opt, 0, sizeof(jobopt_T));
+ }
+
+ /*
+ * Get the PART_ number from the first character of an option name.
+ */
+ static int
+ part_from_char(int c)
+ {
+ return c == 'i' ? PART_IN : c == 'o' ? PART_OUT: PART_ERR;
+ }
+
+ /*
+ * Get the option entries from the dict in "tv", parse them and put the result
+ * in "opt".
+ * Only accept options in "supported".
+ * If an option value is invalid return FAIL.
+ */
+ int
+ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
+ {
+ typval_T *item;
+ char_u *val;
+ dict_T *dict;
+ int todo;
+ hashitem_T *hi;
+ int part;
+
+ opt->jo_set = 0;
+ if (tv->v_type == VAR_UNKNOWN)
+ return OK;
+ if (tv->v_type != VAR_DICT)
+ {
+ EMSG(_(e_invarg));
+ return FAIL;
+ }
+ dict = tv->vval.v_dict;
+ if (dict == NULL)
+ return OK;
+
+ todo = (int)dict->dv_hashtab.ht_used;
+ for (hi = dict->dv_hashtab.ht_array; todo > 0; ++hi)
+ if (!HASHITEM_EMPTY(hi))
+ {
+ item = &dict_lookup(hi)->di_tv;
+
+ if (STRCMP(hi->hi_key, "mode") == 0)
+ {
+ if (!(supported & JO_MODE))
+ break;
+ if (handle_mode(item, opt, &opt->jo_mode, JO_MODE) == FAIL)
+ return FAIL;
+ }
+ else if (STRCMP(hi->hi_key, "in-mode") == 0)
+ {
+ if (!(supported & JO_IN_MODE))
+ break;
+ if (handle_mode(item, opt, &opt->jo_in_mode, JO_IN_MODE)
+ == FAIL)
+ return FAIL;
+ }
+ else if (STRCMP(hi->hi_key, "out-mode") == 0)
+ {
+ if (!(supported & JO_OUT_MODE))
+ break;
+ if (handle_mode(item, opt, &opt->jo_out_mode, JO_OUT_MODE)
+ == FAIL)
+ return FAIL;
+ }
+ else if (STRCMP(hi->hi_key, "err-mode") == 0)
+ {
+ if (!(supported & JO_ERR_MODE))
+ break;
+ if (handle_mode(item, opt, &opt->jo_err_mode, JO_ERR_MODE)
+ == FAIL)
+ return FAIL;
+ }
+ else if (STRCMP(hi->hi_key, "in-io") == 0
+ || STRCMP(hi->hi_key, "out-io") == 0
+ || STRCMP(hi->hi_key, "err-io") == 0)
+ {
+ if (!(supported & JO_OUT_IO))
+ break;
+ if (handle_io(item, part_from_char(*hi->hi_key), opt) == FAIL)
+ return FAIL;
+ }
+ else if (STRCMP(hi->hi_key, "in-name") == 0
+ || STRCMP(hi->hi_key, "out-name") == 0
+ || STRCMP(hi->hi_key, "err-name") == 0)
+ {
+ part = part_from_char(*hi->hi_key);
+
+ if (!(supported & JO_OUT_IO))
+ break;
+ opt->jo_set |= JO_OUT_NAME << (part - PART_OUT);
+ opt->jo_io_name[part] =
+ get_tv_string_buf_chk(item, opt->jo_io_name_buf[part]);
+ }
+ else if (STRCMP(hi->hi_key, "in-buf") == 0
+ || STRCMP(hi->hi_key, "out-buf") == 0
+ || STRCMP(hi->hi_key, "err-buf") == 0)
+ {
+ part = part_from_char(*hi->hi_key);
+
+ if (!(supported & JO_OUT_IO))
+ break;
+ opt->jo_set |= JO_OUT_BUF << (part - PART_OUT);
+ opt->jo_io_buf[part] = get_tv_number(item);
+ if (opt->jo_io_buf[part] <= 0)
+ {
+ EMSG2(_(e_invarg2), get_tv_string(item));
+ return FAIL;
+ }
+ if (buflist_findnr(opt->jo_io_buf[part]) == NULL)
+ {
+ EMSGN(_(e_nobufnr), (long)opt->jo_io_buf[part]);
+ return FAIL;
+ }
+ }
+ else if (STRCMP(hi->hi_key, "in-top") == 0
+ || STRCMP(hi->hi_key, "in-bot") == 0)
+ {
+ linenr_T *lp;
+
+ if (!(supported & JO_OUT_IO))
+ break;
+ if (hi->hi_key[3] == 't')
+ {
+ lp = &opt->jo_in_top;
+ opt->jo_set |= JO_IN_TOP;
+ }
+ else
+ {
+ lp = &opt->jo_in_bot;
+ opt->jo_set |= JO_IN_BOT;
+ }
+ *lp = get_tv_number(item);
+ if (*lp < 0)
+ {
+ EMSG2(_(e_invarg2), get_tv_string(item));
+ return FAIL;
+ }
+ }
+ else if (STRCMP(hi->hi_key, "channel") == 0)
+ {
+ if (!(supported & JO_OUT_IO))
+ break;
+ opt->jo_set |= JO_CHANNEL;
+ if (item->v_type != VAR_CHANNEL)
+ {
+ EMSG2(_(e_invarg2), "channel");
+ return FAIL;
+ }
+ opt->jo_channel = item->vval.v_channel;
+ }
+ else if (STRCMP(hi->hi_key, "callback") == 0)
+ {
+ if (!(supported & JO_CALLBACK))
+ break;
+ opt->jo_set |= JO_CALLBACK;
+ opt->jo_callback = get_callback(item);
+ if (opt->jo_callback == NULL)
+ {
+ EMSG2(_(e_invarg2), "callback");
+ return FAIL;
+ }
+ }
+ else if (STRCMP(hi->hi_key, "out-cb") == 0)
+ {
+ if (!(supported & JO_OUT_CALLBACK))
+ break;
+ opt->jo_set |= JO_OUT_CALLBACK;
+ opt->jo_out_cb = get_callback(item);
+ if (opt->jo_out_cb == NULL)
+ {
+ EMSG2(_(e_invarg2), "out-cb");
+ return FAIL;
+ }
+ }
+ else if (STRCMP(hi->hi_key, "err-cb") == 0)
+ {
+ if (!(supported & JO_ERR_CALLBACK))
+ break;
+ opt->jo_set |= JO_ERR_CALLBACK;
+ opt->jo_err_cb = get_callback(item);
+ if (opt->jo_err_cb == NULL)
+ {
+ EMSG2(_(e_invarg2), "err-cb");
+ return FAIL;
+ }
+ }
+ else if (STRCMP(hi->hi_key, "close-cb") == 0)
+ {
+ if (!(supported & JO_CLOSE_CALLBACK))
+ break;
+ opt->jo_set |= JO_CLOSE_CALLBACK;
+ opt->jo_close_cb = get_callback(item);
+ if (opt->jo_close_cb == NULL)
+ {
+ EMSG2(_(e_invarg2), "close-cb");
+ return FAIL;
+ }
+ }
+ else if (STRCMP(hi->hi_key, "waittime") == 0)
+ {
+ if (!(supported & JO_WAITTIME))
+ break;
+ opt->jo_set |= JO_WAITTIME;
+ opt->jo_waittime = get_tv_number(item);
+ }
+ else if (STRCMP(hi->hi_key, "timeout") == 0)
+ {
+ if (!(supported & JO_TIMEOUT))
+ break;
+ opt->jo_set |= JO_TIMEOUT;
+ opt->jo_timeout = get_tv_number(item);
+ }
+ else if (STRCMP(hi->hi_key, "out-timeout") == 0)
+ {
+ if (!(supported & JO_OUT_TIMEOUT))
+ break;
+ opt->jo_set |= JO_OUT_TIMEOUT;
+ opt->jo_out_timeout = get_tv_number(item);
+ }
+ else if (STRCMP(hi->hi_key, "err-timeout") == 0)
+ {
+ if (!(supported & JO_ERR_TIMEOUT))
+ break;
+ opt->jo_set |= JO_ERR_TIMEOUT;
+ opt->jo_err_timeout = get_tv_number(item);
+ }
+ else if (STRCMP(hi->hi_key, "part") == 0)
+ {
+ if (!(supported & JO_PART))
+ break;
+ opt->jo_set |= JO_PART;
+ val = get_tv_string(item);
+ if (STRCMP(val, "err") == 0)
+ opt->jo_part = PART_ERR;
+ else
+ {
+ EMSG2(_(e_invarg2), val);
+ return FAIL;
+ }
+ }
+ else if (STRCMP(hi->hi_key, "id") == 0)
+ {
+ if (!(supported & JO_ID))
+ break;
+ 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 if (STRCMP(hi->hi_key, "exit-cb") == 0)
+ {
+ if (!(supported & JO_EXIT_CB))
+ break;
+ opt->jo_set |= JO_EXIT_CB;
+ opt->jo_exit_cb = get_tv_string_buf_chk(item, opt->jo_ecb_buf);
+ if (opt->jo_exit_cb == NULL)
+ {
+ EMSG2(_(e_invarg2), "exit-cb");
+ return FAIL;
+ }
+ }
+ else
+ break;
+ --todo;
+ }
+ if (todo > 0)
+ {
+ EMSG2(_(e_invarg2), hi->hi_key);
+ return FAIL;
+ }
+
+ return OK;
+ }
+
+ /*
+ * Get the channel from the argument.
+ * Returns NULL if the handle is invalid.
+ */
+ channel_T *
+ get_channel_arg(typval_T *tv, int check_open)
+ {
+ channel_T *channel = NULL;
+
+ if (tv->v_type == VAR_JOB)
+ {
+ if (tv->vval.v_job != NULL)
+ channel = tv->vval.v_job->jv_channel;
+ }
+ else if (tv->v_type == VAR_CHANNEL)
+ {
+ channel = tv->vval.v_channel;
+ }
+ else
+ {
+ EMSG2(_(e_invarg2), get_tv_string(tv));
+ return NULL;
+ }
+
+ if (check_open && (channel == NULL || !channel_is_open(channel)))
+ {
+ EMSG(_("E906: not an open channel"));
+ return NULL;
+ }
+ return channel;
+ }
+
+ static job_T *first_job = NULL;
+
+ static void
+ job_free(job_T *job)
+ {
+ ch_log(job->jv_channel, "Freeing job");
+ if (job->jv_channel != NULL)
+ {
+ /* The link from the channel to the job doesn't count as a reference,
+ * thus don't decrement the refcount of the job. The reference from
+ * the job to the channel does count the refrence, decrement it and
+ * NULL the reference. We don't set ch_job_killed, unreferencing the
+ * job doesn't mean it stops running. */
+ job->jv_channel->ch_job = NULL;
+ channel_unref(job->jv_channel);
+ }
+ 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->jv_exit_cb);
+ vim_free(job);
+ }
+
+ void
+ job_unref(job_T *job)
+ {
+ if (job != NULL && --job->jv_refcount <= 0)
+ {
+ /* Do not free the job when it has not ended yet and there is a
+ * "stoponexit" flag or an exit callback. */
+ if (job->jv_status != JOB_STARTED
+ || (job->jv_stoponexit == NULL && job->jv_exit_cb == NULL))
+ {
+ job_free(job);
+ }
+ else if (job->jv_channel != NULL)
+ {
+ /* Do remove the link to the channel, otherwise it hangs
+ * around until Vim exits. See job_free() for refcount. */
+ job->jv_channel->ch_job = NULL;
+ channel_unref(job->jv_channel);
+ job->jv_channel = NULL;
+ }
+ }
+ }
+
+ /*
+ * Allocate a job. Sets the refcount to one and sets options default.
+ */
+ static job_T *
+ job_alloc(void)
+ {
+ job_T *job;
+
+ 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;
+ }
+
+ 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);
+ }
+ if (opt->jo_set & JO_EXIT_CB)
+ {
+ vim_free(job->jv_exit_cb);
+ if (opt->jo_exit_cb == NULL || *opt->jo_exit_cb == NUL)
+ job->jv_exit_cb = NULL;
+ else
+ job->jv_exit_cb = vim_strsave(opt->jo_exit_cb);
+ }
+ }
+
+ /*
+ * 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_status == JOB_STARTED && job->jv_stoponexit != NULL)
+ mch_stop_job(job, job->jv_stoponexit);
+ }
+
+ /*
+ * Called once in a while: check if any jobs with an "exit-cb" have ended.
+ */
+ void
+ job_check_ended(void)
+ {
+ static time_t last_check = 0;
+ time_t now;
+ job_T *job;
+ job_T *next;
+
+ /* Only do this once in 10 seconds. */
+ now = time(NULL);
+ if (last_check + 10 < now)
+ {
+ last_check = now;
+ for (job = first_job; job != NULL; job = next)
+ {
+ next = job->jv_next;
+ if (job->jv_status == JOB_STARTED && job->jv_exit_cb != NULL)
+ job_status(job); /* may free "job" */
+ }
+ }
+ }
+
+ /*
+ * "job_start()" function
+ */
+ job_T *
+ job_start(typval_T *argvars)
+ {
+ job_T *job;
+ char_u *cmd = NULL;
+ #if defined(UNIX)
+ # define USE_ARGV
+ char **argv = NULL;
+ int argc = 0;
+ #else
+ garray_T ga;
+ #endif
+ jobopt_T opt;
+ int part;
+
+ job = job_alloc();
+ if (job == NULL)
+ return NULL;
+
+ job->jv_status = JOB_FAILED;
+
+ /* Default mode is NL. */
+ 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
+ + JO_STOPONEXIT + JO_EXIT_CB + JO_OUT_IO) == FAIL)
+ return job;
+
+ /* Check that when io is "file" that there is a file name. */
+ for (part = PART_OUT; part <= PART_IN; ++part)
+ if ((opt.jo_set & (JO_OUT_IO << (part - PART_OUT)))
+ && opt.jo_io[part] == JIO_FILE
+ && (!(opt.jo_set & (JO_OUT_NAME << (part - PART_OUT)))
+ || *opt.jo_io_name[part] == NUL))
+ {
+ EMSG(_("E920: -io file requires -name to be set"));
+ return job;
+ }
+
+ if ((opt.jo_set & JO_IN_IO) && opt.jo_io[PART_IN] == JIO_BUFFER)
+ {
+ buf_T *buf = NULL;
+
+ /* check that we can find the buffer before starting the job */
+ if (opt.jo_set & JO_IN_BUF)
+ {
+ buf = buflist_findnr(opt.jo_io_buf[PART_IN]);
+ if (buf == NULL)
+ EMSGN(_(e_nobufnr), (long)opt.jo_io_buf[PART_IN]);
+ }
+ else if (!(opt.jo_set & JO_IN_NAME))
+ {
+ EMSG(_("E915: in-io buffer requires in-buf or in-name to be set"));
+ }
+ else
+ buf = buflist_find_by_name(opt.jo_io_name[PART_IN], FALSE);
+ if (buf == NULL)
+ return job;
+ if (buf->b_ml.ml_mfp == NULL)
+ {
+ char_u numbuf[NUMBUFLEN];
+ char_u *s;
+
+ if (opt.jo_set & JO_IN_BUF)
+ {
+ sprintf((char *)numbuf, "%d", opt.jo_io_buf[PART_IN]);
+ s = numbuf;
+ }
+ else
+ s = opt.jo_io_name[PART_IN];
+ EMSG2(_("E918: buffer must be loaded: %s"), s);
+ return job;
+ }
+ job->jv_in_buf = buf;
+ }
+
+ job_set_options(job, &opt);
+
+ #ifndef USE_ARGV
+ ga_init2(&ga, (int)sizeof(char*), 20);
+ #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 job;
+ 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 job;
+ }
+ 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 job;
+ #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
+ /* Only escape when needed, double quotes are not always allowed. */
+ if (li != l->lv_first && vim_strpbrk(s, (char_u *)" \t\"") != NULL)
+ {
+ s = vim_strsave_shellescape(s, FALSE, TRUE);
+ if (s == NULL)
+ goto theend;
+ ga_concat(&ga, s);
+ vim_free(s);
+ }
+ else
+ ga_concat(&ga, 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
+ if (ch_log_active())
+ {
+ garray_T ga;
+ int i;
+
+ ga_init2(&ga, (int)sizeof(char), 200);
+ for (i = 0; i < argc; ++i)
+ {
+ if (i > 0)
+ ga_concat(&ga, (char_u *)" ");
+ ga_concat(&ga, (char_u *)argv[i]);
+ }
+ ch_logs(NULL, "Starting job: %s", (char *)ga.ga_data);
+ ga_clear(&ga);
+ }
+ mch_start_job(argv, job, &opt);
+ #else
+ ch_logs(NULL, "Starting job: %s", (char *)cmd);
+ mch_start_job((char *)cmd, job, &opt);
+ #endif
+
+ /* If the channel is reading from a buffer, write lines now. */
+ if (job->jv_channel != NULL)
+ channel_write_in(job->jv_channel);
+
+ theend:
+ #ifdef USE_ARGV
+ vim_free(argv);
+ #else
+ vim_free(ga.ga_data);
+ #endif
+ return job;
+ }
+
+ /*
+ * Get the status of "job" and invoke the exit callback when needed.
+ * The returned string is not allocated.
+ */
+ char *
+ job_status(job_T *job)
+ {
+ char *result;
+
+ 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);
+ if (job->jv_status == JOB_ENDED)
+ ch_log(job->jv_channel, "Job ended");
+ if (job->jv_status == JOB_ENDED && job->jv_exit_cb != NULL)
+ {
+ typval_T argv[3];
+ typval_T rettv;
+ int dummy;
+
+ /* invoke the exit callback; make sure the refcount is > 0 */
+ ++job->jv_refcount;
+ argv[0].v_type = VAR_JOB;
+ argv[0].vval.v_job = job;
+ argv[1].v_type = VAR_NUMBER;
+ argv[1].vval.v_number = job->jv_exitval;
+ call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb),
+ &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL);
+ clear_tv(&rettv);
+ --job->jv_refcount;
+ }
+ if (job->jv_status == JOB_ENDED && job->jv_refcount == 0)
+ {
+ /* The job was already unreferenced, now that it ended it can be
+ * freed. Careful: caller must not use "job" after this! */
+ job_free(job);
+ }
+ }
+ return result;
+ }
+
+ int
+ job_stop(job_T *job, typval_T *argvars)
+ {
+ 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 0;
+ }
+ }
+ ch_logs(job->jv_channel, "Stopping job with '%s'", (char *)arg);
+ if (mch_stop_job(job, arg) == FAIL)
+ return 0;
+
+ /* Assume that "hup" does not kill the job. */
+ if (job->jv_channel != NULL && STRCMP(arg, "hup") != 0)
+ job->jv_channel->ch_job_killed = TRUE;
+
+ /* We don't try freeing the job, obviously the caller still has a
+ * reference to it. */
+ return 1;
+ }
+
#endif /* FEAT_JOB_CHANNEL */
*** ../vim-7.4.1538/src/proto/channel.pro 2016-03-11 22:19:39.934894037
+0100
--- src/proto/channel.pro 2016-03-12 13:33:25.446710489 +0100
***************
*** 1,13 ****
/* channel.c */
! void ch_logfile(FILE *file);
int ch_log_active(void);
void ch_log(channel_T *ch, char *msg);
void ch_logs(channel_T *ch, char *msg, char *name);
channel_T *add_channel(void);
! int channel_may_free(channel_T *channel);
void channel_free(channel_T *channel);
void channel_gui_register_all(void);
channel_T *channel_open(char *hostname, int port_in, int waittime, void
(*nb_close_cb)(void));
void channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err);
void channel_set_job(channel_T *channel, job_T *job, jobopt_T *options);
void channel_set_options(channel_T *channel, jobopt_T *opt);
--- 1,14 ----
/* channel.c */
! void ch_logfile(char_u *fname, char_u *opt);
int ch_log_active(void);
void ch_log(channel_T *ch, char *msg);
void ch_logs(channel_T *ch, char *msg, char *name);
channel_T *add_channel(void);
! int channel_unref(channel_T *channel);
void channel_free(channel_T *channel);
void channel_gui_register_all(void);
channel_T *channel_open(char *hostname, int port_in, int waittime, void
(*nb_close_cb)(void));
+ channel_T *channel_open_func(typval_T *argvars);
void channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err);
void channel_set_job(channel_T *channel, job_T *job, jobopt_T *options);
void channel_set_options(channel_T *channel, jobopt_T *opt);
***************
*** 27,35 ****
--- 28,40 ----
void channel_read(channel_T *channel, int part, char *func);
char_u *channel_read_block(channel_T *channel, int part, int timeout);
int channel_read_json_block(channel_T *channel, int part, int timeout, int
id, typval_T **rettv);
+ void common_channel_read(typval_T *argvars, typval_T *rettv, int raw);
channel_T *channel_fd2channel(sock_T fd, int *partp);
void channel_handle_events(void);
int channel_send(channel_T *channel, int part, char_u *buf, char *fun);
+ channel_T *send_common(typval_T *argvars, char_u *text, int id, int eval,
jobopt_T *opt, char *fun, int *part_read);
+ void ch_expr_common(typval_T *argvars, typval_T *rettv, int eval);
+ void ch_raw_common(typval_T *argvars, typval_T *rettv, int eval);
int channel_poll_setup(int nfd_in, void *fds_in);
int channel_poll_check(int ret_in, void *fds_in);
int channel_select_setup(int maxfd_in, void *rfds_in);
***************
*** 40,43 ****
--- 45,58 ----
int channel_part_read(channel_T *channel);
ch_mode_T channel_get_mode(channel_T *channel, int part);
int channel_get_timeout(channel_T *channel, int part);
+ void clear_job_options(jobopt_T *opt);
+ int get_job_options(typval_T *tv, jobopt_T *opt, int supported);
+ channel_T *get_channel_arg(typval_T *tv, int check_open);
+ void job_unref(job_T *job);
+ void job_set_options(job_T *job, jobopt_T *opt);
+ void job_stop_on_exit(void);
+ void job_check_ended(void);
+ job_T *job_start(typval_T *argvars);
+ char *job_status(job_T *job);
+ int job_stop(job_T *job, typval_T *argvars);
/* vim: set ft=c : */
*** ../vim-7.4.1538/src/proto/eval.pro 2016-02-21 19:14:36.679958696 +0100
--- src/proto/eval.pro 2016-03-12 13:32:31.475267912 +0100
***************
*** 79,93 ****
dictitem_T *dict_find(dict_T *d, char_u *key, int len);
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);
int call_func(char_u *funcname, int len, typval_T *rettv, int argcount,
typval_T *argvars, linenr_T firstline, linenr_T lastline, int *doesrange, int
evaluate, dict_T *selfdict);
int func_call(char_u *name, typval_T *args, dict_T *selfdict, typval_T
*rettv);
void dict_extend(dict_T *d1, dict_T *d2, char_u *action);
- void job_check_ended(void);
void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv);
float_T vim_round(float_T f);
long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, char_u
*skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit);
--- 79,91 ----
dictitem_T *dict_find(dict_T *d, char_u *key, int len);
char_u *get_dict_string(dict_T *d, char_u *key, int save);
long get_dict_number(dict_T *d, char_u *key);
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);
int call_func(char_u *funcname, int len, typval_T *rettv, int argcount,
typval_T *argvars, linenr_T firstline, linenr_T lastline, int *doesrange, int
evaluate, dict_T *selfdict);
+ buf_T *buflist_find_by_name(char_u *name, int curtab_only);
int func_call(char_u *name, typval_T *args, dict_T *selfdict, typval_T
*rettv);
void dict_extend(dict_T *d1, dict_T *d2, char_u *action);
void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv);
float_T vim_round(float_T f);
long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, char_u
*skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit);
***************
*** 107,113 ****
--- 105,114 ----
typval_T *alloc_tv(void);
void free_tv(typval_T *varp);
void clear_tv(typval_T *varp);
+ long get_tv_number(typval_T *varp);
long get_tv_number_chk(typval_T *varp, int *denote);
+ char_u *get_tv_string(typval_T *varp);
+ char_u *get_tv_string_buf(typval_T *varp, char_u *buf);
char_u *get_tv_string_chk(typval_T *varp);
char_u *get_tv_string_buf_chk(typval_T *varp, char_u *buf);
char_u *get_var_value(char_u *name);
*** ../vim-7.4.1538/src/version.c 2016-03-12 12:40:54.219242159 +0100
--- src/version.c 2016-03-12 13:00:08.419328525 +0100
***************
*** 745,746 ****
--- 745,748 ----
{ /* Add new patch number below this line */
+ /**/
+ 1539,
/**/
--
How To Keep A Healthy Level Of Insanity:
18. When leaving the zoo, start running towards the parking lot,
yelling "run for your lives, they're loose!!"
/// 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.