Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 41e3c94

Browse files
committed
psql: when tab-completing, use quotes on file names that need them
psql backslash commands that deal with file or directory names require quotes around those that have spaces, single quotes, or backslashes. However, tab-completing such names does not provide said quotes, and is thus almost useless with them. This patch fixes the problem by having a wrapper function around rl_filename_completion_function that dequotes on input and quotes on output. This eases dealing with such names. Author: Noah Misch
1 parent cb3a7c2 commit 41e3c94

File tree

3 files changed

+128
-2
lines changed

3 files changed

+128
-2
lines changed

src/bin/psql/stringutils.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,3 +272,72 @@ strip_quotes(char *source, char quote, char escape, int encoding)
272272

273273
*dst = '\0';
274274
}
275+
276+
277+
/*
278+
* quote_if_needed
279+
*
280+
* Opposite of strip_quotes(). If "source" denotes itself literally without
281+
* quoting or escaping, returns NULL. Otherwise, returns a malloc'd copy with
282+
* quoting and escaping applied:
283+
*
284+
* source - string to parse
285+
* entails_quote - any of these present? need outer quotes
286+
* quote - doubled within string, affixed to both ends
287+
* escape - doubled within string
288+
* encoding - the active character-set encoding
289+
*
290+
* Do not use this as a substitute for PQescapeStringConn(). Use it for
291+
* strings to be parsed by strtokx() or psql_scan_slash_option().
292+
*/
293+
char *
294+
quote_if_needed(const char *source, const char *entails_quote,
295+
char quote, char escape, int encoding)
296+
{
297+
const char *src;
298+
char *ret;
299+
char *dst;
300+
bool need_quotes = false;
301+
302+
psql_assert(source);
303+
psql_assert(quote);
304+
305+
src = source;
306+
dst = ret = pg_malloc(2 * strlen(src) + 3); /* excess */
307+
308+
*dst++ = quote;
309+
310+
while (*src)
311+
{
312+
char c = *src;
313+
int i;
314+
315+
if (c == quote)
316+
{
317+
need_quotes = true;
318+
*dst++ = quote;
319+
}
320+
else if (c == escape)
321+
{
322+
need_quotes = true;
323+
*dst++ = escape;
324+
}
325+
else if (strchr(entails_quote, c))
326+
need_quotes = true;
327+
328+
i = PQmblen(src, encoding);
329+
while (i--)
330+
*dst++ = *src++;
331+
}
332+
333+
*dst++ = quote;
334+
*dst = '\0';
335+
336+
if (!need_quotes)
337+
{
338+
free(ret);
339+
ret = NULL;
340+
}
341+
342+
return ret;
343+
}

src/bin/psql/stringutils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,7 @@ extern char *strtokx(const char *s,
1919
bool del_quotes,
2020
int encoding);
2121

22+
extern char *quote_if_needed(const char *source, const char *entails_quote,
23+
char quote, char escape, int encoding);
24+
2225
#endif /* STRINGUTILS_H */

src/bin/psql/tab-complete.c

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,7 @@ static char *complete_from_list(const char *text, int state);
680680
static char *complete_from_const(const char *text, int state);
681681
static char **complete_from_variables(char *text,
682682
const char *prefix, const char *suffix);
683+
static char *complete_from_files(const char *text, int state);
683684

684685
static char *pg_strdup_same_case(const char *s, const char *ref);
685686
static PGresult *exec_query(const char *query);
@@ -1630,7 +1631,10 @@ psql_completion(char *text, int start, int end)
16301631
pg_strcasecmp(prev3_wd, "BINARY") == 0) &&
16311632
(pg_strcasecmp(prev_wd, "FROM") == 0 ||
16321633
pg_strcasecmp(prev_wd, "TO") == 0))
1633-
matches = completion_matches(text, filename_completion_function);
1634+
{
1635+
completion_charp = "";
1636+
matches = completion_matches(text, complete_from_files);
1637+
}
16341638

16351639
/* Handle COPY|BINARY <sth> FROM|TO filename */
16361640
else if ((pg_strcasecmp(prev4_wd, "COPY") == 0 ||
@@ -2953,7 +2957,10 @@ psql_completion(char *text, int start, int end)
29532957
strcmp(prev_wd, "\\s") == 0 ||
29542958
strcmp(prev_wd, "\\w") == 0 || strcmp(prev_wd, "\\write") == 0
29552959
)
2956-
matches = completion_matches(text, filename_completion_function);
2960+
{
2961+
completion_charp = "\\";
2962+
matches = completion_matches(text, complete_from_files);
2963+
}
29572964

29582965
/*
29592966
* Finally, we look through the list of "things", such as TABLE, INDEX and
@@ -3426,6 +3433,53 @@ complete_from_variables(char *text, const char *prefix, const char *suffix)
34263433
}
34273434

34283435

3436+
/*
3437+
* This function wraps rl_filename_completion_function() to strip quotes from
3438+
* the input before searching for matches and to quote any matches for which
3439+
* the consuming command will require it.
3440+
*/
3441+
static char *
3442+
complete_from_files(const char *text, int state)
3443+
{
3444+
static const char *unquoted_text;
3445+
char *unquoted_match;
3446+
char *ret = NULL;
3447+
3448+
if (state == 0)
3449+
{
3450+
/* Initialization: stash the unquoted input. */
3451+
unquoted_text = strtokx(text, "", NULL, "'", *completion_charp,
3452+
false, true, pset.encoding);
3453+
/* expect a NULL return for the empty string only */
3454+
if (!unquoted_text)
3455+
{
3456+
psql_assert(!*text);
3457+
unquoted_text = text;
3458+
}
3459+
}
3460+
3461+
unquoted_match = filename_completion_function(unquoted_text, state);
3462+
if (unquoted_match)
3463+
{
3464+
/*
3465+
* Caller sets completion_charp to a zero- or one-character string
3466+
* containing the escape character. This is necessary since \copy has
3467+
* no escape character, but every other backslash command recognizes
3468+
* "\" as an escape character. Since we have only two callers, don't
3469+
* bother providing a macro to simplify this.
3470+
*/
3471+
ret = quote_if_needed(unquoted_match, " \t\r\n\"`",
3472+
'\'', *completion_charp, pset.encoding);
3473+
if (ret)
3474+
free(unquoted_match);
3475+
else
3476+
ret = unquoted_match;
3477+
}
3478+
3479+
return ret;
3480+
}
3481+
3482+
34293483
/* HELPER FUNCTIONS */
34303484

34313485

0 commit comments

Comments
 (0)