diff src/eval.c @ 7957:b74549818500 v7.4.1274

commit https://github.com/vim/vim/commit/835dc636a5350f610b62f110227d2363b5b2880a Author: Bram Moolenaar <Bram@vim.org> Date: Sun Feb 7 14:27:38 2016 +0100 patch 7.4.1274 Problem: Cannot run a job. Solution: Add job_start(), job_status() and job_stop(). Currently only works for Unix.
author Christian Brabandt <cb@256bit.org>
date Sun, 07 Feb 2016 14:30:04 +0100
parents a4de8e834b8f
children fc9ba91a6533
line wrap: on
line diff
--- a/src/eval.c
+++ b/src/eval.c
@@ -451,6 +451,9 @@ static dict_T *dict_copy(dict_T *orig, i
 static long dict_len(dict_T *d);
 static char_u *dict2string(typval_T *tv, int copyID);
 static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate);
+#ifdef FEAT_JOB
+static void job_free(job_T *job);
+#endif
 static char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID);
 static char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID);
 static char_u *string_quote(char_u *str, int function);
@@ -619,6 +622,11 @@ static void f_invert(typval_T *argvars, 
 static void f_isdirectory(typval_T *argvars, typval_T *rettv);
 static void f_islocked(typval_T *argvars, typval_T *rettv);
 static void f_items(typval_T *argvars, typval_T *rettv);
+#ifdef FEAT_JOB
+static void f_job_start(typval_T *argvars, typval_T *rettv);
+static void f_job_stop(typval_T *argvars, typval_T *rettv);
+static void f_job_status(typval_T *argvars, typval_T *rettv);
+#endif
 static void f_join(typval_T *argvars, typval_T *rettv);
 static void f_jsondecode(typval_T *argvars, typval_T *rettv);
 static void f_jsonencode(typval_T *argvars, typval_T *rettv);
@@ -3062,10 +3070,11 @@ tv_op(typval_T *tv1, typval_T *tv2, char
     {
 	switch (tv1->v_type)
 	{
+	    case VAR_UNKNOWN:
 	    case VAR_DICT:
 	    case VAR_FUNC:
 	    case VAR_SPECIAL:
-	    case VAR_UNKNOWN:
+	    case VAR_JOB:
 		break;
 
 	    case VAR_LIST:
@@ -3844,6 +3853,7 @@ item_lock(typval_T *tv, int deep, int lo
 	case VAR_FUNC:
 	case VAR_FLOAT:
 	case VAR_SPECIAL:
+	case VAR_JOB:
 	    break;
 
 	case VAR_LIST:
@@ -5339,6 +5349,7 @@ eval_index(
 	    return FAIL;
 #endif
 	case VAR_SPECIAL:
+	case VAR_JOB:
 	    if (verbose)
 		EMSG(_("E909: Cannot index a special variable"));
 	    return FAIL;
@@ -5446,10 +5457,11 @@ eval_index(
 
 	switch (rettv->v_type)
 	{
-	    case VAR_SPECIAL:
+	    case VAR_UNKNOWN:
 	    case VAR_FUNC:
 	    case VAR_FLOAT:
-	    case VAR_UNKNOWN:
+	    case VAR_SPECIAL:
+	    case VAR_JOB:
 		break; /* not evaluating, skipping over subscript */
 
 	    case VAR_NUMBER:
@@ -6167,9 +6179,6 @@ tv_equal(
 
     switch (tv1->v_type)
     {
-	case VAR_UNKNOWN:
-	    break;
-
 	case VAR_LIST:
 	    ++recursive_cnt;
 	    r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, TRUE);
@@ -6190,11 +6199,6 @@ tv_equal(
 	case VAR_NUMBER:
 	    return tv1->vval.v_number == tv2->vval.v_number;
 
-#ifdef FEAT_FLOAT
-	case VAR_FLOAT:
-	    return tv1->vval.v_float == tv2->vval.v_float;
-#endif
-
 	case VAR_STRING:
 	    s1 = get_tv_string_buf(tv1, buf1);
 	    s2 = get_tv_string_buf(tv2, buf2);
@@ -6202,6 +6206,17 @@ tv_equal(
 
 	case VAR_SPECIAL:
 	    return tv1->vval.v_number == tv2->vval.v_number;
+
+	case VAR_FLOAT:
+#ifdef FEAT_FLOAT
+	    return tv1->vval.v_float == tv2->vval.v_float;
+#endif
+	case VAR_JOB:
+#ifdef FEAT_JOB
+	    return tv1->vval.v_job == tv2->vval.v_job;
+#endif
+	case VAR_UNKNOWN:
+	    break;
     }
 
     /* VAR_UNKNOWN can be the result of a invalid expression, let's say it
@@ -6924,7 +6939,7 @@ garbage_collect(void)
 }
 
 /*
- * Free lists and dictionaries that are no longer referenced.
+ * Free lists, dictionaries and jobs that are no longer referenced.
  */
     static int
 free_unref_items(int copyID)
@@ -6969,6 +6984,7 @@ free_unref_items(int copyID)
 	}
 	ll = ll_next;
     }
+
     return did_free;
 }
 
@@ -7694,6 +7710,40 @@ failret:
     return OK;
 }
 
+#ifdef FEAT_JOB
+    static void
+job_free(job_T *job)
+{
+    /* TODO: free any handles */
+
+    vim_free(job);
+}
+
+    static void
+job_unref(job_T *job)
+{
+    if (job != NULL && --job->jv_refcount <= 0)
+	job_free(job);
+}
+
+/*
+ * Allocate a job.  Sets the refcount to one.
+ */
+    static job_T *
+job_alloc(void)
+{
+    job_T *job;
+
+    job = (job_T *)alloc_clear(sizeof(job_T));
+    if (job != NULL)
+    {
+	job->jv_refcount = 1;
+    }
+    return job;
+}
+
+#endif
+
     static char *
 get_var_special_name(int nr)
 {
@@ -7789,12 +7839,13 @@ echo_string(
 	case VAR_STRING:
 	case VAR_NUMBER:
 	case VAR_UNKNOWN:
+	case VAR_JOB:
 	    *tofree = NULL;
 	    r = get_tv_string_buf(tv, numbuf);
 	    break;
 
-#ifdef FEAT_FLOAT
 	case VAR_FLOAT:
+#ifdef FEAT_FLOAT
 	    *tofree = NULL;
 	    vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv->vval.v_float);
 	    r = numbuf;
@@ -7844,6 +7895,7 @@ tv2string(
 	case VAR_LIST:
 	case VAR_DICT:
 	case VAR_SPECIAL:
+	case VAR_JOB:
 	case VAR_UNKNOWN:
 	    break;
     }
@@ -8148,6 +8200,11 @@ static struct fst
     {"isdirectory",	1, 1, f_isdirectory},
     {"islocked",	1, 1, f_islocked},
     {"items",		1, 1, f_items},
+#ifdef FEAT_CHANNEL
+    {"job_start",	1, 2, f_job_start},
+    {"job_status",	1, 1, f_job_status},
+    {"job_stop",	1, 1, f_job_stop},
+#endif
     {"join",		1, 2, f_join},
     {"jsondecode",	1, 1, f_jsondecode},
     {"jsonencode",	1, 1, f_jsonencode},
@@ -10535,8 +10592,8 @@ f_empty(typval_T *argvars, typval_T *ret
 	case VAR_NUMBER:
 	    n = argvars[0].vval.v_number == 0;
 	    break;
-#ifdef FEAT_FLOAT
 	case VAR_FLOAT:
+#ifdef FEAT_FLOAT
 	    n = argvars[0].vval.v_float == 0.0;
 	    break;
 #endif
@@ -10552,6 +10609,11 @@ f_empty(typval_T *argvars, typval_T *ret
 	    n = argvars[0].vval.v_number != VVAL_TRUE;
 	    break;
 
+	case VAR_JOB:
+#ifdef FEAT_JOB
+	    n = argvars[0].vval.v_job->jv_status != JOB_STARTED;
+	    break;
+#endif
 	case VAR_UNKNOWN:
 	    EMSG2(_(e_intern2), "f_empty(UNKNOWN)");
 	    n = TRUE;
@@ -13060,6 +13122,9 @@ f_has(typval_T *argvars, typval_T *rettv
 #ifdef FEAT_INS_EXPAND
 	"insert_expand",
 #endif
+#ifdef FEAT_JOB
+	"job",
+#endif
 #ifdef FEAT_JUMPLIST
 	"jumplist",
 #endif
@@ -14188,6 +14253,161 @@ f_items(typval_T *argvars, typval_T *ret
     dict_list(argvars, rettv, 2);
 }
 
+#ifdef FEAT_JOB
+/*
+ * "job_start()" function
+ */
+    static void
+f_job_start(typval_T *argvars UNUSED, 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
+
+    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;
+#ifndef USE_ARGV
+    ga_init2(&ga, 200);
+#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
+	    if (li != l->lv_first)
+	    {
+		s = vim_strsave_shellescape(s, FALSE, TRUE);
+		if (s == NULL)
+		    goto theend;
+	    }
+	    ga_concat(&ga, s);
+	    vim_free(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
+    mch_start_job(argv, job);
+#else
+    mch_start_job(cmd, job);
+#endif
+
+theend:
+#ifdef USE_ARGV
+    if (argv != NULL)
+	vim_free(argv);
+#else
+    vim_free(ga.ga_data);
+#endif
+}
+
+/*
+ * "job_status()" function
+ */
+    static void
+f_job_status(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
+{
+    char *result;
+
+    if (argvars[0].v_type != VAR_JOB)
+	EMSG(_(e_invarg));
+    else
+    {
+	job_T *job = argvars[0].vval.v_job;
+
+	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);
+	rettv->v_type = VAR_STRING;
+	rettv->vval.v_string = vim_strsave((char_u *)result);
+    }
+}
+
+/*
+ * "job_stop()" function
+ */
+    static void
+f_job_stop(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
+{
+    if (argvars[0].v_type != VAR_JOB)
+	EMSG(_(e_invarg));
+    else
+    {
+	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;
+	    }
+	}
+	if (mch_stop_job(argvars[0].vval.v_job, arg) == FAIL)
+	    rettv->vval.v_number = 0;
+	else
+	    rettv->vval.v_number = 1;
+    }
+}
+#endif
+
 /*
  * "join()" function
  */
@@ -14295,6 +14515,7 @@ f_len(typval_T *argvars, typval_T *rettv
 	case VAR_SPECIAL:
 	case VAR_FLOAT:
 	case VAR_FUNC:
+	case VAR_JOB:
 	    EMSG(_("E701: Invalid type for len()"));
 	    break;
     }
@@ -19658,6 +19879,9 @@ f_type(typval_T *argvars, typval_T *rett
 	     else
 		 n = 7;
 	     break;
+#ifdef FEAT_JOB
+	case VAR_JOB:    n = 8; break;
+#endif
 	case VAR_UNKNOWN:
 	     EMSG2(_(e_intern2), "f_type(UNKNOWN)");
 	     n = -1;
@@ -21024,10 +21248,13 @@ free_tv(typval_T *varp)
 	    case VAR_DICT:
 		dict_unref(varp->vval.v_dict);
 		break;
+	    case VAR_JOB:
+#ifdef FEAT_JOB
+		job_unref(varp->vval.v_job);
+		break;
+#endif
 	    case VAR_NUMBER:
-#ifdef FEAT_FLOAT
 	    case VAR_FLOAT:
-#endif
 	    case VAR_UNKNOWN:
 	    case VAR_SPECIAL:
 		break;
@@ -21065,11 +21292,17 @@ clear_tv(typval_T *varp)
 	    case VAR_SPECIAL:
 		varp->vval.v_number = 0;
 		break;
-#ifdef FEAT_FLOAT
 	    case VAR_FLOAT:
+#ifdef FEAT_FLOAT
 		varp->vval.v_float = 0.0;
 		break;
 #endif
+	    case VAR_JOB:
+#ifdef FEAT_JOB
+		job_unref(varp->vval.v_job);
+		varp->vval.v_job = NULL;
+#endif
+		break;
 	    case VAR_UNKNOWN:
 		break;
 	}
@@ -21112,8 +21345,8 @@ get_tv_number_chk(typval_T *varp, int *d
     {
 	case VAR_NUMBER:
 	    return (long)(varp->vval.v_number);
-#ifdef FEAT_FLOAT
 	case VAR_FLOAT:
+#ifdef FEAT_FLOAT
 	    EMSG(_("E805: Using a Float as a Number"));
 	    break;
 #endif
@@ -21134,6 +21367,11 @@ get_tv_number_chk(typval_T *varp, int *d
 	case VAR_SPECIAL:
 	    return varp->vval.v_number == VVAL_TRUE ? 1 : 0;
 	    break;
+	case VAR_JOB:
+#ifdef FEAT_JOB
+	    EMSG(_("E910: Using a Job as a Number"));
+	    break;
+#endif
 	case VAR_UNKNOWN:
 	    EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)");
 	    break;
@@ -21153,10 +21391,8 @@ get_tv_float(typval_T *varp)
     {
 	case VAR_NUMBER:
 	    return (float_T)(varp->vval.v_number);
-#ifdef FEAT_FLOAT
 	case VAR_FLOAT:
 	    return varp->vval.v_float;
-#endif
 	case VAR_FUNC:
 	    EMSG(_("E891: Using a Funcref as a Float"));
 	    break;
@@ -21172,6 +21408,11 @@ get_tv_float(typval_T *varp)
 	case VAR_SPECIAL:
 	    EMSG(_("E907: Using a special value as a Float"));
 	    break;
+	case VAR_JOB:
+# ifdef FEAT_JOB
+	    EMSG(_("E911: Using a Job as a Float"));
+	    break;
+# endif
 	case VAR_UNKNOWN:
 	    EMSG2(_(e_intern2), "get_tv_float(UNKNOWN)");
 	    break;
@@ -21272,8 +21513,8 @@ get_tv_string_buf_chk(typval_T *varp, ch
 	case VAR_DICT:
 	    EMSG(_("E731: using Dictionary as a String"));
 	    break;
-#ifdef FEAT_FLOAT
 	case VAR_FLOAT:
+#ifdef FEAT_FLOAT
 	    EMSG(_(e_float_as_string));
 	    break;
 #endif
@@ -21284,6 +21525,24 @@ get_tv_string_buf_chk(typval_T *varp, ch
 	case VAR_SPECIAL:
 	    STRCPY(buf, get_var_special_name(varp->vval.v_number));
 	    return buf;
+	case VAR_JOB:
+#ifdef FEAT_JOB
+	    {
+		job_T *job = varp->vval.v_job;
+		char  *status = job->jv_status == JOB_FAILED ? "fail"
+				: job->jv_status == JOB_ENDED ? "dead"
+				: "run";
+# ifdef UNIX
+		vim_snprintf((char *)buf, NUMBUFLEN,
+			    "process %ld %s", (long)job->jv_pid, status);
+# else
+		/* TODO */
+		vim_snprintf((char *)buf, NUMBUFLEN, "process ? %s", status);
+# endif
+		return buf;
+	    }
+#endif
+	    break;
 	case VAR_UNKNOWN:
 	    EMSG(_("E908: using an invalid value as a String"));
 	    break;
@@ -21903,11 +22162,17 @@ copy_tv(typval_T *from, typval_T *to)
 	case VAR_SPECIAL:
 	    to->vval.v_number = from->vval.v_number;
 	    break;
-#ifdef FEAT_FLOAT
 	case VAR_FLOAT:
+#ifdef FEAT_FLOAT
 	    to->vval.v_float = from->vval.v_float;
 	    break;
 #endif
+	case VAR_JOB:
+#ifdef FEAT_FLOAT
+	    to->vval.v_job = from->vval.v_job;
+	    ++to->vval.v_job->jv_refcount;
+	    break;
+#endif
 	case VAR_STRING:
 	case VAR_FUNC:
 	    if (from->vval.v_string == NULL)
@@ -21970,12 +22235,11 @@ item_copy(
     switch (from->v_type)
     {
 	case VAR_NUMBER:
-#ifdef FEAT_FLOAT
 	case VAR_FLOAT:
-#endif
 	case VAR_STRING:
 	case VAR_FUNC:
 	case VAR_SPECIAL:
+	case VAR_JOB:
 	    copy_tv(from, to);
 	    break;
 	case VAR_LIST:
@@ -24649,6 +24913,7 @@ write_viminfo_varlist(FILE *fp)
 
 		    case VAR_UNKNOWN:
 		    case VAR_FUNC:
+		    case VAR_JOB:
 				     continue;
 		}
 		fprintf(fp, "!%s\t%s\t", this_var->di_key, s);