To: vim_dev@googlegroups.com Subject: Patch 7.4.1539 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///