summaryrefslogtreecommitdiffstats
path: root/lib/libedit
diff options
context:
space:
mode:
authorjilles <jilles@FreeBSD.org>2010-06-15 21:34:57 +0000
committerjilles <jilles@FreeBSD.org>2010-06-15 21:34:57 +0000
commit1babf0ea165658db31c411e8e666ba5453b8e1f0 (patch)
treea84dac06f08aa076cd8d1ae683ae34e3228efa78 /lib/libedit
parent257c4e7afba0fb7f97f82475ee329d3f98179c91 (diff)
downloadFreeBSD-src-1babf0ea165658db31c411e8e666ba5453b8e1f0.zip
FreeBSD-src-1babf0ea165658db31c411e8e666ba5453b8e1f0.tar.gz
libedit: Allow simple quoting in filename completion.
The completer recognizes characters escaped with backslashes as being literal parts of a word, and adds backslashes to avoid almost all misinterpretation. In particular, filenames containing spaces can be completed correctly. For bug compatibility with the NetBSD version, the improved completion function has a new name, _el_fn_sh_complete, and _el_fn_complete is unchanged. Submitted by: Guy Yur
Diffstat (limited to 'lib/libedit')
-rw-r--r--lib/libedit/filecomplete.c143
-rw-r--r--lib/libedit/filecomplete.h5
2 files changed, 137 insertions, 11 deletions
diff --git a/lib/libedit/filecomplete.c b/lib/libedit/filecomplete.c
index ce0db10..97ba2d0 100644
--- a/lib/libedit/filecomplete.c
+++ b/lib/libedit/filecomplete.c
@@ -52,6 +52,8 @@ __FBSDID("$FreeBSD$");
static char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@',
'$', '>', '<', '=', ';', '|', '&', '{', '(', '\0' };
+/* Tilde is deliberately omitted here, we treat it specially. */
+static char extra_quote_chars[] = { ')', '}', '\0' };
/********************************/
@@ -380,10 +382,14 @@ fn_complete(EditLine *el,
char **(*attempted_completion_function)(const char *, int, int),
const char *word_break, const char *special_prefixes,
const char *(*app_func)(const char *), size_t query_items,
- int *completion_type, int *over, int *point, int *end)
+ int *completion_type, int *over, int *point, int *end,
+ const char *(*find_word_start_func)(const char *, const char *),
+ char *(*dequoting_func)(const char *),
+ char *(*quoting_func)(const char *))
{
const LineInfo *li;
char *temp;
+ char *dequoted_temp;
char **matches;
const char *ctemp;
size_t len;
@@ -404,11 +410,15 @@ fn_complete(EditLine *el,
/* We now look backwards for the start of a filename/variable word */
li = el_line(el);
- ctemp = li->cursor;
- while (ctemp > li->buffer
- && !strchr(word_break, ctemp[-1])
- && (!special_prefixes || !strchr(special_prefixes, ctemp[-1]) ) )
- ctemp--;
+ if (find_word_start_func)
+ ctemp = find_word_start_func(li->buffer, li->cursor);
+ else {
+ ctemp = li->cursor;
+ while (ctemp > li->buffer
+ && !strchr(word_break, ctemp[-1])
+ && (!special_prefixes || !strchr(special_prefixes, ctemp[-1]) ) )
+ ctemp--;
+ }
len = li->cursor - ctemp;
#if defined(__SSP__) || defined(__SSP_ALL__)
@@ -421,6 +431,13 @@ fn_complete(EditLine *el,
(void)strncpy(temp, ctemp, len);
temp[len] = '\0';
+ if (dequoting_func) {
+ dequoted_temp = dequoting_func(temp);
+ if (dequoted_temp == NULL)
+ return retval;
+ } else
+ dequoted_temp = NULL;
+
/* these can be used by function called in completion_matches() */
/* or (*attempted_completion_function)() */
if (point != 0)
@@ -430,13 +447,13 @@ fn_complete(EditLine *el,
if (attempted_completion_function) {
int cur_off = (int)(li->cursor - li->buffer);
- matches = (*attempted_completion_function) (temp,
+ matches = (*attempted_completion_function) (dequoted_temp ? dequoted_temp : temp,
(int)(cur_off - len), cur_off);
} else
matches = 0;
if (!attempted_completion_function ||
(over != NULL && !*over && !matches))
- matches = completion_matches(temp, complet_func);
+ matches = completion_matches(dequoted_temp ? dequoted_temp : temp, complet_func);
if (over != NULL)
*over = 0;
@@ -451,8 +468,18 @@ fn_complete(EditLine *el,
* possible matches if there is possible completion.
*/
if (matches[0][0] != '\0') {
+ char *quoted_match;
+ if (quoting_func) {
+ quoted_match = quoting_func(matches[0]);
+ if (quoted_match == NULL)
+ goto free_matches;
+ } else
+ quoted_match = NULL;
+
el_deletestr(el, (int) len);
- el_insertstr(el, matches[0]);
+ el_insertstr(el, quoted_match ? quoted_match : matches[0]);
+
+ free(quoted_match);
}
if (what_to_do == '?')
@@ -514,12 +541,14 @@ fn_complete(EditLine *el,
retval = CC_NORM;
}
+free_matches:
/* free elements of array and the array itself */
for (i = 0; matches[i]; i++)
free(matches[i]);
free(matches);
matches = NULL;
}
+ free(dequoted_temp);
#if defined(__SSP__) || defined(__SSP_ALL__)
free(temp);
#endif
@@ -536,5 +565,99 @@ _el_fn_complete(EditLine *el, int ch __attribute__((__unused__)))
{
return (unsigned char)fn_complete(el, NULL, NULL,
break_chars, NULL, NULL, 100,
- NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL);
+}
+
+
+static const char *
+sh_find_word_start(const char *buffer, const char *cursor)
+{
+ const char *word_start = buffer;
+
+ while (buffer < cursor) {
+ if (*buffer == '\\')
+ buffer++;
+ else if (strchr(break_chars, *buffer))
+ word_start = buffer + 1;
+
+ buffer++;
+ }
+
+ return word_start;
+}
+
+
+static char *
+sh_quote(const char *str)
+{
+ const char *src;
+ int extra_len = 0;
+ char *quoted_str, *dst;
+
+ for (src = str; *src != '\0'; src++)
+ if (strchr(break_chars, *src) ||
+ strchr(extra_quote_chars, *src))
+ extra_len++;
+
+ quoted_str = malloc(sizeof(*quoted_str) *
+ (strlen(str) + extra_len + 1));
+ if (quoted_str == NULL)
+ return NULL;
+
+ dst = quoted_str;
+ for (src = str; *src != '\0'; src++) {
+ if (strchr(break_chars, *src) ||
+ strchr(extra_quote_chars, *src))
+ *dst++ = '\\';
+ *dst++ = *src;
+ }
+ *dst = '\0';
+
+ return quoted_str;
+}
+
+
+static char *
+sh_dequote(const char *str)
+{
+ char *dequoted_str, *dst;
+
+ /* save extra space to replace \~ with ./~ */
+ dequoted_str = malloc(sizeof(*dequoted_str) * (strlen(str) + 1 + 1));
+ if (dequoted_str == NULL)
+ return NULL;
+
+ dst = dequoted_str;
+
+ /* dequote \~ at start as ./~ */
+ if (*str == '\\' && str[1] == '~') {
+ str++;
+ *dst++ = '.';
+ *dst++ = '/';
+ }
+
+ while (*str) {
+ if (*str == '\\')
+ str++;
+ if (*str)
+ *dst++ = *str++;
+ }
+ *dst = '\0';
+
+ return dequoted_str;
+}
+
+
+/*
+ * completion function using sh quoting rules; for key binding
+ */
+/* ARGSUSED */
+unsigned char
+_el_fn_sh_complete(EditLine *el, int ch __attribute__((__unused__)))
+{
+ return (unsigned char)fn_complete(el, NULL, NULL,
+ break_chars, NULL, NULL, 100,
+ NULL, NULL, NULL, NULL,
+ sh_find_word_start, sh_dequote, sh_quote);
}
diff --git a/lib/libedit/filecomplete.h b/lib/libedit/filecomplete.h
index 0b01376..a3156e9 100644
--- a/lib/libedit/filecomplete.h
+++ b/lib/libedit/filecomplete.h
@@ -36,7 +36,10 @@ int fn_complete(EditLine *,
char *(*)(const char *, int),
char **(*)(const char *, int, int),
const char *, const char *, const char *(*)(const char *), size_t,
- int *, int *, int *, int *);
+ int *, int *, int *, int *,
+ const char *(*)(const char *, const char *),
+ char *(*)(const char *),
+ char *(*)(const char *));
void fn_display_match_list(EditLine *, char **, size_t, size_t);
char *fn_tilde_expand(const char *);
OpenPOWER on IntegriCloud