Patch 7.4.1274
Problem:    Cannot run a job.
Solution:   Add job_start(), job_status() and job_stop(). Currently only works
            for Unix.
Files:      eval.c, structs.h, runtime/doc/eval.txt, src/os_unix.c,
            src/proto/os_unix.pro, src/feature.h, src/version.c,
            src/testdir/test_channel.vim


*** ../vim-7.4.1274/src/eval.c  2016-02-07 00:00:30.576064995 +0100
--- src/eval.c  2016-02-07 13:59:00.784058601 +0100
***************
*** 451,456 ****
--- 451,459 ----
  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,624 ****
--- 622,632 ----
  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,3071 ****
      {
        switch (tv1->v_type)
        {
            case VAR_DICT:
            case VAR_FUNC:
            case VAR_SPECIAL:
!           case VAR_UNKNOWN:
                break;
  
            case VAR_LIST:
--- 3070,3080 ----
      {
        switch (tv1->v_type)
        {
+           case VAR_UNKNOWN:
            case VAR_DICT:
            case VAR_FUNC:
            case VAR_SPECIAL:
!           case VAR_JOB:
                break;
  
            case VAR_LIST:
***************
*** 3844,3849 ****
--- 3853,3859 ----
        case VAR_FUNC:
        case VAR_FLOAT:
        case VAR_SPECIAL:
+       case VAR_JOB:
            break;
  
        case VAR_LIST:
***************
*** 5339,5344 ****
--- 5349,5355 ----
            return FAIL;
  #endif
        case VAR_SPECIAL:
+       case VAR_JOB:
            if (verbose)
                EMSG(_("E909: Cannot index a special variable"));
            return FAIL;
***************
*** 5446,5455 ****
  
        switch (rettv->v_type)
        {
!           case VAR_SPECIAL:
            case VAR_FUNC:
            case VAR_FLOAT:
!           case VAR_UNKNOWN:
                break; /* not evaluating, skipping over subscript */
  
            case VAR_NUMBER:
--- 5457,5467 ----
  
        switch (rettv->v_type)
        {
!           case VAR_UNKNOWN:
            case VAR_FUNC:
            case VAR_FLOAT:
!           case VAR_SPECIAL:
!           case VAR_JOB:
                break; /* not evaluating, skipping over subscript */
  
            case VAR_NUMBER:
***************
*** 6167,6175 ****
  
      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);
--- 6179,6184 ----
***************
*** 6190,6200 ****
        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);
--- 6199,6204 ----
***************
*** 6202,6207 ****
--- 6206,6222 ----
  
        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,6930 ****
  }
  
  /*
!  * Free lists and dictionaries that are no longer referenced.
   */
      static int
  free_unref_items(int copyID)
--- 6939,6945 ----
  }
  
  /*
!  * Free lists, dictionaries and jobs that are no longer referenced.
   */
      static int
  free_unref_items(int copyID)
***************
*** 6969,6974 ****
--- 6984,6990 ----
        }
        ll = ll_next;
      }
+ 
      return did_free;
  }
  
***************
*** 7694,7699 ****
--- 7710,7749 ----
      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,7800 ****
        case VAR_STRING:
        case VAR_NUMBER:
        case VAR_UNKNOWN:
            *tofree = NULL;
            r = get_tv_string_buf(tv, numbuf);
            break;
  
- #ifdef FEAT_FLOAT
        case VAR_FLOAT:
            *tofree = NULL;
            vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv->vval.v_float);
            r = numbuf;
--- 7839,7851 ----
        case VAR_STRING:
        case VAR_NUMBER:
        case VAR_UNKNOWN:
+       case VAR_JOB:
            *tofree = NULL;
            r = get_tv_string_buf(tv, numbuf);
            break;
  
        case VAR_FLOAT:
+ #ifdef FEAT_FLOAT
            *tofree = NULL;
            vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv->vval.v_float);
            r = numbuf;
***************
*** 7844,7849 ****
--- 7895,7901 ----
        case VAR_LIST:
        case VAR_DICT:
        case VAR_SPECIAL:
+       case VAR_JOB:
        case VAR_UNKNOWN:
            break;
      }
***************
*** 8148,8153 ****
--- 8200,8210 ----
      {"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,10542 ****
        case VAR_NUMBER:
            n = argvars[0].vval.v_number == 0;
            break;
- #ifdef FEAT_FLOAT
        case VAR_FLOAT:
            n = argvars[0].vval.v_float == 0.0;
            break;
  #endif
--- 10592,10599 ----
        case VAR_NUMBER:
            n = argvars[0].vval.v_number == 0;
            break;
        case VAR_FLOAT:
+ #ifdef FEAT_FLOAT
            n = argvars[0].vval.v_float == 0.0;
            break;
  #endif
***************
*** 10552,10557 ****
--- 10609,10619 ----
            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,13065 ****
--- 13122,13130 ----
  #ifdef FEAT_INS_EXPAND
        "insert_expand",
  #endif
+ #ifdef FEAT_JOB
+       "job",
+ #endif
  #ifdef FEAT_JUMPLIST
        "jumplist",
  #endif
***************
*** 14188,14193 ****
--- 14253,14413 ----
      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,14300 ****
--- 14515,14521 ----
        case VAR_SPECIAL:
        case VAR_FLOAT:
        case VAR_FUNC:
+       case VAR_JOB:
            EMSG(_("E701: Invalid type for len()"));
            break;
      }
***************
*** 19658,19663 ****
--- 19879,19887 ----
             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,21033 ****
            case VAR_DICT:
                dict_unref(varp->vval.v_dict);
                break;
            case VAR_NUMBER:
- #ifdef FEAT_FLOAT
            case VAR_FLOAT:
- #endif
            case VAR_UNKNOWN:
            case VAR_SPECIAL:
                break;
--- 21248,21260 ----
            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:
            case VAR_FLOAT:
            case VAR_UNKNOWN:
            case VAR_SPECIAL:
                break;
***************
*** 21065,21075 ****
            case VAR_SPECIAL:
                varp->vval.v_number = 0;
                break;
- #ifdef FEAT_FLOAT
            case VAR_FLOAT:
                varp->vval.v_float = 0.0;
                break;
  #endif
            case VAR_UNKNOWN:
                break;
        }
--- 21292,21308 ----
            case VAR_SPECIAL:
                varp->vval.v_number = 0;
                break;
            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,21119 ****
      {
        case VAR_NUMBER:
            return (long)(varp->vval.v_number);
- #ifdef FEAT_FLOAT
        case VAR_FLOAT:
            EMSG(_("E805: Using a Float as a Number"));
            break;
  #endif
--- 21345,21352 ----
      {
        case VAR_NUMBER:
            return (long)(varp->vval.v_number);
        case VAR_FLOAT:
+ #ifdef FEAT_FLOAT
            EMSG(_("E805: Using a Float as a Number"));
            break;
  #endif
***************
*** 21134,21139 ****
--- 21367,21377 ----
        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,21162 ****
      {
        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;
--- 21391,21398 ----
***************
*** 21172,21177 ****
--- 21408,21418 ----
        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,21279 ****
        case VAR_DICT:
            EMSG(_("E731: using Dictionary as a String"));
            break;
- #ifdef FEAT_FLOAT
        case VAR_FLOAT:
            EMSG(_(e_float_as_string));
            break;
  #endif
--- 21513,21520 ----
        case VAR_DICT:
            EMSG(_("E731: using Dictionary as a String"));
            break;
        case VAR_FLOAT:
+ #ifdef FEAT_FLOAT
            EMSG(_(e_float_as_string));
            break;
  #endif
***************
*** 21284,21289 ****
--- 21525,21548 ----
        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,21913 ****
        case VAR_SPECIAL:
            to->vval.v_number = from->vval.v_number;
            break;
- #ifdef FEAT_FLOAT
        case VAR_FLOAT:
            to->vval.v_float = from->vval.v_float;
            break;
  #endif
        case VAR_STRING:
        case VAR_FUNC:
            if (from->vval.v_string == NULL)
--- 22162,22178 ----
        case VAR_SPECIAL:
            to->vval.v_number = from->vval.v_number;
            break;
        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,21981 ****
      switch (from->v_type)
      {
        case VAR_NUMBER:
- #ifdef FEAT_FLOAT
        case VAR_FLOAT:
- #endif
        case VAR_STRING:
        case VAR_FUNC:
        case VAR_SPECIAL:
            copy_tv(from, to);
            break;
        case VAR_LIST:
--- 22235,22245 ----
      switch (from->v_type)
      {
        case VAR_NUMBER:
        case VAR_FLOAT:
        case VAR_STRING:
        case VAR_FUNC:
        case VAR_SPECIAL:
+       case VAR_JOB:
            copy_tv(from, to);
            break;
        case VAR_LIST:
***************
*** 24649,24654 ****
--- 24913,24919 ----
  
                    case VAR_UNKNOWN:
                    case VAR_FUNC:
+                   case VAR_JOB:
                                     continue;
                }
                fprintf(fp, "!%s\t%s\t", this_var->di_key, s);
*** ../vim-7.4.1274/src/structs.h       2016-02-06 18:09:53.067952958 +0100
--- src/structs.h       2016-02-07 13:56:50.617415037 +0100
***************
*** 1110,1126 ****
  
  typedef struct listvar_S list_T;
  typedef struct dictvar_S dict_T;
  
  typedef enum
  {
      VAR_UNKNOWN = 0,
!     VAR_NUMBER,       /* "v_number" is used */
!     VAR_STRING,       /* "v_string" is used */
!     VAR_FUNC, /* "v_string" is function name */
!     VAR_LIST, /* "v_list" is used */
!     VAR_DICT, /* "v_dict" is used */
!     VAR_FLOAT,        /* "v_float" is used */
!     VAR_SPECIAL       /* "v_number" is used */
  } vartype_T;
  
  /*
--- 1110,1128 ----
  
  typedef struct listvar_S list_T;
  typedef struct dictvar_S dict_T;
+ typedef struct jobvar_S job_T;
  
  typedef enum
  {
      VAR_UNKNOWN = 0,
!     VAR_NUMBER,        /* "v_number" is used */
!     VAR_STRING,        /* "v_string" is used */
!     VAR_FUNC,  /* "v_string" is function name */
!     VAR_LIST,  /* "v_list" is used */
!     VAR_DICT,  /* "v_dict" is used */
!     VAR_FLOAT,         /* "v_float" is used */
!     VAR_SPECIAL, /* "v_number" is used */
!     VAR_JOB    /* "v_job" is used */
  } vartype_T;
  
  /*
***************
*** 1139,1144 ****
--- 1141,1149 ----
        char_u          *v_string;      /* string value (can be NULL!) */
        list_T          *v_list;        /* list value (can be NULL!) */
        dict_T          *v_dict;        /* dict value (can be NULL!) */
+ #ifdef FEAT_JOB
+       job_T           *v_job;         /* job value (can be NULL!) */
+ #endif
      }         vval;
  } typval_T;
  
***************
*** 1204,1210 ****
      char_u    di_flags;       /* flags (only used for variable) */
      char_u    di_key[1];      /* key (actually longer!) */
  };
- 
  typedef struct dictitem_S dictitem_T;
  
  #define DI_FLAGS_RO   1  /* "di_flags" value: read-only variable */
--- 1209,1214 ----
***************
*** 1228,1233 ****
--- 1232,1261 ----
      dict_T    *dv_used_prev;  /* previous dict in used dicts list */
  };
  
+ typedef enum
+ {
+     JOB_FAILED,
+     JOB_STARTED,
+     JOB_ENDED
+ } jobstatus_T;
+ 
+ /*
+  * Structure to hold info about a Job.
+  */
+ struct jobvar_S
+ {
+ #ifdef UNIX
+     pid_t     jv_pid;
+     int               jv_exitval;
+ #endif
+ #ifdef WIN32
+     PROCESS_INFORMATION       jf_pi;
+ #endif
+     jobstatus_T       jv_status;
+ 
+     int               jv_refcount;    /* reference count */
+ };
+ 
  /* structure used for explicit stack while garbage collecting hash tables */
  typedef struct ht_stack_S
  {
*** ../vim-7.4.1274/runtime/doc/eval.txt        2016-02-07 14:23:21.744830821 
+0100
--- runtime/doc/eval.txt        2016-02-07 14:10:33.428839814 +0100
***************
*** 59,64 ****
--- 56,68 ----
                value. |Dictionary|
                Example: {'blue': "#0000ff", 'red': "#ff0000"}
  
+ Funcref               A reference to a function |Funcref|.
+               Example: function("strlen")
+ 
+ Special               v:false, v:true, v:none and v:null
+ 
+ Job           Used for job control, see |job_start()|.
+ 
  The Number and String types are converted automatically, depending on how they
  are used.
  
***************
*** 1922,1927 ****
--- 1952,1960 ----
  isdirectory( {directory})     Number  TRUE if {directory} is a directory
  islocked( {expr})             Number  TRUE if {expr} is locked
  items( {dict})                        List    key-value pairs in {dict}
+ job_start({command} [, {options}]) Job        start a job     
+ job_status({job})             String  get the status of a job
+ job_stop({job} [, {how}])     Number  stop a job
  join( {list} [, {sep}])               String  join {list} items into one 
String
  jsondecode( {string})         any     decode JSON
  jsonencode( {expr})           String  encode JSON
***************
*** 4200,4205 ****
--- 4303,4375 ----
                order.
  
  
+ job_start({command} [, {options}])                            *job_start()*
+               Start a job and return a Job object.  Unlike |system()| and
+               |:!cmd| this does not wait for the job to finish.
+ 
+               {command} can be a string.  This works best on MS-Windows.  On
+               Unix it is split up in white-separated parts to be passed to
+               execvp().  Arguments in double quotes can contain white space.
+ 
+               {command} can be a list, where the first item is the executable
+               and further items are the arguments.  All items are converted
+               to String.  This works best on Unix.
+ 
+               The command is executed directly, not through a shell, the
+               'shell' option is not used.  To use the shell: >
+       let job = job_start(["/bin/sh", "-c", "echo hello"])
+ <             Or: >
+       let job = job_start('/bin/sh -c "echo hello"')
+ <             However, the status of the job will now be the status of the
+               shell, and stopping the job means stopping the shell and the
+               command may continue to run.
+ 
+               On Unix $PATH is used to search for the executable only when
+               the command does not contain a slash.
+ 
+               The job will use the same terminal as Vim.  If it reads from
+               stdin the job and Vim will be fighting over input, that
+               doesn't work.  Redirect stdin and stdout to avoid problems: >
+       let job = job_start(['sh', '-c', "myserver </dev/null >/dev/null"])
+ <
+               The returned Job object can be used to get the status with
+               |job_status()| and stop the job with |job_stop()|.
+ 
+               {options} must be a Dictionary.  It can contain these optional
+               items:
+                       killonexit      When non-zero kill the job when Vim
+                                       exits. (default: 0, don't kill)
+ 
+               {only available when compiled with the |+channel| feature}
+ 
+ job_status({job})                                             *job_status()*
+               Returns a String with the status of {job}:
+                       "run"   job is running
+                       "fail"  job failed to start
+                       "dead"  job died or was stopped after running
+ 
+               {only available when compiled with the |+channel| feature}
+ 
+ job_stop({job} [, {how}])                                     *job_stop()*
+               Stop the {job}.  This can also be used to signal the job.
+ 
+               When {how} is omitted or is "term" the job will be terminated
+               normally.  For Unix SIGTERM is sent.
+               Other values:
+                       "hup"   Unix: SIGHUP
+                       "quit"  Unix: SIGQUIT
+                       "kill"  Unix: SIGKILL (strongest way to stop)
+                       number  Unix: signal with that number
+ 
+               The result is a Number: 1 if the operation could be executed,
+               0 if "how" is not supported on the system.
+               Note that even when the operation was executed, whether the
+               job was actually stopped needs to be checked with
+               job_status().
+               The operation will even be done when the job wasn't running.
+ 
+               {only available when compiled with the |+channel| feature}
+ 
  join({list} [, {sep}])                                        *join()*
                Join the items in {list} together into one String.
                When {sep} is specified it is put in between the items.  If
*** ../vim-7.4.1274/src/os_unix.c       2016-02-07 14:23:21.744830821 +0100
--- src/os_unix.c       2016-02-07 13:46:32.215854387 +0100
***************
*** 3919,3924 ****
--- 3919,3984 ----
      return wait_pid;
  }
  
+ #if defined(FEAT_JOB) || !defined(USE_SYSTEM) || defined(PROTO)
+     int
+ mch_parse_cmd(char_u *cmd, int use_shcf, char ***argv, int *argc)
+ {
+     int               i;
+     char_u    *p;
+     int               inquote;
+ 
+     /*
+      * Do this loop twice:
+      * 1: find number of arguments
+      * 2: separate them and build argv[]
+      */
+     for (i = 0; i < 2; ++i)
+     {
+       p = cmd;
+       inquote = FALSE;
+       *argc = 0;
+       for (;;)
+       {
+           if (i == 1)
+               (*argv)[*argc] = (char *)p;
+           ++*argc;
+           while (*p != NUL && (inquote || (*p != ' ' && *p != TAB)))
+           {
+               if (*p == '"')
+                   inquote = !inquote;
+               ++p;
+           }
+           if (*p == NUL)
+               break;
+           if (i == 1)
+               *p++ = NUL;
+           p = skipwhite(p);
+       }
+       if (*argv == NULL)
+       {
+           if (use_shcf)
+           {
+               /* Account for possible multiple args in p_shcf. */
+               p = p_shcf;
+               for (;;)
+               {
+                   p = skiptowhite(p);
+                   if (*p == NUL)
+                       break;
+                   ++*argc;
+                   p = skipwhite(p);
+               }
+           }
+ 
+           *argv = (char **)alloc((unsigned)((*argc + 4) * sizeof(char *)));
+           if (*argv == NULL)      /* out of memory */
+               return FAIL;
+       }
+     }
+     return OK;
+ }
+ #endif
+ 
      int
  mch_call_shell(
      char_u    *cmd,
***************
*** 4046,4052 ****
  # define EXEC_FAILED 122    /* Exit code when shell didn't execute.  Don't use
                               127, some shells use that already */
  
!     char_u    *newcmd = NULL;
      pid_t     pid;
      pid_t     wpid = 0;
      pid_t     wait_pid = 0;
--- 4106,4112 ----
  # define EXEC_FAILED 122    /* Exit code when shell didn't execute.  Don't use
                               127, some shells use that already */
  
!     char_u    *newcmd;
      pid_t     pid;
      pid_t     wpid = 0;
      pid_t     wait_pid = 0;
***************
*** 4061,4067 ****
      char_u    *p_shcf_copy = NULL;
      int               i;
      char_u    *p;
-     int               inquote;
      int               pty_master_fd = -1;         /* for pty's */
  # ifdef FEAT_GUI
      int               pty_slave_fd = -1;
--- 4121,4126 ----
***************
*** 4086,4138 ****
      if (options & SHELL_COOKED)
        settmode(TMODE_COOK);           /* set to normal mode */
  
!     /*
!      * Do this loop twice:
!      * 1: find number of arguments
!      * 2: separate them and build argv[]
!      */
!     for (i = 0; i < 2; ++i)
!     {
!       p = newcmd;
!       inquote = FALSE;
!       argc = 0;
!       for (;;)
!       {
!           if (i == 1)
!               argv[argc] = (char *)p;
!           ++argc;
!           while (*p && (inquote || (*p != ' ' && *p != TAB)))
!           {
!               if (*p == '"')
!                   inquote = !inquote;
!               ++p;
!           }
!           if (*p == NUL)
!               break;
!           if (i == 1)
!               *p++ = NUL;
!           p = skipwhite(p);
!       }
!       if (argv == NULL)
!       {
!           /*
!            * Account for possible multiple args in p_shcf.
!            */
!           p = p_shcf;
!           for (;;)
!           {
!               p = skiptowhite(p);
!               if (*p == NUL)
!                   break;
!               ++argc;
!               p = skipwhite(p);
!           }
  
-           argv = (char **)alloc((unsigned)((argc + 4) * sizeof(char *)));
-           if (argv == NULL)       /* out of memory */
-               goto error;
-       }
-     }
      if (cmd != NULL)
      {
        char_u  *s;
--- 4145,4153 ----
      if (options & SHELL_COOKED)
        settmode(TMODE_COOK);           /* set to normal mode */
  
!     if (mch_parse_cmd(newcmd, TRUE, &argv, &argc) == FAIL)
!       goto error;
  
      if (cmd != NULL)
      {
        char_u  *s;
***************
*** 5006,5011 ****
--- 5021,5117 ----
  #endif /* USE_SYSTEM */
  }
  
+ #if defined(FEAT_JOB) || defined(PROTO)
+     void
+ mch_start_job(char **argv, job_T *job)
+ {
+     pid_t pid = fork();
+ 
+     if (pid  == -1)   /* maybe we should use vfork() */
+     {
+       job->jv_status = JOB_FAILED;
+     }
+     else if (pid == 0)
+     {
+       /* child */
+       reset_signals();                /* handle signals normally */
+ 
+ # ifdef HAVE_SETSID
+       /* Create our own process group, so that the child and all its
+        * children can be kill()ed.  Don't do this when using pipes,
+        * because stdin is not a tty, we would lose /dev/tty. */
+       (void)setsid();
+ # endif
+ 
+       /* See above for type of argv. */
+       execvp(argv[0], argv);
+ 
+       perror("executing job failed");
+       _exit(EXEC_FAILED);         /* exec failed, return failure code */
+     }
+     else
+     {
+       /* parent */
+       job->jv_pid = pid;
+       job->jv_status = JOB_STARTED;
+     }
+ }
+ 
+     char *
+ mch_job_status(job_T *job)
+ {
+ # ifdef HAVE_UNION_WAIT
+     union wait        status;
+ # else
+     int               status = -1;
+ # endif
+     pid_t     wait_pid = 0;
+ 
+ # ifdef __NeXT__
+     wait_pid = wait4(job->jv_pid, &status, WNOHANG, (struct rusage *)0);
+ # else
+     wait_pid = waitpid(job->jv_pid, &status, WNOHANG);
+ # endif
+     if (wait_pid == -1)
+     {
+       /* process must have exited */
+       job->jv_status = JOB_ENDED;
+       return "dead";
+     }
+     if (wait_pid == 0)
+       return "run";
+     if (WIFEXITED(status))
+     {
+       /* LINTED avoid "bitwise operation on signed value" */
+       job->jv_exitval = WEXITSTATUS(status);
+       job->jv_status = JOB_ENDED;
+       return "dead";
+     }
+     return "run";
+ }
+ 
+     int
+ mch_stop_job(job_T *job, char_u *how)
+ {
+     int sig = -1;
+ 
+     if (STRCMP(how, "hup") == 0)
+       sig = SIGHUP;
+     else if (*how == NUL || STRCMP(how, "term") == 0)
+       sig = SIGTERM;
+     else if (STRCMP(how, "quit") == 0)
+       sig = SIGQUIT;
+     else if (STRCMP(how, "kill") == 0)
+       sig = SIGKILL;
+     else if (isdigit(*how))
+       sig = atoi((char *)how);
+     else
+       return FAIL;
+     kill(job->jv_pid, sig);
+     return OK;
+ }
+ #endif
+ 
  /*
   * Check for CTRL-C typed by reading all available characters.
   * In cooked mode we should get SIGINT, no need to check.
*** ../vim-7.4.1274/src/proto/os_unix.pro       2016-02-07 14:23:21.744830821 
+0100
--- src/proto/os_unix.pro       2016-02-07 13:42:04.306643148 +0100
***************
*** 55,61 ****
--- 55,65 ----
  int mch_get_shellsize(void);
  void mch_set_shellsize(void);
  void mch_new_shellsize(void);
+ int mch_parse_cmd(char_u *cmd, int use_shcf, char ***argv, int *argc);
  int mch_call_shell(char_u *cmd, int options);
+ void mch_start_job(char **argv, job_T *job);
+ char *mch_job_status(job_T *job);
+ int mch_stop_job(job_T *job, char_u *how);
  void mch_breakcheck(void);
  int mch_expandpath(garray_T *gap, char_u *path, int flags);
  int mch_expand_wildcards(int num_pat, char_u **pat, int *num_file, char_u 
***file, int flags);
*** ../vim-7.4.1274/src/feature.h       2016-02-07 14:23:21.744830821 +0100
--- src/feature.h       2016-02-06 22:07:35.019457345 +0100
***************
*** 1255,1267 ****
  #endif
  
  /*
!  * The Channel feature requires +eval.
   */
  #if !defined(FEAT_EVAL) && defined(FEAT_CHANNEL)
  # undef FEAT_CHANNEL
  #endif
  
  /*
   * +signs             Allow signs to be displayed to the left of text lines.
   *                    Adds the ":sign" command.
   */
--- 1255,1274 ----
  #endif
  
  /*
!  * The +channel feature requires +eval.
   */
  #if !defined(FEAT_EVAL) && defined(FEAT_CHANNEL)
  # undef FEAT_CHANNEL
  #endif
  
  /*
+  * The +job feature requires Unix and +eval.
+  */
+ #if defined(UNIX) && defined(FEAT_EVAL)
+ # define FEAT_JOB
+ #endif
+ 
+ /*
   * +signs             Allow signs to be displayed to the left of text lines.
   *                    Adds the ":sign" command.
   */
*** ../vim-7.4.1274/src/version.c       2016-02-07 14:23:21.744830821 +0100
--- src/version.c       2016-02-07 14:15:33.405713009 +0100
***************
*** 289,294 ****
--- 289,299 ----
  #else
        "-insert_expand",
  #endif
+ #ifdef FEAT_JOB
+       "+job",
+ #else
+       "-job",
+ #endif
  #ifdef FEAT_JUMPLIST
        "+jumplist",
  #else
*** ../vim-7.4.1274/src/testdir/test_channel.vim        2016-02-07 
14:23:21.744830821 +0100
--- src/testdir/test_channel.vim        2016-02-07 14:08:57.777836779 +0100
***************
*** 8,15 ****
  " This test requires the Python command to run the test server.
  " This most likely only works on Unix and Windows.
  if has('unix')
!   " We also need the pkill command to make sure the server can be stopped.
!   if !executable('python') || !executable('pkill')
      finish
    endif
  elseif has('win32')
--- 8,16 ----
  " This test requires the Python command to run the test server.
  " This most likely only works on Unix and Windows.
  if has('unix')
!   " We also need the job feature or the pkill command to make sure the server
!   " can be stopped.
!   if !(executable('python') && (has('job') || executable('pkill')))
      finish
    endif
  elseif has('win32')
***************
*** 27,33 ****
    " The Python program writes the port number in Xportnr.
    call delete("Xportnr")
  
!   if has('win32')
      silent !start cmd /c start "test_channel" py test_channel.py
    else
      silent !python test_channel.py&
--- 28,36 ----
    " The Python program writes the port number in Xportnr.
    call delete("Xportnr")
  
!   if has('job')
!     let s:job = job_start("python test_channel.py")
!   elseif has('win32')
      silent !start cmd /c start "test_channel" py test_channel.py
    else
      silent !python test_channel.py&
***************
*** 62,68 ****
  endfunc
  
  func s:kill_server()
!   if has('win32')
      call system('taskkill /IM py.exe /T /F /FI "WINDOWTITLE eq test_channel"')
    else
      call system("pkill -f test_channel.py")
--- 65,73 ----
  endfunc
  
  func s:kill_server()
!   if has('job')
!     call job_stop(s:job)
!   elseif has('win32')
      call system('taskkill /IM py.exe /T /F /FI "WINDOWTITLE eq test_channel"')
    else
      call system("pkill -f test_channel.py")
*** ../vim-7.4.1274/src/version.c       2016-02-07 14:23:21.744830821 +0100
--- src/version.c       2016-02-07 14:15:33.405713009 +0100
***************
*** 744,745 ****
--- 749,752 ----
  {   /* Add new patch number below this line */
+ /**/
+     1274,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
167. You have more than 200 websites bookmarked.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui