diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index b0b318f426fb0db576e1264ec5e354522d89a965..3854f7f421fc853a60ce538f6e3f2f670f454021 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -132,6 +132,7 @@ static const char *const * completion_charpp; /* to pass a list of strings */ static const char *completion_info_charp; /* to pass a second string */ static const char *completion_info_charp2; /* to pass a third string */ static const SchemaQuery *completion_squery; /* to pass a SchemaQuery */ +static bool completion_case_sensitive; /* completion is case sensitive */ /* * A few macros to ease typing. You can use these to complete the given @@ -155,15 +156,24 @@ do { \ matches = completion_matches(text, complete_from_schema_query); \ } while (0) +#define COMPLETE_WITH_LIST_CS(list) \ +do { \ + completion_charpp = list; \ + completion_case_sensitive = true; \ + matches = completion_matches(text, complete_from_list); \ +} while (0) + #define COMPLETE_WITH_LIST(list) \ do { \ completion_charpp = list; \ + completion_case_sensitive = false; \ matches = completion_matches(text, complete_from_list); \ } while (0) #define COMPLETE_WITH_CONST(string) \ do { \ completion_charp = string; \ + completion_case_sensitive = false; \ matches = completion_matches(text, complete_from_const); \ } while (0) @@ -671,6 +681,7 @@ 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 *pg_strdup_same_case(const char *s, const char *ref); static PGresult *exec_query(const char *query); static void get_previous_words(int point, char **previous_words, int nwords); @@ -771,7 +782,7 @@ psql_completion(char *text, int start, int end) /* If a backslash command was started, continue */ if (text[0] == '\\') - COMPLETE_WITH_LIST(backslash_commands); + COMPLETE_WITH_LIST_CS(backslash_commands); /* Variable interpolation */ else if (text[0] == ':' && text[1] != ':') @@ -2907,7 +2918,7 @@ psql_completion(char *text, int start, int end) "null", "fieldsep", "tuples_only", "title", "tableattr", "linestyle", "pager", "recordsep", NULL}; - COMPLETE_WITH_LIST(my_list); + COMPLETE_WITH_LIST_CS(my_list); } else if (strcmp(prev2_wd, "\\pset") == 0) { @@ -2917,14 +2928,14 @@ psql_completion(char *text, int start, int end) {"unaligned", "aligned", "wrapped", "html", "latex", "troff-ms", NULL}; - COMPLETE_WITH_LIST(my_list); + COMPLETE_WITH_LIST_CS(my_list); } else if (strcmp(prev_wd, "linestyle") == 0) { static const char *const my_list[] = {"ascii", "old-ascii", "unicode", NULL}; - COMPLETE_WITH_LIST(my_list); + COMPLETE_WITH_LIST_CS(my_list); } } else if (strcmp(prev_wd, "\\set") == 0) @@ -3030,7 +3041,7 @@ create_or_drop_command_generator(const char *text, int state, bits32 excluded) { if ((pg_strncasecmp(name, text, string_length) == 0) && !(words_after_create[list_index - 1].flags & excluded)) - return pg_strdup(name); + return pg_strdup_same_case(name, text); } /* if nothing matches, return NULL */ return NULL; @@ -3298,7 +3309,7 @@ complete_from_list(const char *text, int state) { list_index = 0; string_length = strlen(text); - casesensitive = true; + casesensitive = completion_case_sensitive; matches = 0; } @@ -3313,7 +3324,14 @@ complete_from_list(const char *text, int state) /* Second pass is case insensitive, don't bother counting matches */ if (!casesensitive && pg_strncasecmp(text, item, string_length) == 0) - return pg_strdup(item); + { + if (completion_case_sensitive) + return pg_strdup(item); + else + /* If case insensitive matching was requested initially, return + * it in the case of what was already entered. */ + return pg_strdup_same_case(item, text); + } } /* @@ -3343,12 +3361,16 @@ complete_from_list(const char *text, int state) static char * complete_from_const(const char *text, int state) { - (void) text; /* We don't care about what was entered - * already. */ - psql_assert(completion_charp); if (state == 0) - return pg_strdup(completion_charp); + { + if (completion_case_sensitive) + return pg_strdup(completion_charp); + else + /* If case insensitive matching was requested initially, return it + * in the case of what was already entered. */ + return pg_strdup_same_case(completion_charp, text); + } else return NULL; } @@ -3394,7 +3416,7 @@ complete_from_variables(char *text, const char *prefix, const char *suffix) } varnames[nvars] = NULL; - COMPLETE_WITH_LIST((const char * const *) varnames); + COMPLETE_WITH_LIST_CS((const char * const *) varnames); for (i = 0; i < nvars; i++) free(varnames[i]); @@ -3407,6 +3429,31 @@ complete_from_variables(char *text, const char *prefix, const char *suffix) /* HELPER FUNCTIONS */ +/* + * Make a pg_strdup copy of s and convert it to the same case as ref. + */ +static char * +pg_strdup_same_case(const char *s, const char *ref) +{ + char *ret, *p; + unsigned char first = ref[0]; + + if (isalpha(first)) + { + ret = pg_strdup(s); + if (islower(first)) + for (p = ret; *p; p++) + *p = pg_tolower((unsigned char) *p); + else + for (p = ret; *p; p++) + *p = pg_toupper((unsigned char) *p); + return ret; + } + else + return pg_strdup(s); +} + + /* * Execute a query and report any errors. This should be the preferred way of * talking to the database in this file.