Port the el_fn_sh_complete from FreeBSD. This function is required by FreeBSD-9.0's sh. So that we can drop the bundled version in FreeBSD-9.0 diff -uNr libedit-20120311-3.0/src/filecomplete.c libedit-20120311-3.0.freebsd/src/filecomplete.c --- libedit-20120311-3.0/src/filecomplete.c 2012-03-11 09:54:58.000000000 +0000 +++ libedit-20120311-3.0.freebsd/src/filecomplete.c 2012-04-23 12:14:25.000000000 +0000 @@ -56,6 +56,9 @@ static const Char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@', '$', '>', '<', '=', ';', '|', '&', '{', '(', '\0' }; +/* Tilde is deliberately omitted here, we treat it specially. */ +static const Char extra_quote_chars[] = { ')', '}', '*', '?', '[', '$', '\0' }; + /********************************/ @@ -411,10 +414,14 @@ 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 TYPE(LineInfo) *li; Char *temp; + char *dequoted_temp; char **matches; const Char *ctemp; size_t len; @@ -435,17 +442,28 @@ /* We now look backwards for the start of a filename/variable word */ li = FUN(el,line)(el); + if (find_word_start_func) + ctemp = ct_decode_string(find_word_start_func(ct_encode_string(li->buffer,&el->el_scratch), ct_encode_string(li->cursor,&el->el_scratch)),&el->el_scratch); + else { ctemp = li->cursor; while (ctemp > li->buffer && !Strchr(word_break, ctemp[-1]) && (!special_prefixes || !Strchr(special_prefixes, ctemp[-1]) ) ) ctemp--; + } len = (size_t)(li->cursor - ctemp); temp = el_malloc((len + 1) * sizeof(*temp)); (void)Strncpy(temp, ctemp, len); temp[len] = '\0'; + if (dequoting_func) { + dequoted_temp = dequoting_func(ct_encode_string(temp,&el->el_scratch)); + 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) @@ -456,14 +474,14 @@ if (attempted_completion_function) { int cur_off = (int)(li->cursor - li->buffer); matches = (*attempted_completion_function)( - ct_encode_string(temp, &el->el_scratch), + dequoted_temp? dequoted_temp : ct_encode_string(temp, &el->el_scratch), cur_off - (int)len, cur_off); } else matches = 0; if (!attempted_completion_function || (over != NULL && !*over && !matches)) matches = completion_matches( - ct_encode_string(temp, &el->el_scratch), complet_func); + dequoted_temp? dequoted_temp : ct_encode_string(temp, &el->el_scratch), complet_func); if (over != NULL) *over = 0; @@ -478,9 +496,19 @@ * 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); FUN(el,insertstr)(el, - ct_decode_string(matches[0], &el->el_scratch)); + ct_decode_string(quoted_match? quoted_match : matches[0], &el->el_scratch)); + + free(quoted_match); } if (what_to_do == '?') @@ -553,12 +581,14 @@ retval = CC_NORM; } +free_matches: /* free elements of array and the array itself */ for (i = 0; matches[i]; i++) el_free(matches[i]); el_free(matches); matches = NULL; } + el_free(dequoted_temp); el_free(temp); return retval; } @@ -572,5 +602,102 @@ { return (unsigned char)fn_complete(el, NULL, NULL, break_chars, NULL, NULL, (size_t)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; + + if (*str == '-' || *str == '+') + extra_len += 2; + 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; + if (*str == '-' || *str == '+') + *dst++ = '.', *dst++ = '/'; + 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 -uNr libedit-20120311-3.0/src/filecomplete.h libedit-20120311-3.0.freebsd/src/filecomplete.h --- libedit-20120311-3.0/src/filecomplete.h 2010-04-22 19:13:17.000000000 +0000 +++ libedit-20120311-3.0.freebsd/src/filecomplete.h 2012-04-23 12:04:12.000000000 +0000 @@ -35,7 +35,10 @@ 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 *); Binary files libedit-20120311-3.0/src/filecomplete.o and libedit-20120311-3.0.freebsd/src/filecomplete.o differ diff -uNr libedit-20120311-3.0/src/histedit.h libedit-20120311-3.0.freebsd/src/histedit.h --- libedit-20120311-3.0/src/histedit.h 2011-08-02 06:52:08.000000000 +0000 +++ libedit-20120311-3.0.freebsd/src/histedit.h 2012-04-23 11:44:33.000000000 +0000 @@ -113,6 +113,7 @@ int el_set(EditLine *, int, ...); int el_get(EditLine *, int, ...); unsigned char _el_fn_complete(EditLine *, int); +unsigned char _el_fn_sh_complete(EditLine *, int); /* * el_set/el_get parameters diff -ur libedit-20120311-3.0/src/readline.c libedit-20120311-3.0.freebsd/src/readline.c --- libedit-20120311-3.0/src/readline.c 2012-03-11 09:54:58.000000000 +0000 +++ libedit-20120311-3.0.freebsd/src/readline.c 2012-04-23 12:20:11.000000000 +0000 @@ -1773,7 +1773,7 @@ _rl_completion_append_character_function, (size_t)rl_completion_query_items, &rl_completion_type, &rl_attempted_completion_over, - &rl_point, &rl_end); + &rl_point, &rl_end, NULL, NULL, NULL); }