Hi Bram!

On Sun, Feb 7, 2016 at 10:27 PM, Bram Moolenaar <[email protected]> wrote:
>
> 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.

This is a really big news :)
Do you have a plan to add more channel functions, like

* Output to a spawned process's stdin
* Input from a spawned process's stdout / stderr

I think it is useful if Vim has more communication APIs.

-- 
-- 
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