Last active
May 9, 2022 02:44
-
-
Save mattn/d47e7d3bfe5ade4be86062b565a4bfca to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/channel.c b/src/channel.c | |
index 2d68287..859a7d0 100644 | |
--- a/src/channel.c | |
+++ b/src/channel.c | |
@@ -4649,6 +4649,83 @@ job_check_ended(void) | |
} | |
} | |
+#ifndef USE_ARGV | |
+ char_u * | |
+escape_arg(char_u *arg) | |
+{ | |
+ int slen, dlen, escaping, 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 == '\\' || *s == '^') | |
+ ++dlen; | |
+ if (*s == ' ' || *s == '\t') | |
+ has_spaces = TRUE; | |
+ } | |
+ | |
+ if (dlen == slen) | |
+ return vim_strsave(arg); | |
+ | |
+ if (has_spaces) | |
+ dlen += 2; | |
+ | |
+ /* 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 '^': | |
+ if (has_spaces) | |
+ *d++ = *s++; | |
+ *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; | |
+} | |
+#endif | |
+ | |
/* | |
* "job_start()" function | |
*/ | |
@@ -4776,27 +4853,12 @@ job_start(typval_T *argvars) | |
#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); | |
+ s = escape_arg(s); | |
+ if (s == NULL) | |
+ goto theend; | |
+ ga_concat(&ga, s); | |
+ vim_free(s); | |
if (li->li_next != NULL) | |
ga_append(&ga, ' '); | |
#endif | |
diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim | |
index fbcd496..6ebbcec 100644 | |
--- a/src/testdir/test_channel.vim | |
+++ b/src/testdir/test_channel.vim | |
@@ -1494,12 +1494,7 @@ func Test_read_nonl_line() | |
endif | |
let g:linecount = 0 | |
- if has('win32') | |
- " workaround: 'shellescape' does improper escaping double quotes | |
- let arg = 'import sys;sys.stdout.write(\"1\n2\n3\")' | |
- else | |
- let arg = 'import sys;sys.stdout.write("1\n2\n3")' | |
- endif | |
+ let arg = 'import sys;sys.stdout.write("1\n2\n3")' | |
call job_start([s:python, '-c', arg], {'callback': 'MyLineCountCb'}) | |
call WaitFor('3 <= g:linecount') | |
call assert_equal(3, g:linecount) | |
@@ -1541,5 +1536,53 @@ func Test_close_lambda() | |
call s:run_server('Ch_test_close_lambda') | |
endfunc | |
+func s:test_list_args(cmd, out, remove_lf) | |
+ try | |
+ let s:out = '' | |
+ call job_start([s:python, '-c', a:cmd], {'callback': {ch,msg->execute('let s:out.=msg')}, 'out_mode': 'raw'}) | |
+ call WaitFor('"" != s:out') | |
+ if has('win32') | |
+ let s:out = substitute(s:out, '\r', '', 'g') | |
+ endif | |
+ if a:remove_lf | |
+ let s:out = substitute(s:out, '\n$', '', 'g') | |
+ endif | |
+ call assert_equal(a:out, s:out) | |
+ finally | |
+ unlet s:out | |
+ endtry | |
+endfunc | |
+ | |
+func Test_list_args() | |
+ if !has('job') | |
+ return | |
+ endif | |
+ | |
+ call s:test_list_args('import sys;sys.stdout.write("hello world")', "hello world", 0) | |
+ call s:test_list_args('import sys;sys.stdout.write("hello\nworld")', "hello\nworld", 0) | |
+ call s:test_list_args('import sys;sys.stdout.write(''hello\nworld'')', "hello\nworld", 0) | |
+ call s:test_list_args('import sys;sys.stdout.write(''hello"world'')', "hello\"world", 0) | |
+ call s:test_list_args('import sys;sys.stdout.write(''hello^world'')', "hello^world", 0) | |
+ call s:test_list_args('import sys;sys.stdout.write("hello&&world")', "hello&&world", 0) | |
+ call s:test_list_args('import sys;sys.stdout.write(''hello\\world'')', "hello\\world", 0) | |
+ call s:test_list_args('import sys;sys.stdout.write(''hello\\\\world'')', "hello\\\\world", 0) | |
+ call s:test_list_args('import sys;sys.stdout.write("hello\"world\"")', 'hello"world"', 0) | |
+ call s:test_list_args('import sys;sys.stdout.write("h\"ello worl\"d")', 'h"ello worl"d', 0) | |
+ call s:test_list_args('import sys;sys.stdout.write("h\"e\\\"llo wor\\\"l\"d")', 'h"e\"llo wor\"l"d', 0) | |
+ call s:test_list_args('import sys;sys.stdout.write("h\"e\\\"llo world")', 'h"e\"llo world', 0) | |
+ call s:test_list_args('import sys;sys.stdout.write("hello\tworld")', "hello\tworld", 0) | |
+ | |
+ " tests which not contain spaces in the argument | |
+ call s:test_list_args('print("hello\nworld")', "hello\nworld", 1) | |
+ call s:test_list_args('print(''hello\nworld'')', "hello\nworld", 1) | |
+ call s:test_list_args('print(''hello"world'')', "hello\"world", 1) | |
+ call s:test_list_args('print(''hello^world'')', "hello^world", 1) | |
+ call s:test_list_args('print("hello&&world")', "hello&&world", 1) | |
+ call s:test_list_args('print(''hello\\world'')', "hello\\world", 1) | |
+ call s:test_list_args('print(''hello\\\\world'')', "hello\\\\world", 1) | |
+ call s:test_list_args('print("hello\"world\"")', 'hello"world"', 1) | |
+ call s:test_list_args('print("hello\tworld")', "hello\tworld", 1) | |
+endfunc | |
+ | |
" Uncomment this to see what happens, output is in src/testdir/channellog. | |
" call ch_logfile('channellog', 'w') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment