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