sh: fix autocompletion for commands that share name with a directory
Provide libedit a special function making it always add a space after the autocompleted command. The default one adds a slash if the word is also a name of a directory in the current working directory, but this is wrong for commands. Reviewed by: bapt, jilles Differential Revision: https://reviews.freebsd.org/D34544
This commit is contained in:
parent
7ed8e142a0
commit
68700941c7
@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <paths.h>
|
#include <paths.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -73,12 +74,14 @@ EditLine *el; /* editline cookie */
|
|||||||
int displayhist;
|
int displayhist;
|
||||||
static int savehist;
|
static int savehist;
|
||||||
static FILE *el_in, *el_out;
|
static FILE *el_in, *el_out;
|
||||||
|
static bool in_command_completion;
|
||||||
|
|
||||||
static char *fc_replace(const char *, char *, char *);
|
static char *fc_replace(const char *, char *, char *);
|
||||||
static int not_fcnumber(const char *);
|
static int not_fcnumber(const char *);
|
||||||
static int str_to_event(const char *, int);
|
static int str_to_event(const char *, int);
|
||||||
static int comparator(const void *, const void *, void *);
|
static int comparator(const void *, const void *, void *);
|
||||||
static char **sh_matches(const char *, int, int);
|
static char **sh_matches(const char *, int, int);
|
||||||
|
static const char *append_char_function(const char *);
|
||||||
static unsigned char sh_complete(EditLine *, int);
|
static unsigned char sh_complete(EditLine *, int);
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
@ -602,8 +605,10 @@ static char
|
|||||||
size_t i = 0, size = 16, uniq;
|
size_t i = 0, size = 16, uniq;
|
||||||
size_t curpos = end - start, lcstring = -1;
|
size_t curpos = end - start, lcstring = -1;
|
||||||
|
|
||||||
|
in_command_completion = false;
|
||||||
if (start > 0 || memchr("/.~", text[0], 3) != NULL)
|
if (start > 0 || memchr("/.~", text[0], 3) != NULL)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
in_command_completion = true;
|
||||||
if ((free_path = path = strdup(pathval())) == NULL)
|
if ((free_path = path = strdup(pathval())) == NULL)
|
||||||
goto out;
|
goto out;
|
||||||
if ((matches = malloc(size * sizeof(matches[0]))) == NULL)
|
if ((matches = malloc(size * sizeof(matches[0]))) == NULL)
|
||||||
@ -696,6 +701,32 @@ static char
|
|||||||
return (matches);
|
return (matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we don't specify this function as app_func in the call to fn_complete2,
|
||||||
|
* libedit will use the default one, which adds a " " to plain files and
|
||||||
|
* a "/" to directories regardless of whether it's a command name or a plain
|
||||||
|
* path (relative or absolute). We never want to add "/" to commands.
|
||||||
|
*
|
||||||
|
* For example, after I did "mkdir rmdir", "rmdi" would be autocompleted to
|
||||||
|
* "rmdir/" instead of "rmdir ".
|
||||||
|
*/
|
||||||
|
static const char *
|
||||||
|
append_char_function(const char *name)
|
||||||
|
{
|
||||||
|
struct stat stbuf;
|
||||||
|
char *expname = name[0] == '~' ? fn_tilde_expand(name) : NULL;
|
||||||
|
const char *rs;
|
||||||
|
|
||||||
|
if (!in_command_completion &&
|
||||||
|
stat(expname ? expname : name, &stbuf) == 0 &&
|
||||||
|
S_ISDIR(stbuf.st_mode))
|
||||||
|
rs = "/";
|
||||||
|
else
|
||||||
|
rs = " ";
|
||||||
|
free(expname);
|
||||||
|
return (rs);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is passed to el_set(el, EL_ADDFN, ...) so that it's possible to
|
* This is passed to el_set(el, EL_ADDFN, ...) so that it's possible to
|
||||||
* bind a key (tab by default) to execute the function.
|
* bind a key (tab by default) to execute the function.
|
||||||
@ -704,8 +735,8 @@ unsigned char
|
|||||||
sh_complete(EditLine *sel, int ch __unused)
|
sh_complete(EditLine *sel, int ch __unused)
|
||||||
{
|
{
|
||||||
return (unsigned char)fn_complete2(sel, NULL, sh_matches,
|
return (unsigned char)fn_complete2(sel, NULL, sh_matches,
|
||||||
L" \t\n\"\\'`@$><=;|&{(", NULL, NULL, (size_t)100,
|
L" \t\n\"\\'`@$><=;|&{(", NULL, append_char_function,
|
||||||
NULL, &((int) {0}), NULL, NULL, FN_QUOTE_MATCH);
|
(size_t)100, NULL, &((int) {0}), NULL, NULL, FN_QUOTE_MATCH);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
Loading…
Reference in New Issue
Block a user