*dst = '\0';
}
+
+
+/*
+ * quote_if_needed
+ *
+ * Opposite of strip_quotes(). If "source" denotes itself literally without
+ * quoting or escaping, returns NULL. Otherwise, returns a malloc'd copy with
+ * quoting and escaping applied:
+ *
+ * source - string to parse
+ * entails_quote - any of these present? need outer quotes
+ * quote - doubled within string, affixed to both ends
+ * escape - doubled within string
+ * encoding - the active character-set encoding
+ *
+ * Do not use this as a substitute for PQescapeStringConn(). Use it for
+ * strings to be parsed by strtokx() or psql_scan_slash_option().
+ */
+char *
+quote_if_needed(const char *source, const char *entails_quote,
+ char quote, char escape, int encoding)
+{
+ const char *src;
+ char *ret;
+ char *dst;
+ bool need_quotes = false;
+
+ psql_assert(source);
+ psql_assert(quote);
+
+ src = source;
+ dst = ret = pg_malloc(2 * strlen(src) + 3); /* excess */
+
+ *dst++ = quote;
+
+ while (*src)
+ {
+ char c = *src;
+ int i;
+
+ if (c == quote)
+ {
+ need_quotes = true;
+ *dst++ = quote;
+ }
+ else if (c == escape)
+ {
+ need_quotes = true;
+ *dst++ = escape;
+ }
+ else if (strchr(entails_quote, c))
+ need_quotes = true;
+
+ i = PQmblen(src, encoding);
+ while (i--)
+ *dst++ = *src++;
+ }
+
+ *dst++ = quote;
+ *dst = '\0';
+
+ if (!need_quotes)
+ {
+ free(ret);
+ ret = NULL;
+ }
+
+ return ret;
+}
static char *complete_from_const(const char *text, int state);
static char **complete_from_variables(char *text,
const char *prefix, const char *suffix);
+static char *complete_from_files(const char *text, int state);
static char *pg_strdup_same_case(const char *s, const char *ref);
static PGresult *exec_query(const char *query);
pg_strcasecmp(prev3_wd, "BINARY") == 0) &&
(pg_strcasecmp(prev_wd, "FROM") == 0 ||
pg_strcasecmp(prev_wd, "TO") == 0))
- matches = completion_matches(text, filename_completion_function);
+ {
+ completion_charp = "";
+ matches = completion_matches(text, complete_from_files);
+ }
/* Handle COPY|BINARY <sth> FROM|TO filename */
else if ((pg_strcasecmp(prev4_wd, "COPY") == 0 ||
strcmp(prev_wd, "\\s") == 0 ||
strcmp(prev_wd, "\\w") == 0 || strcmp(prev_wd, "\\write") == 0
)
- matches = completion_matches(text, filename_completion_function);
+ {
+ completion_charp = "\\";
+ matches = completion_matches(text, complete_from_files);
+ }
/*
* Finally, we look through the list of "things", such as TABLE, INDEX and
}
+/*
+ * This function wraps rl_filename_completion_function() to strip quotes from
+ * the input before searching for matches and to quote any matches for which
+ * the consuming command will require it.
+ */
+static char *
+complete_from_files(const char *text, int state)
+{
+ static const char *unquoted_text;
+ char *unquoted_match;
+ char *ret = NULL;
+
+ if (state == 0)
+ {
+ /* Initialization: stash the unquoted input. */
+ unquoted_text = strtokx(text, "", NULL, "'", *completion_charp,
+ false, true, pset.encoding);
+ /* expect a NULL return for the empty string only */
+ if (!unquoted_text)
+ {
+ psql_assert(!*text);
+ unquoted_text = text;
+ }
+ }
+
+ unquoted_match = filename_completion_function(unquoted_text, state);
+ if (unquoted_match)
+ {
+ /*
+ * Caller sets completion_charp to a zero- or one-character string
+ * containing the escape character. This is necessary since \copy has
+ * no escape character, but every other backslash command recognizes
+ * "\" as an escape character. Since we have only two callers, don't
+ * bother providing a macro to simplify this.
+ */
+ ret = quote_if_needed(unquoted_match, " \t\r\n\"`",
+ '\'', *completion_charp, pset.encoding);
+ if (ret)
+ free(unquoted_match);
+ else
+ ret = unquoted_match;
+ }
+
+ return ret;
+}
+
+
/* HELPER FUNCTIONS */