1
0
forked from aniani/vim

patch 8.0.0928: MS-Windows: passing arglist to job has escaping problems

Problem:    MS-Windows: passing arglist to job has escaping problems.
Solution:   Improve escaping. (Yasuhiro Matsumoto, closes #1954)
This commit is contained in:
Bram Moolenaar
2017-08-13 17:13:09 +02:00
parent 274a52fd58
commit dcaa61384c
6 changed files with 226 additions and 80 deletions

View File

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