summaryrefslogtreecommitdiff
path: root/src/channel.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2017-08-13 17:13:09 +0200
committerBram Moolenaar <Bram@vim.org>2017-08-13 17:13:09 +0200
commitdcaa61384ca76e42f7feda5640fb85b58cee03e5 (patch)
tree21eb8c92bc31eae5fe4d51c6f19bed1c05f01eb9 /src/channel.c
parent274a52fd58bbd88f5fe8b96d87abe3574c8169af (diff)
downloadvim-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.c137
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
}