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