@@ -2217,7 +2217,7 @@ doConnStrQuoting(PQExpBuffer buf, const char *str)
22172217
22182218/*
22192219 * Append the given string to the shell command being built in the buffer,
2220- * with suitable shell-style quoting.
2220+ * with suitable shell-style quoting to create exactly one argument .
22212221 *
22222222 * Forbid LF or CR characters, which have scant practical use beyond designing
22232223 * security breaches. The Windows command shell is unusable as a conduit for
@@ -2249,8 +2249,20 @@ doShellQuoting(PQExpBuffer buf, const char *str)
22492249 }
22502250 appendPQExpBufferChar (buf , '\'' );
22512251#else /* WIN32 */
2252+ int backslash_run_length = 0 ;
22522253
2253- appendPQExpBufferChar (buf , '"' );
2254+ /*
2255+ * A Windows system() argument experiences two layers of interpretation.
2256+ * First, cmd.exe interprets the string. Its behavior is undocumented,
2257+ * but a caret escapes any byte except LF or CR that would otherwise have
2258+ * special meaning. Handling of a caret before LF or CR differs between
2259+ * "cmd.exe /c" and other modes, and it is unusable here.
2260+ *
2261+ * Second, the new process parses its command line to construct argv (see
2262+ * https://msdn.microsoft.com/en-us/library/17w5ykft.aspx). This treats
2263+ * backslash-double quote sequences specially.
2264+ */
2265+ appendPQExpBufferStr (buf , "^\"" );
22542266 for (p = str ; * p ; p ++ )
22552267 {
22562268 if (* p == '\n' || * p == '\r' )
@@ -2261,11 +2273,41 @@ doShellQuoting(PQExpBuffer buf, const char *str)
22612273 exit (EXIT_FAILURE );
22622274 }
22632275
2276+ /* Change N backslashes before a double quote to 2N+1 backslashes. */
22642277 if (* p == '"' )
2265- appendPQExpBufferStr (buf , "\\\"" );
2278+ {
2279+ while (backslash_run_length )
2280+ {
2281+ appendPQExpBufferStr (buf , "^\\" );
2282+ backslash_run_length -- ;
2283+ }
2284+ appendPQExpBufferStr (buf , "^\\" );
2285+ }
2286+ else if (* p == '\\' )
2287+ backslash_run_length ++ ;
22662288 else
2267- appendPQExpBufferChar (buf , * p );
2289+ backslash_run_length = 0 ;
2290+
2291+ /*
2292+ * Decline to caret-escape the most mundane characters, to ease
2293+ * debugging and lest we approach the command length limit.
2294+ */
2295+ if (!((* p >= 'a' && * p <= 'z' ) ||
2296+ (* p >= 'A' && * p <= 'Z' ) ||
2297+ (* p >= '0' && * p <= '9' )))
2298+ appendPQExpBufferChar (buf , '^' );
2299+ appendPQExpBufferChar (buf , * p );
2300+ }
2301+
2302+ /*
2303+ * Change N backslashes at end of argument to 2N backslashes, because they
2304+ * precede the double quote that terminates the argument.
2305+ */
2306+ while (backslash_run_length )
2307+ {
2308+ appendPQExpBufferStr (buf , "^\\" );
2309+ backslash_run_length -- ;
22682310 }
2269- appendPQExpBufferChar (buf , '"' );
2311+ appendPQExpBufferStr (buf , "^\"" );
22702312#endif /* WIN32 */
22712313}
0 commit comments