diff src/eval.c @ 8041:c6443e78cf2d v7.4.1315

commit https://github.com/vim/vim/commit/7707344ddec9069b495b2a5ed41f2104466fc88b Author: Bram Moolenaar <Bram@vim.org> Date: Sat Feb 13 23:23:53 2016 +0100 patch 7.4.1315 Problem: Using a channel handle does not allow for freeing it when unused. Solution: Add the Channel variable type.
author Christian Brabandt <cb@256bit.org>
date Sat, 13 Feb 2016 23:30:05 +0100
parents ece323e2b57f
children 167e22951df4
line wrap: on
line diff
--- a/src/eval.c
+++ b/src/eval.c
@@ -509,6 +509,7 @@ static void f_ch_open(typval_T *argvars,
 static void f_ch_readraw(typval_T *argvars, typval_T *rettv);
 static void f_ch_sendexpr(typval_T *argvars, typval_T *rettv);
 static void f_ch_sendraw(typval_T *argvars, typval_T *rettv);
+static void f_ch_status(typval_T *argvars, typval_T *rettv);
 #endif
 static void f_changenr(typval_T *argvars, typval_T *rettv);
 static void f_char2nr(typval_T *argvars, typval_T *rettv);
@@ -3084,6 +3085,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char
 	    case VAR_FUNC:
 	    case VAR_SPECIAL:
 	    case VAR_JOB:
+	    case VAR_CHANNEL:
 		break;
 
 	    case VAR_LIST:
@@ -3863,6 +3865,7 @@ item_lock(typval_T *tv, int deep, int lo
 	case VAR_FLOAT:
 	case VAR_SPECIAL:
 	case VAR_JOB:
+	case VAR_CHANNEL:
 	    break;
 
 	case VAR_LIST:
@@ -5359,6 +5362,7 @@ eval_index(
 #endif
 	case VAR_SPECIAL:
 	case VAR_JOB:
+	case VAR_CHANNEL:
 	    if (verbose)
 		EMSG(_("E909: Cannot index a special variable"));
 	    return FAIL;
@@ -5471,6 +5475,7 @@ eval_index(
 	    case VAR_FLOAT:
 	    case VAR_SPECIAL:
 	    case VAR_JOB:
+	    case VAR_CHANNEL:
 		break; /* not evaluating, skipping over subscript */
 
 	    case VAR_NUMBER:
@@ -6224,6 +6229,10 @@ tv_equal(
 #ifdef FEAT_JOB
 	    return tv1->vval.v_job == tv2->vval.v_job;
 #endif
+	case VAR_CHANNEL:
+#ifdef FEAT_CHANNEL
+	    return tv1->vval.v_channel == tv2->vval.v_channel;
+#endif
 	case VAR_UNKNOWN:
 	    break;
     }
@@ -7719,12 +7728,26 @@ failret:
     return OK;
 }
 
+#ifdef FEAT_CHANNEL
+    static void
+channel_unref(channel_T *channel)
+{
+    if (channel != NULL && --channel->ch_refcount <= 0)
+	channel_free(channel);
+}
+#endif
+
 #ifdef FEAT_JOB
     static void
 job_free(job_T *job)
 {
-    if (job->jv_channel >= 0)
-	channel_close(job->jv_channel);
+    if (job->jv_channel != NULL)
+    {
+	/* The channel doesn't count as a references for the job, we need to
+	 * NULL the reference when the job is freed. */
+	job->jv_channel->ch_job = NULL;
+	channel_unref(job->jv_channel);
+    }
     mch_clear_job(job);
     vim_free(job);
 }
@@ -7746,9 +7769,7 @@ job_alloc(void)
 
     job = (job_T *)alloc_clear(sizeof(job_T));
     if (job != NULL)
-    {
 	job->jv_refcount = 1;
-    }
     return job;
 }
 
@@ -7850,6 +7871,7 @@ echo_string(
 	case VAR_NUMBER:
 	case VAR_UNKNOWN:
 	case VAR_JOB:
+	case VAR_CHANNEL:
 	    *tofree = NULL;
 	    r = get_tv_string_buf(tv, numbuf);
 	    break;
@@ -7906,6 +7928,7 @@ tv2string(
 	case VAR_DICT:
 	case VAR_SPECIAL:
 	case VAR_JOB:
+	case VAR_CHANNEL:
 	case VAR_UNKNOWN:
 	    break;
     }
@@ -8093,6 +8116,7 @@ static struct fst
     {"ch_readraw",	1, 2, f_ch_readraw},
     {"ch_sendexpr",	2, 3, f_ch_sendexpr},
     {"ch_sendraw",	2, 3, f_ch_sendraw},
+    {"ch_status",	1, 1, f_ch_status},
 #endif
     {"changenr",	0, 0, f_changenr},
     {"char2nr",		1, 2, f_char2nr},
@@ -9781,27 +9805,27 @@ f_ceil(typval_T *argvars, typval_T *rett
 
 #ifdef FEAT_CHANNEL
 /*
- * Get the channel index from the handle argument.
- * Returns -1 if the handle is invalid or the channel is closed.
- */
-    static int
+ * Get the channel from the argument.
+ * Returns NULL if the handle is invalid.
+ */
+    static channel_T *
 get_channel_arg(typval_T *tv)
 {
-    int ch_idx;
-
-    if (tv->v_type != VAR_NUMBER)
+    channel_T *channel;
+
+    if (tv->v_type != VAR_CHANNEL)
     {
 	EMSG2(_(e_invarg2), get_tv_string(tv));
-	return -1;
-    }
-    ch_idx = tv->vval.v_number;
-
-    if (!channel_can_write_to(ch_idx))
-    {
-	EMSGN(_("E906: not an open channel"), ch_idx);
-	return -1;
-    }
-    return ch_idx;
+	return NULL;
+    }
+    channel = tv->vval.v_channel;
+
+    if (channel == NULL || !channel_is_open(channel))
+    {
+	EMSG(_("E906: not an open channel"));
+	return NULL;
+    }
+    return channel;
 }
 
 /*
@@ -9810,10 +9834,10 @@ get_channel_arg(typval_T *tv)
     static void
 f_ch_close(typval_T *argvars, typval_T *rettv UNUSED)
 {
-    int ch_idx = get_channel_arg(&argvars[0]);
-
-    if (ch_idx >= 0)
-	channel_close(ch_idx);
+    channel_T *channel = get_channel_arg(&argvars[0]);
+
+    if (channel != NULL)
+	channel_close(channel);
 }
 
 /*
@@ -9873,10 +9897,11 @@ f_ch_open(typval_T *argvars, typval_T *r
     int		waittime = 0;
     int		timeout = 2000;
     ch_mode_T	ch_mode = MODE_JSON;
-    int		ch_idx;
+    channel_T	*channel;
 
     /* default: fail */
-    rettv->vval.v_number = -1;
+    rettv->v_type = VAR_CHANNEL;
+    rettv->vval.v_channel = NULL;
 
     address = get_tv_string(&argvars[0]);
     if (argvars[1].v_type != VAR_UNKNOWN
@@ -9936,15 +9961,15 @@ f_ch_open(typval_T *argvars, typval_T *r
 	return;
     }
 
-    ch_idx = channel_open((char *)address, port, waittime, NULL);
-    if (ch_idx >= 0)
-    {
-	channel_set_json_mode(ch_idx, ch_mode);
-	channel_set_timeout(ch_idx, timeout);
+    channel = channel_open((char *)address, port, waittime, NULL);
+    if (channel != NULL)
+    {
+	channel_set_json_mode(channel, ch_mode);
+	channel_set_timeout(channel, timeout);
 	if (callback != NULL && *callback != NUL)
-	    channel_set_callback(ch_idx, callback);
-    }
-    rettv->vval.v_number = ch_idx;
+	    channel_set_callback(channel, callback);
+    }
+    rettv->vval.v_channel = channel;
 }
 
 /*
@@ -9953,53 +9978,65 @@ f_ch_open(typval_T *argvars, typval_T *r
     static void
 f_ch_readraw(typval_T *argvars, typval_T *rettv)
 {
-    int		ch_idx;
+    channel_T *channel;
 
     /* return an empty string by default */
     rettv->v_type = VAR_STRING;
     rettv->vval.v_string = NULL;
 
-    ch_idx = get_channel_arg(&argvars[0]);
-    if (ch_idx < 0)
-    {
-	EMSG(_(e_invarg));
-	return;
-    }
-    rettv->vval.v_string = channel_read_block(ch_idx);
+    channel = get_channel_arg(&argvars[0]);
+    if (channel != NULL)
+	rettv->vval.v_string = channel_read_block(channel);
+}
+
+/*
+ * "ch_status()" function
+ */
+    static void
+f_ch_status(typval_T *argvars, typval_T *rettv)
+{
+    /* return an empty string by default */
+    rettv->v_type = VAR_STRING;
+
+    if (argvars[0].v_type != VAR_CHANNEL)
+    {
+	EMSG2(_(e_invarg2), get_tv_string(&argvars[0]));
+	rettv->vval.v_string = NULL;
+    }
+    else
+	rettv->vval.v_string = vim_strsave(
+			 (char_u *)channel_status(argvars[0].vval.v_channel));
 }
 
 /*
  * common for "sendexpr()" and "sendraw()"
- * Returns the channel index if the caller should read the response.
- * Otherwise returns -1.
- */
-    static int
+ * Returns the channel if the caller should read the response.
+ * Otherwise returns NULL.
+ */
+    static channel_T *
 send_common(typval_T *argvars, char_u *text, int id, char *fun)
 {
-    int		ch_idx;
+    channel_T	*channel;
     char_u	*callback = NULL;
 
-    ch_idx = get_channel_arg(&argvars[0]);
-    if (ch_idx < 0)
-    {
-	EMSG(_(e_invarg));
-	return -1;
-    }
+    channel = get_channel_arg(&argvars[0]);
+    if (channel == NULL)
+	return NULL;
 
     if (argvars[2].v_type != VAR_UNKNOWN)
     {
 	callback = get_callback(&argvars[2]);
 	if (callback == NULL)
-	    return -1;
+	    return NULL;
     }
     /* Set the callback. An empty callback means no callback and not reading
      * the response. */
     if (callback != NULL && *callback != NUL)
-	channel_set_req_callback(ch_idx, callback, id);
-
-    if (channel_send(ch_idx, text, fun) == OK && callback == NULL)
-	return ch_idx;
-    return -1;
+	channel_set_req_callback(channel, callback, id);
+
+    if (channel_send(channel, text, fun) == OK && callback == NULL)
+	return channel;
+    return NULL;
 }
 
 /*
@@ -10010,7 +10047,7 @@ f_ch_sendexpr(typval_T *argvars, typval_
 {
     char_u	*text;
     typval_T	*listtv;
-    int		ch_idx;
+    channel_T	*channel;
     int		id;
     ch_mode_T	ch_mode;
 
@@ -10018,14 +10055,11 @@ f_ch_sendexpr(typval_T *argvars, typval_
     rettv->v_type = VAR_STRING;
     rettv->vval.v_string = NULL;
 
-    ch_idx = get_channel_arg(&argvars[0]);
-    if (ch_idx < 0)
-    {
-	EMSG(_(e_invarg));
-	return;
-    }
-
-    ch_mode = channel_get_mode(ch_idx);
+    channel = get_channel_arg(&argvars[0]);
+    if (channel == NULL)
+	return;
+
+    ch_mode = channel_get_mode(channel);
     if (ch_mode == MODE_RAW)
     {
 	EMSG(_("E912: cannot use ch_sendexpr() with a raw channel"));
@@ -10038,11 +10072,11 @@ f_ch_sendexpr(typval_T *argvars, typval_
     if (text == NULL)
 	return;
 
-    ch_idx = send_common(argvars, text, id, "sendexpr");
+    channel = send_common(argvars, text, id, "sendexpr");
     vim_free(text);
-    if (ch_idx >= 0)
-    {
-	if (channel_read_json_block(ch_idx, id, &listtv) == OK)
+    if (channel != NULL)
+    {
+	if (channel_read_json_block(channel, id, &listtv) == OK)
 	{
 	    list_T *list = listtv->vval.v_list;
 
@@ -10050,7 +10084,7 @@ f_ch_sendexpr(typval_T *argvars, typval_
 	     * avoid the value being freed. */
 	    *rettv = list->lv_last->li_tv;
 	    list->lv_last->li_tv.v_type = VAR_NUMBER;
-	    clear_tv(listtv);
+	    free_tv(listtv);
 	}
     }
 }
@@ -10063,16 +10097,16 @@ f_ch_sendraw(typval_T *argvars, typval_T
 {
     char_u	buf[NUMBUFLEN];
     char_u	*text;
-    int		ch_idx;
+    channel_T	*channel;
 
     /* return an empty string by default */
     rettv->v_type = VAR_STRING;
     rettv->vval.v_string = NULL;
 
     text = get_tv_string_buf(&argvars[1], buf);
-    ch_idx = send_common(argvars, text, 0, "sendraw");
-    if (ch_idx >= 0)
-	rettv->vval.v_string = channel_read_block(ch_idx);
+    channel = send_common(argvars, text, 0, "sendraw");
+    if (channel != NULL)
+	rettv->vval.v_string = channel_read_block(channel);
 }
 #endif
 
@@ -10708,7 +10742,14 @@ f_empty(typval_T *argvars, typval_T *ret
 
 	case VAR_JOB:
 #ifdef FEAT_JOB
-	    n = argvars[0].vval.v_job->jv_status != JOB_STARTED;
+	    n = argvars[0].vval.v_job == NULL
+			   || argvars[0].vval.v_job->jv_status != JOB_STARTED;
+	    break;
+#endif
+	case VAR_CHANNEL:
+#ifdef FEAT_CMDWIN
+	    n = argvars[0].vval.v_channel == NULL
+			       || !channel_is_open(argvars[0].vval.v_channel);
 	    break;
 #endif
 	case VAR_UNKNOWN:
@@ -14366,8 +14407,10 @@ f_job_getchannel(typval_T *argvars, typv
     {
 	job_T *job = argvars[0].vval.v_job;
 
-	rettv->v_type = VAR_NUMBER;
-	rettv->vval.v_number = job->jv_channel;
+	rettv->v_type = VAR_CHANNEL;
+	rettv->vval.v_channel = job->jv_channel;
+	if (job->jv_channel != NULL)
+	    ++job->jv_channel->ch_refcount;
     }
 }
 
@@ -14659,6 +14702,7 @@ f_len(typval_T *argvars, typval_T *rettv
 	case VAR_FLOAT:
 	case VAR_FUNC:
 	case VAR_JOB:
+	case VAR_CHANNEL:
 	    EMSG(_("E701: Invalid type for len()"));
 	    break;
     }
@@ -20040,7 +20084,8 @@ f_type(typval_T *argvars, typval_T *rett
 	     else
 		 n = 7;
 	     break;
-	case VAR_JOB:    n = 8; break;
+	case VAR_JOB:     n = 8; break;
+	case VAR_CHANNEL: n = 9; break;
 	case VAR_UNKNOWN:
 	     EMSG2(_(e_intern2), "f_type(UNKNOWN)");
 	     n = -1;
@@ -21412,6 +21457,11 @@ free_tv(typval_T *varp)
 		job_unref(varp->vval.v_job);
 		break;
 #endif
+	    case VAR_CHANNEL:
+#ifdef FEAT_CHANNEL
+		channel_unref(varp->vval.v_channel);
+		break;
+#endif
 	    case VAR_NUMBER:
 	    case VAR_FLOAT:
 	    case VAR_UNKNOWN:
@@ -21462,6 +21512,11 @@ clear_tv(typval_T *varp)
 		varp->vval.v_job = NULL;
 #endif
 		break;
+	    case VAR_CHANNEL:
+#ifdef FEAT_CHANNEL
+		channel_unref(varp->vval.v_channel);
+		varp->vval.v_channel = NULL;
+#endif
 	    case VAR_UNKNOWN:
 		break;
 	}
@@ -21531,6 +21586,11 @@ get_tv_number_chk(typval_T *varp, int *d
 	    EMSG(_("E910: Using a Job as a Number"));
 	    break;
 #endif
+	case VAR_CHANNEL:
+#ifdef FEAT_CHANNEL
+	    EMSG(_("E913: Using a Channel as a Number"));
+	    break;
+#endif
 	case VAR_UNKNOWN:
 	    EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)");
 	    break;
@@ -21572,6 +21632,11 @@ get_tv_float(typval_T *varp)
 	    EMSG(_("E911: Using a Job as a Float"));
 	    break;
 # endif
+	case VAR_CHANNEL:
+# ifdef FEAT_CHANNEL
+	    EMSG(_("E914: Using a Channel as a Float"));
+	    break;
+# endif
 	case VAR_UNKNOWN:
 	    EMSG2(_(e_intern2), "get_tv_float(UNKNOWN)");
 	    break;
@@ -21707,6 +21772,18 @@ get_tv_string_buf_chk(typval_T *varp, ch
 	    }
 #endif
 	    break;
+	case VAR_CHANNEL:
+#ifdef FEAT_CHANNEL
+	    {
+		channel_T *channel = varp->vval.v_channel;
+		char      *status = channel_status(channel);
+
+		vim_snprintf((char *)buf, NUMBUFLEN,
+				     "channel %d %s", channel->ch_id, status);
+		return buf;
+	    }
+#endif
+	    break;
 	case VAR_UNKNOWN:
 	    EMSG(_("E908: using an invalid value as a String"));
 	    break;
@@ -22337,6 +22414,12 @@ copy_tv(typval_T *from, typval_T *to)
 	    ++to->vval.v_job->jv_refcount;
 	    break;
 #endif
+	case VAR_CHANNEL:
+#ifdef FEAT_CHANNEL
+	    to->vval.v_channel = from->vval.v_channel;
+	    ++to->vval.v_channel->ch_refcount;
+	    break;
+#endif
 	case VAR_STRING:
 	case VAR_FUNC:
 	    if (from->vval.v_string == NULL)
@@ -22404,6 +22487,7 @@ item_copy(
 	case VAR_FUNC:
 	case VAR_SPECIAL:
 	case VAR_JOB:
+	case VAR_CHANNEL:
 	    copy_tv(from, to);
 	    break;
 	case VAR_LIST:
@@ -25076,6 +25160,7 @@ write_viminfo_varlist(FILE *fp)
 		    case VAR_UNKNOWN:
 		    case VAR_FUNC:
 		    case VAR_JOB:
+		    case VAR_CHANNEL:
 				     continue;
 		}
 		fprintf(fp, "!%s\t%s\t", this_var->di_key, s);