Mercurial > vim
comparison src/channel.c @ 8498:42277980a76d v7.4.1539
commit https://github.com/vim/vim/commit/8e2c942ce49f2555d7dc2088cf3aa856820c5e32
Author: Bram Moolenaar <Bram@vim.org>
Date: Sat Mar 12 13:43:33 2016 +0100
patch 7.4.1539
Problem: Too much code in eval.c.
Solution: Move job and channel code to channel.c.
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Sat, 12 Mar 2016 13:45:04 +0100 |
parents | caed4b2d305f |
children | ee5cb2e9ed5a |
comparison
equal
deleted
inserted
replaced
8497:da01d5da2cfa | 8498:42277980a76d |
---|---|
95 #ifdef FEAT_RELTIME | 95 #ifdef FEAT_RELTIME |
96 static proftime_T log_start; | 96 static proftime_T log_start; |
97 #endif | 97 #endif |
98 | 98 |
99 void | 99 void |
100 ch_logfile(FILE *file) | 100 ch_logfile(char_u *fname, char_u *opt) |
101 { | 101 { |
102 FILE *file = NULL; | |
103 | |
102 if (log_fd != NULL) | 104 if (log_fd != NULL) |
103 fclose(log_fd); | 105 fclose(log_fd); |
106 | |
107 if (*fname != NUL) | |
108 { | |
109 file = fopen((char *)fname, *opt == 'w' ? "w" : "a"); | |
110 if (file == NULL) | |
111 { | |
112 EMSG2(_(e_notopen), fname); | |
113 return; | |
114 } | |
115 } | |
104 log_fd = file; | 116 log_fd = file; |
117 | |
105 if (log_fd != NULL) | 118 if (log_fd != NULL) |
106 { | 119 { |
107 fprintf(log_fd, "==== start log session ====\n"); | 120 fprintf(log_fd, "==== start log session ====\n"); |
108 #ifdef FEAT_RELTIME | 121 #ifdef FEAT_RELTIME |
109 profile_start(&log_start); | 122 profile_start(&log_start); |
358 * Close a channel and free all its resources if there is no further action | 371 * Close a channel and free all its resources if there is no further action |
359 * possible, there is no callback to be invoked or the associated job was | 372 * possible, there is no callback to be invoked or the associated job was |
360 * killed. | 373 * killed. |
361 * Return TRUE if the channel was freed. | 374 * Return TRUE if the channel was freed. |
362 */ | 375 */ |
363 int | 376 static int |
364 channel_may_free(channel_T *channel) | 377 channel_may_free(channel_T *channel) |
365 { | 378 { |
366 if (!channel_still_useful(channel)) | 379 if (!channel_still_useful(channel)) |
367 { | 380 { |
368 channel_free(channel); | 381 channel_free(channel); |
369 return TRUE; | 382 return TRUE; |
370 } | 383 } |
384 return FALSE; | |
385 } | |
386 | |
387 /* | |
388 * Decrement the reference count on "channel" and maybe free it when it goes | |
389 * down to zero. Don't free it if there is a pending action. | |
390 * Returns TRUE when the channel is no longer referenced. | |
391 */ | |
392 int | |
393 channel_unref(channel_T *channel) | |
394 { | |
395 if (channel != NULL && --channel->ch_refcount <= 0) | |
396 return channel_may_free(channel); | |
371 return FALSE; | 397 return FALSE; |
372 } | 398 } |
373 | 399 |
374 /* | 400 /* |
375 * Close a channel and free all its resources. | 401 * Close a channel and free all its resources. |
815 | 841 |
816 #ifdef FEAT_GUI | 842 #ifdef FEAT_GUI |
817 channel_gui_register_one(channel, PART_SOCK); | 843 channel_gui_register_one(channel, PART_SOCK); |
818 #endif | 844 #endif |
819 | 845 |
846 return channel; | |
847 } | |
848 | |
849 /* | |
850 * Implements ch_open(). | |
851 */ | |
852 channel_T * | |
853 channel_open_func(typval_T *argvars) | |
854 { | |
855 char_u *address; | |
856 char_u *p; | |
857 char *rest; | |
858 int port; | |
859 jobopt_T opt; | |
860 channel_T *channel; | |
861 | |
862 address = get_tv_string(&argvars[0]); | |
863 if (argvars[1].v_type != VAR_UNKNOWN | |
864 && (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL)) | |
865 { | |
866 EMSG(_(e_invarg)); | |
867 return NULL; | |
868 } | |
869 | |
870 /* parse address */ | |
871 p = vim_strchr(address, ':'); | |
872 if (p == NULL) | |
873 { | |
874 EMSG2(_(e_invarg2), address); | |
875 return NULL; | |
876 } | |
877 *p++ = NUL; | |
878 port = strtol((char *)p, &rest, 10); | |
879 if (*address == NUL || port <= 0 || *rest != NUL) | |
880 { | |
881 p[-1] = ':'; | |
882 EMSG2(_(e_invarg2), address); | |
883 return NULL; | |
884 } | |
885 | |
886 /* parse options */ | |
887 clear_job_options(&opt); | |
888 opt.jo_mode = MODE_JSON; | |
889 opt.jo_timeout = 2000; | |
890 if (get_job_options(&argvars[1], &opt, | |
891 JO_MODE_ALL + JO_CB_ALL + JO_WAITTIME + JO_TIMEOUT_ALL) == FAIL) | |
892 return NULL; | |
893 if (opt.jo_timeout < 0) | |
894 { | |
895 EMSG(_(e_invarg)); | |
896 return NULL; | |
897 } | |
898 | |
899 channel = channel_open((char *)address, port, opt.jo_waittime, NULL); | |
900 if (channel != NULL) | |
901 { | |
902 opt.jo_set = JO_ALL; | |
903 channel_set_options(channel, &opt); | |
904 } | |
820 return channel; | 905 return channel; |
821 } | 906 } |
822 | 907 |
823 static void | 908 static void |
824 may_close_part(sock_T *fd) | 909 may_close_part(sock_T *fd) |
2305 } | 2390 } |
2306 channel->ch_part[part].ch_block_id = 0; | 2391 channel->ch_part[part].ch_block_id = 0; |
2307 return FAIL; | 2392 return FAIL; |
2308 } | 2393 } |
2309 | 2394 |
2395 /* | |
2396 * Common for ch_read() and ch_readraw(). | |
2397 */ | |
2398 void | |
2399 common_channel_read(typval_T *argvars, typval_T *rettv, int raw) | |
2400 { | |
2401 channel_T *channel; | |
2402 int part; | |
2403 jobopt_T opt; | |
2404 int mode; | |
2405 int timeout; | |
2406 int id = -1; | |
2407 typval_T *listtv = NULL; | |
2408 | |
2409 /* return an empty string by default */ | |
2410 rettv->v_type = VAR_STRING; | |
2411 rettv->vval.v_string = NULL; | |
2412 | |
2413 clear_job_options(&opt); | |
2414 if (get_job_options(&argvars[1], &opt, JO_TIMEOUT + JO_PART + JO_ID) | |
2415 == FAIL) | |
2416 return; | |
2417 | |
2418 channel = get_channel_arg(&argvars[0], TRUE); | |
2419 if (channel != NULL) | |
2420 { | |
2421 if (opt.jo_set & JO_PART) | |
2422 part = opt.jo_part; | |
2423 else | |
2424 part = channel_part_read(channel); | |
2425 mode = channel_get_mode(channel, part); | |
2426 timeout = channel_get_timeout(channel, part); | |
2427 if (opt.jo_set & JO_TIMEOUT) | |
2428 timeout = opt.jo_timeout; | |
2429 | |
2430 if (raw || mode == MODE_RAW || mode == MODE_NL) | |
2431 rettv->vval.v_string = channel_read_block(channel, part, timeout); | |
2432 else | |
2433 { | |
2434 if (opt.jo_set & JO_ID) | |
2435 id = opt.jo_id; | |
2436 channel_read_json_block(channel, part, timeout, id, &listtv); | |
2437 if (listtv != NULL) | |
2438 { | |
2439 *rettv = *listtv; | |
2440 vim_free(listtv); | |
2441 } | |
2442 else | |
2443 { | |
2444 rettv->v_type = VAR_SPECIAL; | |
2445 rettv->vval.v_number = VVAL_NONE; | |
2446 } | |
2447 } | |
2448 } | |
2449 } | |
2450 | |
2310 # if defined(WIN32) || defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK) \ | 2451 # if defined(WIN32) || defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK) \ |
2311 || defined(PROTO) | 2452 || defined(PROTO) |
2312 /* | 2453 /* |
2313 * Lookup the channel from the socket. Set "partp" to the fd index. | 2454 * Lookup the channel from the socket. Set "partp" to the fd index. |
2314 * Returns NULL when the socket isn't found. | 2455 * Returns NULL when the socket isn't found. |
2408 return FAIL; | 2549 return FAIL; |
2409 } | 2550 } |
2410 | 2551 |
2411 channel->ch_error = FALSE; | 2552 channel->ch_error = FALSE; |
2412 return OK; | 2553 return OK; |
2554 } | |
2555 | |
2556 /* | |
2557 * Common for "ch_sendexpr()" and "ch_sendraw()". | |
2558 * Returns the channel if the caller should read the response. | |
2559 * Sets "part_read" to the the read fd. | |
2560 * Otherwise returns NULL. | |
2561 */ | |
2562 channel_T * | |
2563 send_common( | |
2564 typval_T *argvars, | |
2565 char_u *text, | |
2566 int id, | |
2567 int eval, | |
2568 jobopt_T *opt, | |
2569 char *fun, | |
2570 int *part_read) | |
2571 { | |
2572 channel_T *channel; | |
2573 int part_send; | |
2574 | |
2575 channel = get_channel_arg(&argvars[0], TRUE); | |
2576 if (channel == NULL) | |
2577 return NULL; | |
2578 part_send = channel_part_send(channel); | |
2579 *part_read = channel_part_read(channel); | |
2580 | |
2581 clear_job_options(opt); | |
2582 if (get_job_options(&argvars[2], opt, JO_CALLBACK + JO_TIMEOUT) == FAIL) | |
2583 return NULL; | |
2584 | |
2585 /* Set the callback. An empty callback means no callback and not reading | |
2586 * the response. With "ch_evalexpr()" and "ch_evalraw()" a callback is not | |
2587 * allowed. */ | |
2588 if (opt->jo_callback != NULL && *opt->jo_callback != NUL) | |
2589 { | |
2590 if (eval) | |
2591 { | |
2592 EMSG2(_("E917: Cannot use a callback with %s()"), fun); | |
2593 return NULL; | |
2594 } | |
2595 channel_set_req_callback(channel, part_send, opt->jo_callback, id); | |
2596 } | |
2597 | |
2598 if (channel_send(channel, part_send, text, fun) == OK | |
2599 && opt->jo_callback == NULL) | |
2600 return channel; | |
2601 return NULL; | |
2602 } | |
2603 | |
2604 /* | |
2605 * common for "ch_evalexpr()" and "ch_sendexpr()" | |
2606 */ | |
2607 void | |
2608 ch_expr_common(typval_T *argvars, typval_T *rettv, int eval) | |
2609 { | |
2610 char_u *text; | |
2611 typval_T *listtv; | |
2612 channel_T *channel; | |
2613 int id; | |
2614 ch_mode_T ch_mode; | |
2615 int part_send; | |
2616 int part_read; | |
2617 jobopt_T opt; | |
2618 int timeout; | |
2619 | |
2620 /* return an empty string by default */ | |
2621 rettv->v_type = VAR_STRING; | |
2622 rettv->vval.v_string = NULL; | |
2623 | |
2624 channel = get_channel_arg(&argvars[0], TRUE); | |
2625 if (channel == NULL) | |
2626 return; | |
2627 part_send = channel_part_send(channel); | |
2628 | |
2629 ch_mode = channel_get_mode(channel, part_send); | |
2630 if (ch_mode == MODE_RAW || ch_mode == MODE_NL) | |
2631 { | |
2632 EMSG(_("E912: cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel")); | |
2633 return; | |
2634 } | |
2635 | |
2636 id = channel_get_id(); | |
2637 text = json_encode_nr_expr(id, &argvars[1], | |
2638 ch_mode == MODE_JS ? JSON_JS : 0); | |
2639 if (text == NULL) | |
2640 return; | |
2641 | |
2642 channel = send_common(argvars, text, id, eval, &opt, | |
2643 eval ? "ch_evalexpr" : "ch_sendexpr", &part_read); | |
2644 vim_free(text); | |
2645 if (channel != NULL && eval) | |
2646 { | |
2647 if (opt.jo_set & JO_TIMEOUT) | |
2648 timeout = opt.jo_timeout; | |
2649 else | |
2650 timeout = channel_get_timeout(channel, part_read); | |
2651 timeout = channel_get_timeout(channel, part_read); | |
2652 if (channel_read_json_block(channel, part_read, timeout, id, &listtv) | |
2653 == OK) | |
2654 { | |
2655 list_T *list = listtv->vval.v_list; | |
2656 | |
2657 /* Move the item from the list and then change the type to | |
2658 * avoid the value being freed. */ | |
2659 *rettv = list->lv_last->li_tv; | |
2660 list->lv_last->li_tv.v_type = VAR_NUMBER; | |
2661 free_tv(listtv); | |
2662 } | |
2663 } | |
2664 } | |
2665 | |
2666 /* | |
2667 * common for "ch_evalraw()" and "ch_sendraw()" | |
2668 */ | |
2669 void | |
2670 ch_raw_common(typval_T *argvars, typval_T *rettv, int eval) | |
2671 { | |
2672 char_u buf[NUMBUFLEN]; | |
2673 char_u *text; | |
2674 channel_T *channel; | |
2675 int part_read; | |
2676 jobopt_T opt; | |
2677 int timeout; | |
2678 | |
2679 /* return an empty string by default */ | |
2680 rettv->v_type = VAR_STRING; | |
2681 rettv->vval.v_string = NULL; | |
2682 | |
2683 text = get_tv_string_buf(&argvars[1], buf); | |
2684 channel = send_common(argvars, text, 0, eval, &opt, | |
2685 eval ? "ch_evalraw" : "ch_sendraw", &part_read); | |
2686 if (channel != NULL && eval) | |
2687 { | |
2688 if (opt.jo_set & JO_TIMEOUT) | |
2689 timeout = opt.jo_timeout; | |
2690 else | |
2691 timeout = channel_get_timeout(channel, part_read); | |
2692 rettv->vval.v_string = channel_read_block(channel, part_read, timeout); | |
2693 } | |
2413 } | 2694 } |
2414 | 2695 |
2415 # if (defined(UNIX) && !defined(HAVE_SELECT)) || defined(PROTO) | 2696 # if (defined(UNIX) && !defined(HAVE_SELECT)) || defined(PROTO) |
2416 /* | 2697 /* |
2417 * Add open channels to the poll struct. | 2698 * Add open channels to the poll struct. |
2693 channel_get_timeout(channel_T *channel, int part) | 2974 channel_get_timeout(channel_T *channel, int part) |
2694 { | 2975 { |
2695 return channel->ch_part[part].ch_timeout; | 2976 return channel->ch_part[part].ch_timeout; |
2696 } | 2977 } |
2697 | 2978 |
2979 /* | |
2980 * Get a callback from "arg". It can be a Funcref or a function name. | |
2981 * When "arg" is zero return an empty string. | |
2982 * Return NULL for an invalid argument. | |
2983 */ | |
2984 static char_u * | |
2985 get_callback(typval_T *arg) | |
2986 { | |
2987 if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING) | |
2988 return arg->vval.v_string; | |
2989 if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0) | |
2990 return (char_u *)""; | |
2991 EMSG(_("E999: Invalid callback argument")); | |
2992 return NULL; | |
2993 } | |
2994 | |
2995 static int | |
2996 handle_mode(typval_T *item, jobopt_T *opt, ch_mode_T *modep, int jo) | |
2997 { | |
2998 char_u *val = get_tv_string(item); | |
2999 | |
3000 opt->jo_set |= jo; | |
3001 if (STRCMP(val, "nl") == 0) | |
3002 *modep = MODE_NL; | |
3003 else if (STRCMP(val, "raw") == 0) | |
3004 *modep = MODE_RAW; | |
3005 else if (STRCMP(val, "js") == 0) | |
3006 *modep = MODE_JS; | |
3007 else if (STRCMP(val, "json") == 0) | |
3008 *modep = MODE_JSON; | |
3009 else | |
3010 { | |
3011 EMSG2(_(e_invarg2), val); | |
3012 return FAIL; | |
3013 } | |
3014 return OK; | |
3015 } | |
3016 | |
3017 static int | |
3018 handle_io(typval_T *item, int part, jobopt_T *opt) | |
3019 { | |
3020 char_u *val = get_tv_string(item); | |
3021 | |
3022 opt->jo_set |= JO_OUT_IO << (part - PART_OUT); | |
3023 if (STRCMP(val, "null") == 0) | |
3024 opt->jo_io[part] = JIO_NULL; | |
3025 else if (STRCMP(val, "pipe") == 0) | |
3026 opt->jo_io[part] = JIO_PIPE; | |
3027 else if (STRCMP(val, "file") == 0) | |
3028 opt->jo_io[part] = JIO_FILE; | |
3029 else if (STRCMP(val, "buffer") == 0) | |
3030 opt->jo_io[part] = JIO_BUFFER; | |
3031 else if (STRCMP(val, "out") == 0 && part == PART_ERR) | |
3032 opt->jo_io[part] = JIO_OUT; | |
3033 else | |
3034 { | |
3035 EMSG2(_(e_invarg2), val); | |
3036 return FAIL; | |
3037 } | |
3038 return OK; | |
3039 } | |
3040 | |
3041 void | |
3042 clear_job_options(jobopt_T *opt) | |
3043 { | |
3044 vim_memset(opt, 0, sizeof(jobopt_T)); | |
3045 } | |
3046 | |
3047 /* | |
3048 * Get the PART_ number from the first character of an option name. | |
3049 */ | |
3050 static int | |
3051 part_from_char(int c) | |
3052 { | |
3053 return c == 'i' ? PART_IN : c == 'o' ? PART_OUT: PART_ERR; | |
3054 } | |
3055 | |
3056 /* | |
3057 * Get the option entries from the dict in "tv", parse them and put the result | |
3058 * in "opt". | |
3059 * Only accept options in "supported". | |
3060 * If an option value is invalid return FAIL. | |
3061 */ | |
3062 int | |
3063 get_job_options(typval_T *tv, jobopt_T *opt, int supported) | |
3064 { | |
3065 typval_T *item; | |
3066 char_u *val; | |
3067 dict_T *dict; | |
3068 int todo; | |
3069 hashitem_T *hi; | |
3070 int part; | |
3071 | |
3072 opt->jo_set = 0; | |
3073 if (tv->v_type == VAR_UNKNOWN) | |
3074 return OK; | |
3075 if (tv->v_type != VAR_DICT) | |
3076 { | |
3077 EMSG(_(e_invarg)); | |
3078 return FAIL; | |
3079 } | |
3080 dict = tv->vval.v_dict; | |
3081 if (dict == NULL) | |
3082 return OK; | |
3083 | |
3084 todo = (int)dict->dv_hashtab.ht_used; | |
3085 for (hi = dict->dv_hashtab.ht_array; todo > 0; ++hi) | |
3086 if (!HASHITEM_EMPTY(hi)) | |
3087 { | |
3088 item = &dict_lookup(hi)->di_tv; | |
3089 | |
3090 if (STRCMP(hi->hi_key, "mode") == 0) | |
3091 { | |
3092 if (!(supported & JO_MODE)) | |
3093 break; | |
3094 if (handle_mode(item, opt, &opt->jo_mode, JO_MODE) == FAIL) | |
3095 return FAIL; | |
3096 } | |
3097 else if (STRCMP(hi->hi_key, "in-mode") == 0) | |
3098 { | |
3099 if (!(supported & JO_IN_MODE)) | |
3100 break; | |
3101 if (handle_mode(item, opt, &opt->jo_in_mode, JO_IN_MODE) | |
3102 == FAIL) | |
3103 return FAIL; | |
3104 } | |
3105 else if (STRCMP(hi->hi_key, "out-mode") == 0) | |
3106 { | |
3107 if (!(supported & JO_OUT_MODE)) | |
3108 break; | |
3109 if (handle_mode(item, opt, &opt->jo_out_mode, JO_OUT_MODE) | |
3110 == FAIL) | |
3111 return FAIL; | |
3112 } | |
3113 else if (STRCMP(hi->hi_key, "err-mode") == 0) | |
3114 { | |
3115 if (!(supported & JO_ERR_MODE)) | |
3116 break; | |
3117 if (handle_mode(item, opt, &opt->jo_err_mode, JO_ERR_MODE) | |
3118 == FAIL) | |
3119 return FAIL; | |
3120 } | |
3121 else if (STRCMP(hi->hi_key, "in-io") == 0 | |
3122 || STRCMP(hi->hi_key, "out-io") == 0 | |
3123 || STRCMP(hi->hi_key, "err-io") == 0) | |
3124 { | |
3125 if (!(supported & JO_OUT_IO)) | |
3126 break; | |
3127 if (handle_io(item, part_from_char(*hi->hi_key), opt) == FAIL) | |
3128 return FAIL; | |
3129 } | |
3130 else if (STRCMP(hi->hi_key, "in-name") == 0 | |
3131 || STRCMP(hi->hi_key, "out-name") == 0 | |
3132 || STRCMP(hi->hi_key, "err-name") == 0) | |
3133 { | |
3134 part = part_from_char(*hi->hi_key); | |
3135 | |
3136 if (!(supported & JO_OUT_IO)) | |
3137 break; | |
3138 opt->jo_set |= JO_OUT_NAME << (part - PART_OUT); | |
3139 opt->jo_io_name[part] = | |
3140 get_tv_string_buf_chk(item, opt->jo_io_name_buf[part]); | |
3141 } | |
3142 else if (STRCMP(hi->hi_key, "in-buf") == 0 | |
3143 || STRCMP(hi->hi_key, "out-buf") == 0 | |
3144 || STRCMP(hi->hi_key, "err-buf") == 0) | |
3145 { | |
3146 part = part_from_char(*hi->hi_key); | |
3147 | |
3148 if (!(supported & JO_OUT_IO)) | |
3149 break; | |
3150 opt->jo_set |= JO_OUT_BUF << (part - PART_OUT); | |
3151 opt->jo_io_buf[part] = get_tv_number(item); | |
3152 if (opt->jo_io_buf[part] <= 0) | |
3153 { | |
3154 EMSG2(_(e_invarg2), get_tv_string(item)); | |
3155 return FAIL; | |
3156 } | |
3157 if (buflist_findnr(opt->jo_io_buf[part]) == NULL) | |
3158 { | |
3159 EMSGN(_(e_nobufnr), (long)opt->jo_io_buf[part]); | |
3160 return FAIL; | |
3161 } | |
3162 } | |
3163 else if (STRCMP(hi->hi_key, "in-top") == 0 | |
3164 || STRCMP(hi->hi_key, "in-bot") == 0) | |
3165 { | |
3166 linenr_T *lp; | |
3167 | |
3168 if (!(supported & JO_OUT_IO)) | |
3169 break; | |
3170 if (hi->hi_key[3] == 't') | |
3171 { | |
3172 lp = &opt->jo_in_top; | |
3173 opt->jo_set |= JO_IN_TOP; | |
3174 } | |
3175 else | |
3176 { | |
3177 lp = &opt->jo_in_bot; | |
3178 opt->jo_set |= JO_IN_BOT; | |
3179 } | |
3180 *lp = get_tv_number(item); | |
3181 if (*lp < 0) | |
3182 { | |
3183 EMSG2(_(e_invarg2), get_tv_string(item)); | |
3184 return FAIL; | |
3185 } | |
3186 } | |
3187 else if (STRCMP(hi->hi_key, "channel") == 0) | |
3188 { | |
3189 if (!(supported & JO_OUT_IO)) | |
3190 break; | |
3191 opt->jo_set |= JO_CHANNEL; | |
3192 if (item->v_type != VAR_CHANNEL) | |
3193 { | |
3194 EMSG2(_(e_invarg2), "channel"); | |
3195 return FAIL; | |
3196 } | |
3197 opt->jo_channel = item->vval.v_channel; | |
3198 } | |
3199 else if (STRCMP(hi->hi_key, "callback") == 0) | |
3200 { | |
3201 if (!(supported & JO_CALLBACK)) | |
3202 break; | |
3203 opt->jo_set |= JO_CALLBACK; | |
3204 opt->jo_callback = get_callback(item); | |
3205 if (opt->jo_callback == NULL) | |
3206 { | |
3207 EMSG2(_(e_invarg2), "callback"); | |
3208 return FAIL; | |
3209 } | |
3210 } | |
3211 else if (STRCMP(hi->hi_key, "out-cb") == 0) | |
3212 { | |
3213 if (!(supported & JO_OUT_CALLBACK)) | |
3214 break; | |
3215 opt->jo_set |= JO_OUT_CALLBACK; | |
3216 opt->jo_out_cb = get_callback(item); | |
3217 if (opt->jo_out_cb == NULL) | |
3218 { | |
3219 EMSG2(_(e_invarg2), "out-cb"); | |
3220 return FAIL; | |
3221 } | |
3222 } | |
3223 else if (STRCMP(hi->hi_key, "err-cb") == 0) | |
3224 { | |
3225 if (!(supported & JO_ERR_CALLBACK)) | |
3226 break; | |
3227 opt->jo_set |= JO_ERR_CALLBACK; | |
3228 opt->jo_err_cb = get_callback(item); | |
3229 if (opt->jo_err_cb == NULL) | |
3230 { | |
3231 EMSG2(_(e_invarg2), "err-cb"); | |
3232 return FAIL; | |
3233 } | |
3234 } | |
3235 else if (STRCMP(hi->hi_key, "close-cb") == 0) | |
3236 { | |
3237 if (!(supported & JO_CLOSE_CALLBACK)) | |
3238 break; | |
3239 opt->jo_set |= JO_CLOSE_CALLBACK; | |
3240 opt->jo_close_cb = get_callback(item); | |
3241 if (opt->jo_close_cb == NULL) | |
3242 { | |
3243 EMSG2(_(e_invarg2), "close-cb"); | |
3244 return FAIL; | |
3245 } | |
3246 } | |
3247 else if (STRCMP(hi->hi_key, "waittime") == 0) | |
3248 { | |
3249 if (!(supported & JO_WAITTIME)) | |
3250 break; | |
3251 opt->jo_set |= JO_WAITTIME; | |
3252 opt->jo_waittime = get_tv_number(item); | |
3253 } | |
3254 else if (STRCMP(hi->hi_key, "timeout") == 0) | |
3255 { | |
3256 if (!(supported & JO_TIMEOUT)) | |
3257 break; | |
3258 opt->jo_set |= JO_TIMEOUT; | |
3259 opt->jo_timeout = get_tv_number(item); | |
3260 } | |
3261 else if (STRCMP(hi->hi_key, "out-timeout") == 0) | |
3262 { | |
3263 if (!(supported & JO_OUT_TIMEOUT)) | |
3264 break; | |
3265 opt->jo_set |= JO_OUT_TIMEOUT; | |
3266 opt->jo_out_timeout = get_tv_number(item); | |
3267 } | |
3268 else if (STRCMP(hi->hi_key, "err-timeout") == 0) | |
3269 { | |
3270 if (!(supported & JO_ERR_TIMEOUT)) | |
3271 break; | |
3272 opt->jo_set |= JO_ERR_TIMEOUT; | |
3273 opt->jo_err_timeout = get_tv_number(item); | |
3274 } | |
3275 else if (STRCMP(hi->hi_key, "part") == 0) | |
3276 { | |
3277 if (!(supported & JO_PART)) | |
3278 break; | |
3279 opt->jo_set |= JO_PART; | |
3280 val = get_tv_string(item); | |
3281 if (STRCMP(val, "err") == 0) | |
3282 opt->jo_part = PART_ERR; | |
3283 else | |
3284 { | |
3285 EMSG2(_(e_invarg2), val); | |
3286 return FAIL; | |
3287 } | |
3288 } | |
3289 else if (STRCMP(hi->hi_key, "id") == 0) | |
3290 { | |
3291 if (!(supported & JO_ID)) | |
3292 break; | |
3293 opt->jo_set |= JO_ID; | |
3294 opt->jo_id = get_tv_number(item); | |
3295 } | |
3296 else if (STRCMP(hi->hi_key, "stoponexit") == 0) | |
3297 { | |
3298 if (!(supported & JO_STOPONEXIT)) | |
3299 break; | |
3300 opt->jo_set |= JO_STOPONEXIT; | |
3301 opt->jo_stoponexit = get_tv_string_buf_chk(item, | |
3302 opt->jo_soe_buf); | |
3303 if (opt->jo_stoponexit == NULL) | |
3304 { | |
3305 EMSG2(_(e_invarg2), "stoponexit"); | |
3306 return FAIL; | |
3307 } | |
3308 } | |
3309 else if (STRCMP(hi->hi_key, "exit-cb") == 0) | |
3310 { | |
3311 if (!(supported & JO_EXIT_CB)) | |
3312 break; | |
3313 opt->jo_set |= JO_EXIT_CB; | |
3314 opt->jo_exit_cb = get_tv_string_buf_chk(item, opt->jo_ecb_buf); | |
3315 if (opt->jo_exit_cb == NULL) | |
3316 { | |
3317 EMSG2(_(e_invarg2), "exit-cb"); | |
3318 return FAIL; | |
3319 } | |
3320 } | |
3321 else | |
3322 break; | |
3323 --todo; | |
3324 } | |
3325 if (todo > 0) | |
3326 { | |
3327 EMSG2(_(e_invarg2), hi->hi_key); | |
3328 return FAIL; | |
3329 } | |
3330 | |
3331 return OK; | |
3332 } | |
3333 | |
3334 /* | |
3335 * Get the channel from the argument. | |
3336 * Returns NULL if the handle is invalid. | |
3337 */ | |
3338 channel_T * | |
3339 get_channel_arg(typval_T *tv, int check_open) | |
3340 { | |
3341 channel_T *channel = NULL; | |
3342 | |
3343 if (tv->v_type == VAR_JOB) | |
3344 { | |
3345 if (tv->vval.v_job != NULL) | |
3346 channel = tv->vval.v_job->jv_channel; | |
3347 } | |
3348 else if (tv->v_type == VAR_CHANNEL) | |
3349 { | |
3350 channel = tv->vval.v_channel; | |
3351 } | |
3352 else | |
3353 { | |
3354 EMSG2(_(e_invarg2), get_tv_string(tv)); | |
3355 return NULL; | |
3356 } | |
3357 | |
3358 if (check_open && (channel == NULL || !channel_is_open(channel))) | |
3359 { | |
3360 EMSG(_("E906: not an open channel")); | |
3361 return NULL; | |
3362 } | |
3363 return channel; | |
3364 } | |
3365 | |
3366 static job_T *first_job = NULL; | |
3367 | |
3368 static void | |
3369 job_free(job_T *job) | |
3370 { | |
3371 ch_log(job->jv_channel, "Freeing job"); | |
3372 if (job->jv_channel != NULL) | |
3373 { | |
3374 /* The link from the channel to the job doesn't count as a reference, | |
3375 * thus don't decrement the refcount of the job. The reference from | |
3376 * the job to the channel does count the refrence, decrement it and | |
3377 * NULL the reference. We don't set ch_job_killed, unreferencing the | |
3378 * job doesn't mean it stops running. */ | |
3379 job->jv_channel->ch_job = NULL; | |
3380 channel_unref(job->jv_channel); | |
3381 } | |
3382 mch_clear_job(job); | |
3383 | |
3384 if (job->jv_next != NULL) | |
3385 job->jv_next->jv_prev = job->jv_prev; | |
3386 if (job->jv_prev == NULL) | |
3387 first_job = job->jv_next; | |
3388 else | |
3389 job->jv_prev->jv_next = job->jv_next; | |
3390 | |
3391 vim_free(job->jv_stoponexit); | |
3392 vim_free(job->jv_exit_cb); | |
3393 vim_free(job); | |
3394 } | |
3395 | |
3396 void | |
3397 job_unref(job_T *job) | |
3398 { | |
3399 if (job != NULL && --job->jv_refcount <= 0) | |
3400 { | |
3401 /* Do not free the job when it has not ended yet and there is a | |
3402 * "stoponexit" flag or an exit callback. */ | |
3403 if (job->jv_status != JOB_STARTED | |
3404 || (job->jv_stoponexit == NULL && job->jv_exit_cb == NULL)) | |
3405 { | |
3406 job_free(job); | |
3407 } | |
3408 else if (job->jv_channel != NULL) | |
3409 { | |
3410 /* Do remove the link to the channel, otherwise it hangs | |
3411 * around until Vim exits. See job_free() for refcount. */ | |
3412 job->jv_channel->ch_job = NULL; | |
3413 channel_unref(job->jv_channel); | |
3414 job->jv_channel = NULL; | |
3415 } | |
3416 } | |
3417 } | |
3418 | |
3419 /* | |
3420 * Allocate a job. Sets the refcount to one and sets options default. | |
3421 */ | |
3422 static job_T * | |
3423 job_alloc(void) | |
3424 { | |
3425 job_T *job; | |
3426 | |
3427 job = (job_T *)alloc_clear(sizeof(job_T)); | |
3428 if (job != NULL) | |
3429 { | |
3430 job->jv_refcount = 1; | |
3431 job->jv_stoponexit = vim_strsave((char_u *)"term"); | |
3432 | |
3433 if (first_job != NULL) | |
3434 { | |
3435 first_job->jv_prev = job; | |
3436 job->jv_next = first_job; | |
3437 } | |
3438 first_job = job; | |
3439 } | |
3440 return job; | |
3441 } | |
3442 | |
3443 void | |
3444 job_set_options(job_T *job, jobopt_T *opt) | |
3445 { | |
3446 if (opt->jo_set & JO_STOPONEXIT) | |
3447 { | |
3448 vim_free(job->jv_stoponexit); | |
3449 if (opt->jo_stoponexit == NULL || *opt->jo_stoponexit == NUL) | |
3450 job->jv_stoponexit = NULL; | |
3451 else | |
3452 job->jv_stoponexit = vim_strsave(opt->jo_stoponexit); | |
3453 } | |
3454 if (opt->jo_set & JO_EXIT_CB) | |
3455 { | |
3456 vim_free(job->jv_exit_cb); | |
3457 if (opt->jo_exit_cb == NULL || *opt->jo_exit_cb == NUL) | |
3458 job->jv_exit_cb = NULL; | |
3459 else | |
3460 job->jv_exit_cb = vim_strsave(opt->jo_exit_cb); | |
3461 } | |
3462 } | |
3463 | |
3464 /* | |
3465 * Called when Vim is exiting: kill all jobs that have the "stoponexit" flag. | |
3466 */ | |
3467 void | |
3468 job_stop_on_exit() | |
3469 { | |
3470 job_T *job; | |
3471 | |
3472 for (job = first_job; job != NULL; job = job->jv_next) | |
3473 if (job->jv_status == JOB_STARTED && job->jv_stoponexit != NULL) | |
3474 mch_stop_job(job, job->jv_stoponexit); | |
3475 } | |
3476 | |
3477 /* | |
3478 * Called once in a while: check if any jobs with an "exit-cb" have ended. | |
3479 */ | |
3480 void | |
3481 job_check_ended(void) | |
3482 { | |
3483 static time_t last_check = 0; | |
3484 time_t now; | |
3485 job_T *job; | |
3486 job_T *next; | |
3487 | |
3488 /* Only do this once in 10 seconds. */ | |
3489 now = time(NULL); | |
3490 if (last_check + 10 < now) | |
3491 { | |
3492 last_check = now; | |
3493 for (job = first_job; job != NULL; job = next) | |
3494 { | |
3495 next = job->jv_next; | |
3496 if (job->jv_status == JOB_STARTED && job->jv_exit_cb != NULL) | |
3497 job_status(job); /* may free "job" */ | |
3498 } | |
3499 } | |
3500 } | |
3501 | |
3502 /* | |
3503 * "job_start()" function | |
3504 */ | |
3505 job_T * | |
3506 job_start(typval_T *argvars) | |
3507 { | |
3508 job_T *job; | |
3509 char_u *cmd = NULL; | |
3510 #if defined(UNIX) | |
3511 # define USE_ARGV | |
3512 char **argv = NULL; | |
3513 int argc = 0; | |
3514 #else | |
3515 garray_T ga; | |
3516 #endif | |
3517 jobopt_T opt; | |
3518 int part; | |
3519 | |
3520 job = job_alloc(); | |
3521 if (job == NULL) | |
3522 return NULL; | |
3523 | |
3524 job->jv_status = JOB_FAILED; | |
3525 | |
3526 /* Default mode is NL. */ | |
3527 clear_job_options(&opt); | |
3528 opt.jo_mode = MODE_NL; | |
3529 if (get_job_options(&argvars[1], &opt, | |
3530 JO_MODE_ALL + JO_CB_ALL + JO_TIMEOUT_ALL | |
3531 + JO_STOPONEXIT + JO_EXIT_CB + JO_OUT_IO) == FAIL) | |
3532 return job; | |
3533 | |
3534 /* Check that when io is "file" that there is a file name. */ | |
3535 for (part = PART_OUT; part <= PART_IN; ++part) | |
3536 if ((opt.jo_set & (JO_OUT_IO << (part - PART_OUT))) | |
3537 && opt.jo_io[part] == JIO_FILE | |
3538 && (!(opt.jo_set & (JO_OUT_NAME << (part - PART_OUT))) | |
3539 || *opt.jo_io_name[part] == NUL)) | |
3540 { | |
3541 EMSG(_("E920: -io file requires -name to be set")); | |
3542 return job; | |
3543 } | |
3544 | |
3545 if ((opt.jo_set & JO_IN_IO) && opt.jo_io[PART_IN] == JIO_BUFFER) | |
3546 { | |
3547 buf_T *buf = NULL; | |
3548 | |
3549 /* check that we can find the buffer before starting the job */ | |
3550 if (opt.jo_set & JO_IN_BUF) | |
3551 { | |
3552 buf = buflist_findnr(opt.jo_io_buf[PART_IN]); | |
3553 if (buf == NULL) | |
3554 EMSGN(_(e_nobufnr), (long)opt.jo_io_buf[PART_IN]); | |
3555 } | |
3556 else if (!(opt.jo_set & JO_IN_NAME)) | |
3557 { | |
3558 EMSG(_("E915: in-io buffer requires in-buf or in-name to be set")); | |
3559 } | |
3560 else | |
3561 buf = buflist_find_by_name(opt.jo_io_name[PART_IN], FALSE); | |
3562 if (buf == NULL) | |
3563 return job; | |
3564 if (buf->b_ml.ml_mfp == NULL) | |
3565 { | |
3566 char_u numbuf[NUMBUFLEN]; | |
3567 char_u *s; | |
3568 | |
3569 if (opt.jo_set & JO_IN_BUF) | |
3570 { | |
3571 sprintf((char *)numbuf, "%d", opt.jo_io_buf[PART_IN]); | |
3572 s = numbuf; | |
3573 } | |
3574 else | |
3575 s = opt.jo_io_name[PART_IN]; | |
3576 EMSG2(_("E918: buffer must be loaded: %s"), s); | |
3577 return job; | |
3578 } | |
3579 job->jv_in_buf = buf; | |
3580 } | |
3581 | |
3582 job_set_options(job, &opt); | |
3583 | |
3584 #ifndef USE_ARGV | |
3585 ga_init2(&ga, (int)sizeof(char*), 20); | |
3586 #endif | |
3587 | |
3588 if (argvars[0].v_type == VAR_STRING) | |
3589 { | |
3590 /* Command is a string. */ | |
3591 cmd = argvars[0].vval.v_string; | |
3592 #ifdef USE_ARGV | |
3593 if (mch_parse_cmd(cmd, FALSE, &argv, &argc) == FAIL) | |
3594 return job; | |
3595 argv[argc] = NULL; | |
3596 #endif | |
3597 } | |
3598 else if (argvars[0].v_type != VAR_LIST | |
3599 || argvars[0].vval.v_list == NULL | |
3600 || argvars[0].vval.v_list->lv_len < 1) | |
3601 { | |
3602 EMSG(_(e_invarg)); | |
3603 return job; | |
3604 } | |
3605 else | |
3606 { | |
3607 list_T *l = argvars[0].vval.v_list; | |
3608 listitem_T *li; | |
3609 char_u *s; | |
3610 | |
3611 #ifdef USE_ARGV | |
3612 /* Pass argv[] to mch_call_shell(). */ | |
3613 argv = (char **)alloc(sizeof(char *) * (l->lv_len + 1)); | |
3614 if (argv == NULL) | |
3615 return job; | |
3616 #endif | |
3617 for (li = l->lv_first; li != NULL; li = li->li_next) | |
3618 { | |
3619 s = get_tv_string_chk(&li->li_tv); | |
3620 if (s == NULL) | |
3621 goto theend; | |
3622 #ifdef USE_ARGV | |
3623 argv[argc++] = (char *)s; | |
3624 #else | |
3625 /* Only escape when needed, double quotes are not always allowed. */ | |
3626 if (li != l->lv_first && vim_strpbrk(s, (char_u *)" \t\"") != NULL) | |
3627 { | |
3628 s = vim_strsave_shellescape(s, FALSE, TRUE); | |
3629 if (s == NULL) | |
3630 goto theend; | |
3631 ga_concat(&ga, s); | |
3632 vim_free(s); | |
3633 } | |
3634 else | |
3635 ga_concat(&ga, s); | |
3636 if (li->li_next != NULL) | |
3637 ga_append(&ga, ' '); | |
3638 #endif | |
3639 } | |
3640 #ifdef USE_ARGV | |
3641 argv[argc] = NULL; | |
3642 #else | |
3643 cmd = ga.ga_data; | |
3644 #endif | |
3645 } | |
3646 | |
3647 #ifdef USE_ARGV | |
3648 if (ch_log_active()) | |
3649 { | |
3650 garray_T ga; | |
3651 int i; | |
3652 | |
3653 ga_init2(&ga, (int)sizeof(char), 200); | |
3654 for (i = 0; i < argc; ++i) | |
3655 { | |
3656 if (i > 0) | |
3657 ga_concat(&ga, (char_u *)" "); | |
3658 ga_concat(&ga, (char_u *)argv[i]); | |
3659 } | |
3660 ch_logs(NULL, "Starting job: %s", (char *)ga.ga_data); | |
3661 ga_clear(&ga); | |
3662 } | |
3663 mch_start_job(argv, job, &opt); | |
3664 #else | |
3665 ch_logs(NULL, "Starting job: %s", (char *)cmd); | |
3666 mch_start_job((char *)cmd, job, &opt); | |
3667 #endif | |
3668 | |
3669 /* If the channel is reading from a buffer, write lines now. */ | |
3670 if (job->jv_channel != NULL) | |
3671 channel_write_in(job->jv_channel); | |
3672 | |
3673 theend: | |
3674 #ifdef USE_ARGV | |
3675 vim_free(argv); | |
3676 #else | |
3677 vim_free(ga.ga_data); | |
3678 #endif | |
3679 return job; | |
3680 } | |
3681 | |
3682 /* | |
3683 * Get the status of "job" and invoke the exit callback when needed. | |
3684 * The returned string is not allocated. | |
3685 */ | |
3686 char * | |
3687 job_status(job_T *job) | |
3688 { | |
3689 char *result; | |
3690 | |
3691 if (job->jv_status == JOB_ENDED) | |
3692 /* No need to check, dead is dead. */ | |
3693 result = "dead"; | |
3694 else if (job->jv_status == JOB_FAILED) | |
3695 result = "fail"; | |
3696 else | |
3697 { | |
3698 result = mch_job_status(job); | |
3699 if (job->jv_status == JOB_ENDED) | |
3700 ch_log(job->jv_channel, "Job ended"); | |
3701 if (job->jv_status == JOB_ENDED && job->jv_exit_cb != NULL) | |
3702 { | |
3703 typval_T argv[3]; | |
3704 typval_T rettv; | |
3705 int dummy; | |
3706 | |
3707 /* invoke the exit callback; make sure the refcount is > 0 */ | |
3708 ++job->jv_refcount; | |
3709 argv[0].v_type = VAR_JOB; | |
3710 argv[0].vval.v_job = job; | |
3711 argv[1].v_type = VAR_NUMBER; | |
3712 argv[1].vval.v_number = job->jv_exitval; | |
3713 call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb), | |
3714 &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL); | |
3715 clear_tv(&rettv); | |
3716 --job->jv_refcount; | |
3717 } | |
3718 if (job->jv_status == JOB_ENDED && job->jv_refcount == 0) | |
3719 { | |
3720 /* The job was already unreferenced, now that it ended it can be | |
3721 * freed. Careful: caller must not use "job" after this! */ | |
3722 job_free(job); | |
3723 } | |
3724 } | |
3725 return result; | |
3726 } | |
3727 | |
3728 int | |
3729 job_stop(job_T *job, typval_T *argvars) | |
3730 { | |
3731 char_u *arg; | |
3732 | |
3733 if (argvars[1].v_type == VAR_UNKNOWN) | |
3734 arg = (char_u *)""; | |
3735 else | |
3736 { | |
3737 arg = get_tv_string_chk(&argvars[1]); | |
3738 if (arg == NULL) | |
3739 { | |
3740 EMSG(_(e_invarg)); | |
3741 return 0; | |
3742 } | |
3743 } | |
3744 ch_logs(job->jv_channel, "Stopping job with '%s'", (char *)arg); | |
3745 if (mch_stop_job(job, arg) == FAIL) | |
3746 return 0; | |
3747 | |
3748 /* Assume that "hup" does not kill the job. */ | |
3749 if (job->jv_channel != NULL && STRCMP(arg, "hup") != 0) | |
3750 job->jv_channel->ch_job_killed = TRUE; | |
3751 | |
3752 /* We don't try freeing the job, obviously the caller still has a | |
3753 * reference to it. */ | |
3754 return 1; | |
3755 } | |
3756 | |
2698 #endif /* FEAT_JOB_CHANNEL */ | 3757 #endif /* FEAT_JOB_CHANNEL */ |