@@ -2129,7 +2129,7 @@ doConnStrQuoting(PQExpBuffer buf, const char *str)
2129
2129
2130
2130
/*
2131
2131
* Append the given string to the shell command being built in the buffer,
2132
- * with suitable shell-style quoting.
2132
+ * with suitable shell-style quoting to create exactly one argument .
2133
2133
*
2134
2134
* Forbid LF or CR characters, which have scant practical use beyond designing
2135
2135
* security breaches. The Windows command shell is unusable as a conduit for
@@ -2161,8 +2161,20 @@ doShellQuoting(PQExpBuffer buf, const char *str)
2161
2161
}
2162
2162
appendPQExpBufferChar (buf ,'\'' );
2163
2163
#else /* WIN32 */
2164
+ int backslash_run_length = 0 ;
2164
2165
2165
- appendPQExpBufferChar (buf ,'"' );
2166
+ /*
2167
+ * A Windows system() argument experiences two layers of interpretation.
2168
+ * First, cmd.exe interprets the string. Its behavior is undocumented,
2169
+ * but a caret escapes any byte except LF or CR that would otherwise have
2170
+ * special meaning. Handling of a caret before LF or CR differs between
2171
+ * "cmd.exe /c" and other modes, and it is unusable here.
2172
+ *
2173
+ * Second, the new process parses its command line to construct argv (see
2174
+ * https://msdn.microsoft.com/en-us/library/17w5ykft.aspx). This treats
2175
+ * backslash-double quote sequences specially.
2176
+ */
2177
+ appendPQExpBufferStr (buf ,"^\"" );
2166
2178
for (p = str ;* p ;p ++ )
2167
2179
{
2168
2180
if (* p == '\n' || * p == '\r' )
@@ -2173,11 +2185,41 @@ doShellQuoting(PQExpBuffer buf, const char *str)
2173
2185
exit (EXIT_FAILURE );
2174
2186
}
2175
2187
2188
+ /* Change N backslashes before a double quote to 2N+1 backslashes. */
2176
2189
if (* p == '"' )
2177
- appendPQExpBufferStr (buf ,"\\\"" );
2190
+ {
2191
+ while (backslash_run_length )
2192
+ {
2193
+ appendPQExpBufferStr (buf ,"^\\" );
2194
+ backslash_run_length -- ;
2195
+ }
2196
+ appendPQExpBufferStr (buf ,"^\\" );
2197
+ }
2198
+ else if (* p == '\\' )
2199
+ backslash_run_length ++ ;
2178
2200
else
2179
- appendPQExpBufferChar (buf ,* p );
2201
+ backslash_run_length = 0 ;
2202
+
2203
+ /*
2204
+ * Decline to caret-escape the most mundane characters, to ease
2205
+ * debugging and lest we approach the command length limit.
2206
+ */
2207
+ if (!((* p >='a' && * p <='z' )||
2208
+ (* p >='A' && * p <='Z' )||
2209
+ (* p >='0' && * p <='9' )))
2210
+ appendPQExpBufferChar (buf ,'^' );
2211
+ appendPQExpBufferChar (buf ,* p );
2212
+ }
2213
+
2214
+ /*
2215
+ * Change N backslashes at end of argument to 2N backslashes, because they
2216
+ * precede the double quote that terminates the argument.
2217
+ */
2218
+ while (backslash_run_length )
2219
+ {
2220
+ appendPQExpBufferStr (buf ,"^\\" );
2221
+ backslash_run_length -- ;
2180
2222
}
2181
- appendPQExpBufferChar (buf ,'"' );
2223
+ appendPQExpBufferStr (buf ,"^\"" );
2182
2224
#endif /* WIN32 */
2183
2225
}