@@ -180,6 +180,210 @@ quote_identifier(const char *s)
180
180
}
181
181
182
182
183
+ /*
184
+ * Append the given string to the shell command being built in the buffer,
185
+ * with suitable shell-style quoting to create exactly one argument.
186
+ *
187
+ * Forbid LF or CR characters, which have scant practical use beyond designing
188
+ * security breaches. The Windows command shell is unusable as a conduit for
189
+ * arguments containing LF or CR characters. A future major release should
190
+ * reject those characters in CREATE ROLE and CREATE DATABASE, because use
191
+ * there eventually leads to errors here.
192
+ */
193
+ void
194
+ appendShellString (PQExpBuffer buf , const char * str )
195
+ {
196
+ const char * p ;
197
+
198
+ #ifndef WIN32
199
+ appendPQExpBufferChar (buf , '\'' );
200
+ for (p = str ; * p ; p ++ )
201
+ {
202
+ if (* p == '\n' || * p == '\r' )
203
+ {
204
+ fprintf (stderr ,
205
+ _ ("shell command argument contains a newline or carriage return: \"%s\"\n" ),
206
+ str );
207
+ exit (EXIT_FAILURE );
208
+ }
209
+
210
+ if (* p == '\'' )
211
+ appendPQExpBufferStr (buf , "'\"'\"'" );
212
+ else
213
+ appendPQExpBufferChar (buf , * p );
214
+ }
215
+ appendPQExpBufferChar (buf , '\'' );
216
+ #else /* WIN32 */
217
+ int backslash_run_length = 0 ;
218
+
219
+ /*
220
+ * A Windows system() argument experiences two layers of interpretation.
221
+ * First, cmd.exe interprets the string. Its behavior is undocumented,
222
+ * but a caret escapes any byte except LF or CR that would otherwise have
223
+ * special meaning. Handling of a caret before LF or CR differs between
224
+ * "cmd.exe /c" and other modes, and it is unusable here.
225
+ *
226
+ * Second, the new process parses its command line to construct argv (see
227
+ * https://msdn.microsoft.com/en-us/library/17w5ykft.aspx). This treats
228
+ * backslash-double quote sequences specially.
229
+ */
230
+ appendPQExpBufferStr (buf , "^\"" );
231
+ for (p = str ; * p ; p ++ )
232
+ {
233
+ if (* p == '\n' || * p == '\r' )
234
+ {
235
+ fprintf (stderr ,
236
+ _ ("shell command argument contains a newline or carriage return: \"%s\"\n" ),
237
+ str );
238
+ exit (EXIT_FAILURE );
239
+ }
240
+
241
+ /* Change N backslashes before a double quote to 2N+1 backslashes. */
242
+ if (* p == '"' )
243
+ {
244
+ while (backslash_run_length )
245
+ {
246
+ appendPQExpBufferStr (buf , "^\\" );
247
+ backslash_run_length -- ;
248
+ }
249
+ appendPQExpBufferStr (buf , "^\\" );
250
+ }
251
+ else if (* p == '\\' )
252
+ backslash_run_length ++ ;
253
+ else
254
+ backslash_run_length = 0 ;
255
+
256
+ /*
257
+ * Decline to caret-escape the most mundane characters, to ease
258
+ * debugging and lest we approach the command length limit.
259
+ */
260
+ if (!((* p >= 'a' && * p <= 'z' ) ||
261
+ (* p >= 'A' && * p <= 'Z' ) ||
262
+ (* p >= '0' && * p <= '9' )))
263
+ appendPQExpBufferChar (buf , '^' );
264
+ appendPQExpBufferChar (buf , * p );
265
+ }
266
+
267
+ /*
268
+ * Change N backslashes at end of argument to 2N backslashes, because they
269
+ * precede the double quote that terminates the argument.
270
+ */
271
+ while (backslash_run_length )
272
+ {
273
+ appendPQExpBufferStr (buf , "^\\" );
274
+ backslash_run_length -- ;
275
+ }
276
+ appendPQExpBufferStr (buf , "^\"" );
277
+ #endif /* WIN32 */
278
+ }
279
+
280
+
281
+ /*
282
+ * Append the given string to the buffer, with suitable quoting for passing
283
+ * the string as a value, in a keyword/pair value in a libpq connection
284
+ * string
285
+ */
286
+ void
287
+ appendConnStrVal (PQExpBuffer buf , const char * str )
288
+ {
289
+ const char * s ;
290
+ bool needquotes ;
291
+
292
+ /*
293
+ * If the string is one or more plain ASCII characters, no need to quote
294
+ * it. This is quite conservative, but better safe than sorry.
295
+ */
296
+ needquotes = true;
297
+ for (s = str ; * s ; s ++ )
298
+ {
299
+ if (!((* s >= 'a' && * s <= 'z' ) || (* s >= 'A' && * s <= 'Z' ) ||
300
+ (* s >= '0' && * s <= '9' ) || * s == '_' || * s == '.' ))
301
+ {
302
+ needquotes = true;
303
+ break ;
304
+ }
305
+ needquotes = false;
306
+ }
307
+
308
+ if (needquotes )
309
+ {
310
+ appendPQExpBufferChar (buf , '\'' );
311
+ while (* str )
312
+ {
313
+ /* ' and \ must be escaped by to \' and \\ */
314
+ if (* str == '\'' || * str == '\\' )
315
+ appendPQExpBufferChar (buf , '\\' );
316
+
317
+ appendPQExpBufferChar (buf , * str );
318
+ str ++ ;
319
+ }
320
+ appendPQExpBufferChar (buf , '\'' );
321
+ }
322
+ else
323
+ appendPQExpBufferStr (buf , str );
324
+ }
325
+
326
+
327
+ /*
328
+ * Append a psql meta-command that connects to the given database with the
329
+ * then-current connection's user, host and port.
330
+ */
331
+ void
332
+ appendPsqlMetaConnect (PQExpBuffer buf , const char * dbname )
333
+ {
334
+ const char * s ;
335
+ bool complex ;
336
+
337
+ /*
338
+ * If the name is plain ASCII characters, emit a trivial "\connect "foo"".
339
+ * For other names, even many not technically requiring it, skip to the
340
+ * general case. No database has a zero-length name.
341
+ */
342
+ complex = false;
343
+ for (s = dbname ; * s ; s ++ )
344
+ {
345
+ if (* s == '\n' || * s == '\r' )
346
+ {
347
+ fprintf (stderr ,
348
+ _ ("database name contains a newline or carriage return: \"%s\"\n" ),
349
+ dbname );
350
+ exit (EXIT_FAILURE );
351
+ }
352
+
353
+ if (!((* s >= 'a' && * s <= 'z' ) || (* s >= 'A' && * s <= 'Z' ) ||
354
+ (* s >= '0' && * s <= '9' ) || * s == '_' || * s == '.' ))
355
+ {
356
+ complex = true;
357
+ }
358
+ }
359
+
360
+ appendPQExpBufferStr (buf , "\\connect " );
361
+ if (complex )
362
+ {
363
+ PQExpBufferData connstr ;
364
+
365
+ initPQExpBuffer (& connstr );
366
+ appendPQExpBuffer (& connstr , "dbname=" );
367
+ appendConnStrVal (& connstr , dbname );
368
+
369
+ appendPQExpBuffer (buf , "-reuse-previous=on " );
370
+
371
+ /*
372
+ * As long as the name does not contain a newline, SQL identifier
373
+ * quoting satisfies the psql meta-command parser. Prefer not to
374
+ * involve psql-interpreted single quotes, which behaved differently
375
+ * before PostgreSQL 9.2.
376
+ */
377
+ appendPQExpBufferStr (buf , quote_identifier (connstr .data ));
378
+
379
+ termPQExpBuffer (& connstr );
380
+ }
381
+ else
382
+ appendPQExpBufferStr (buf , quote_identifier (dbname ));
383
+ appendPQExpBufferChar (buf , '\n' );
384
+ }
385
+
386
+
183
387
/*
184
388
* get_user_info()
185
389
* (copied from initdb.c) find the current user
0 commit comments