To: vim_dev@googlegroups.com Subject: Patch 7.4.1274 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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"]) + < + 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 -- Bram@Moolenaar.net -- 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 ///