diff options
author | Bram Moolenaar <Bram@vim.org> | 2017-08-13 17:13:09 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2017-08-13 17:13:09 +0200 |
commit | dcaa61384ca76e42f7feda5640fb85b58cee03e5 (patch) | |
tree | 21eb8c92bc31eae5fe4d51c6f19bed1c05f01eb9 /src/channel.c | |
parent | 274a52fd58bbd88f5fe8b96d87abe3574c8169af (diff) | |
download | vim-git-dcaa61384ca76e42f7feda5640fb85b58cee03e5.tar.gz |
patch 8.0.0928: MS-Windows: passing arglist to job has escaping problemsv8.0.0928
Problem: MS-Windows: passing arglist to job has escaping problems.
Solution: Improve escaping. (Yasuhiro Matsumoto, closes #1954)
Diffstat (limited to 'src/channel.c')
-rw-r--r-- | src/channel.c | 137 |
1 files changed, 108 insertions, 29 deletions
diff --git a/src/channel.c b/src/channel.c index 70ee2f309..e8030798c 100644 --- a/src/channel.c +++ b/src/channel.c @@ -4720,6 +4720,111 @@ job_still_useful(job_T *job) return job_need_end_check(job) || job_channel_still_useful(job); } +#if !defined(USE_ARGV) || defined(PROTO) +/* + * Escape one argument for an external command. + * Returns the escaped string in allocated memory. NULL when out of memory. + */ + static char_u * +win32_escape_arg(char_u *arg) +{ + int slen, dlen; + int escaping = 0; + int i; + char_u *s, *d; + char_u *escaped_arg; + int has_spaces = FALSE; + + /* First count the number of extra bytes required. */ + slen = STRLEN(arg); + dlen = slen; + for (s = arg; *s != NUL; MB_PTR_ADV(s)) + { + if (*s == '"' || *s == '\\') + ++dlen; + if (*s == ' ' || *s == '\t') + has_spaces = TRUE; + } + + if (has_spaces) + dlen += 2; + + if (dlen == slen) + return vim_strsave(arg); + + /* Allocate memory for the result and fill it. */ + escaped_arg = alloc(dlen + 1); + if (escaped_arg == NULL) + return NULL; + memset(escaped_arg, 0, dlen+1); + + d = escaped_arg; + + if (has_spaces) + *d++ = '"'; + + for (s = arg; *s != NUL;) + { + switch (*s) + { + case '"': + for (i = 0; i < escaping; i++) + *d++ = '\\'; + escaping = 0; + *d++ = '\\'; + *d++ = *s++; + break; + case '\\': + escaping++; + *d++ = *s++; + break; + default: + escaping = 0; + MB_COPY_CHAR(s, d); + break; + } + } + + /* add terminating quote and finish with a NUL */ + if (has_spaces) + { + for (i = 0; i < escaping; i++) + *d++ = '\\'; + *d++ = '"'; + } + *d = NUL; + + return escaped_arg; +} + +/* + * Build a command line from a list, taking care of escaping. + * The result is put in gap->ga_data. + * Returns FAIL when out of memory. + */ + int +win32_build_cmd(list_T *l, garray_T *gap) +{ + listitem_T *li; + char_u *s; + + for (li = l->lv_first; li != NULL; li = li->li_next) + { + s = get_tv_string_chk(&li->li_tv); + if (s == NULL) + return FAIL; + s = win32_escape_arg(s); + if (s == NULL) + return FAIL; + ga_concat(gap, s); + vim_free(s); + if (li->li_next != NULL) + ga_append(gap, ' '); + } + return OK; +} +#endif + /* * NOTE: Must call job_cleanup() only once right after the status of "job" * changed to JOB_ENDED (i.e. after job_status() returned "dead" first or @@ -5093,51 +5198,25 @@ job_start(typval_T *argvars, jobopt_T *opt_arg) else { list_T *l = argvars[0].vval.v_list; +#ifdef USE_ARGV 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) goto theend; -#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 - /* Only escape when needed, double quotes are not always allowed. */ - if (li != l->lv_first && vim_strpbrk(s, (char_u *)" \t\"") != NULL) - { -# ifdef WIN32 - int old_ssl = p_ssl; - - /* This is using CreateProcess, not cmd.exe. Always use - * double quote and backslashes. */ - p_ssl = 0; -# endif - s = vim_strsave_shellescape(s, FALSE, TRUE); -# ifdef WIN32 - p_ssl = old_ssl; -# endif - if (s == NULL) - goto theend; - ga_concat(&ga, s); - vim_free(s); - } - else - ga_concat(&ga, s); - if (li->li_next != NULL) - ga_append(&ga, ' '); -#endif } -#ifdef USE_ARGV argv[argc] = NULL; #else + if (win32_build_cmd(l, &ga) == FAIL) + goto theend; cmd = ga.ga_data; #endif } |