From 68fc1605e7d3a317477abf1afc6410581724b72d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bert=20M=C3=BCnnich?= Date: Sat, 5 Dec 2015 17:34:49 +0100 Subject: Completion of command names and variables New completion entry functions for readline specific code: - compl_var for variables - compl_fn for functions - compl_builtin for builtins - compl_extcmd for external commands in path - compl_command, which combines the last three The first three are simple wrappers for compl_name, which iterates over a typical rc table (an array of structs) yielding the name fields. The appropriate completion entry function is chosen based on the character/token that preceeds the word to complete: '$' for variable, beginning of line or one of [`@|&(){;] for command. Otherwise the shell falls back to readline's default completion, which it also does, if the chosen completion entry function finds no match. --- edit-readline.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) (limited to 'edit-readline.c') diff --git a/edit-readline.c b/edit-readline.c index acf96aa..9737e3e 100644 --- a/edit-readline.c +++ b/edit-readline.c @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include #include @@ -14,11 +16,99 @@ struct cookie { char *buffer; }; +static char *compl_extcmd(const char *text, int state) { + static DIR *d; + static List *path; + static size_t len; + char *name = NULL; + + if (!state) { + d = NULL; + path = varlookup("path"); + len = strlen(text); + } +nextdir: + while (d == NULL) { + if (path == NULL) + return NULL; + d = opendir(path->w); + path = path->n; + } + while (name == NULL) { + struct dirent *e = readdir(d); + if (e == NULL) { + closedir(d); + d = NULL; + goto nextdir; + } + if (strncmp(e->d_name, text, len) == 0) + name = strdup(e->d_name); + } + return name; +} + +static rl_compentry_func_t *const compl_cmd_funcs[] = { + compl_builtin, + compl_fn, + compl_extcmd +}; + +static char *compl_command(const char *text, int state) { + static size_t i; + static int s; + char *name = NULL; + + if (!state) { + i = 0; + s = 0; + } + while (name == NULL && i < arraysize(compl_cmd_funcs)) { + name = compl_cmd_funcs[i](text, s); + if (name != NULL) { + s = 1; + } else { + i++; + s = 0; + } + } + return name; +} + +static rl_compentry_func_t *compl_func(const char *text, int start, int end) { + int quote = FALSE; + char last = ';', *s, *t; + + for (s = &rl_line_buffer[0], t = &rl_line_buffer[start]; s < t; s++) { + if (!quote && *s != ' ' && *s != '\t') + last = *s; + if (*s == '\'') + quote = !quote; + } + switch (last) { + case '`': case '@': case '|': case '&': + case '(': case ')': case '{': case ';': + return compl_command; + case '$': + return compl_var; + } + return NULL; +} + +static char **rc_completion(const char *text, int start, int end) { + rl_compentry_func_t *func = compl_func(text, start, end); + + if (func != NULL) + return rl_completion_matches(text, func); + else + return NULL; +} + void *edit_begin(int fd) { List *hist; struct cookie *c; rl_initialize(); + rl_attempted_completion_function = rc_completion; rl_catch_signals = 0; rl_completer_quote_characters = "'"; rl_filename_quote_characters = "\t\n !#$&'()*;<=>?@[\\]^`{|}~"; -- cgit v1.2.3