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
This commit is contained in:
parent
0dee704f01
commit
3d0f8bcd30
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=209219
@ -106,6 +106,7 @@ int el_parse(EditLine *, int, const char **);
|
|||||||
int el_set(EditLine *, int, ...);
|
int el_set(EditLine *, int, ...);
|
||||||
int el_get(EditLine *, int, ...);
|
int el_get(EditLine *, int, ...);
|
||||||
unsigned char _el_fn_complete(EditLine *, int);
|
unsigned char _el_fn_complete(EditLine *, int);
|
||||||
|
unsigned char _el_fn_sh_complete(EditLine *, int);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* el_set/el_get parameters
|
* el_set/el_get parameters
|
||||||
|
@ -52,6 +52,8 @@ __FBSDID("$FreeBSD$");
|
|||||||
|
|
||||||
static char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@',
|
static char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@',
|
||||||
'$', '>', '<', '=', ';', '|', '&', '{', '(', '\0' };
|
'$', '>', '<', '=', ';', '|', '&', '{', '(', '\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),
|
char **(*attempted_completion_function)(const char *, int, int),
|
||||||
const char *word_break, const char *special_prefixes,
|
const char *word_break, const char *special_prefixes,
|
||||||
const char *(*app_func)(const char *), size_t query_items,
|
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;
|
const LineInfo *li;
|
||||||
char *temp;
|
char *temp;
|
||||||
|
char *dequoted_temp;
|
||||||
char **matches;
|
char **matches;
|
||||||
const char *ctemp;
|
const char *ctemp;
|
||||||
size_t len;
|
size_t len;
|
||||||
@ -404,11 +410,15 @@ fn_complete(EditLine *el,
|
|||||||
|
|
||||||
/* We now look backwards for the start of a filename/variable word */
|
/* We now look backwards for the start of a filename/variable word */
|
||||||
li = el_line(el);
|
li = el_line(el);
|
||||||
|
if (find_word_start_func)
|
||||||
|
ctemp = find_word_start_func(li->buffer, li->cursor);
|
||||||
|
else {
|
||||||
ctemp = li->cursor;
|
ctemp = li->cursor;
|
||||||
while (ctemp > li->buffer
|
while (ctemp > li->buffer
|
||||||
&& !strchr(word_break, ctemp[-1])
|
&& !strchr(word_break, ctemp[-1])
|
||||||
&& (!special_prefixes || !strchr(special_prefixes, ctemp[-1]) ) )
|
&& (!special_prefixes || !strchr(special_prefixes, ctemp[-1]) ) )
|
||||||
ctemp--;
|
ctemp--;
|
||||||
|
}
|
||||||
|
|
||||||
len = li->cursor - ctemp;
|
len = li->cursor - ctemp;
|
||||||
#if defined(__SSP__) || defined(__SSP_ALL__)
|
#if defined(__SSP__) || defined(__SSP_ALL__)
|
||||||
@ -421,6 +431,13 @@ fn_complete(EditLine *el,
|
|||||||
(void)strncpy(temp, ctemp, len);
|
(void)strncpy(temp, ctemp, len);
|
||||||
temp[len] = '\0';
|
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() */
|
/* these can be used by function called in completion_matches() */
|
||||||
/* or (*attempted_completion_function)() */
|
/* or (*attempted_completion_function)() */
|
||||||
if (point != 0)
|
if (point != 0)
|
||||||
@ -430,13 +447,13 @@ fn_complete(EditLine *el,
|
|||||||
|
|
||||||
if (attempted_completion_function) {
|
if (attempted_completion_function) {
|
||||||
int cur_off = (int)(li->cursor - li->buffer);
|
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);
|
(int)(cur_off - len), cur_off);
|
||||||
} else
|
} else
|
||||||
matches = 0;
|
matches = 0;
|
||||||
if (!attempted_completion_function ||
|
if (!attempted_completion_function ||
|
||||||
(over != NULL && !*over && !matches))
|
(over != NULL && !*over && !matches))
|
||||||
matches = completion_matches(temp, complet_func);
|
matches = completion_matches(dequoted_temp ? dequoted_temp : temp, complet_func);
|
||||||
|
|
||||||
if (over != NULL)
|
if (over != NULL)
|
||||||
*over = 0;
|
*over = 0;
|
||||||
@ -451,8 +468,18 @@ fn_complete(EditLine *el,
|
|||||||
* possible matches if there is possible completion.
|
* possible matches if there is possible completion.
|
||||||
*/
|
*/
|
||||||
if (matches[0][0] != '\0') {
|
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_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 == '?')
|
if (what_to_do == '?')
|
||||||
@ -514,12 +541,14 @@ fn_complete(EditLine *el,
|
|||||||
retval = CC_NORM;
|
retval = CC_NORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free_matches:
|
||||||
/* free elements of array and the array itself */
|
/* free elements of array and the array itself */
|
||||||
for (i = 0; matches[i]; i++)
|
for (i = 0; matches[i]; i++)
|
||||||
free(matches[i]);
|
free(matches[i]);
|
||||||
free(matches);
|
free(matches);
|
||||||
matches = NULL;
|
matches = NULL;
|
||||||
}
|
}
|
||||||
|
free(dequoted_temp);
|
||||||
#if defined(__SSP__) || defined(__SSP_ALL__)
|
#if defined(__SSP__) || defined(__SSP_ALL__)
|
||||||
free(temp);
|
free(temp);
|
||||||
#endif
|
#endif
|
||||||
@ -536,5 +565,99 @@ _el_fn_complete(EditLine *el, int ch __attribute__((__unused__)))
|
|||||||
{
|
{
|
||||||
return (unsigned char)fn_complete(el, NULL, NULL,
|
return (unsigned char)fn_complete(el, NULL, NULL,
|
||||||
break_chars, NULL, NULL, 100,
|
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);
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,10 @@ int fn_complete(EditLine *,
|
|||||||
char *(*)(const char *, int),
|
char *(*)(const char *, int),
|
||||||
char **(*)(const char *, int, int),
|
char **(*)(const char *, int, int),
|
||||||
const char *, const char *, const char *(*)(const char *), size_t,
|
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);
|
void fn_display_match_list(EditLine *, char **, size_t, size_t);
|
||||||
char *fn_tilde_expand(const char *);
|
char *fn_tilde_expand(const char *);
|
||||||
|
Loading…
Reference in New Issue
Block a user