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:
137
src/channel.c
137
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
|
||||
}
|
||||
|
Reference in New Issue
Block a user