commit 68fc1605e7d3a317477abf1afc6410581724b72d
parent 8ecd9704f9564950d8ea80c0fcff04b8861b37e4
Author: Bert Münnich <ber.t@posteo.de>
Date: Sat, 5 Dec 2015 17:34:49 +0100
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.
Diffstat:
4 files changed, 124 insertions(+), 0 deletions(-)
diff --git a/builtins.c b/builtins.c
@@ -636,3 +636,8 @@ static void b_limit(char **av) {
}
}
#endif
+
+extern char *compl_builtin(const char *text, int state) {
+ return compl_name(text, state, &builtins[0].name, arraysize(builtins), &builtins[1].name - &builtins[0].name);
+}
+
diff --git a/edit-readline.c b/edit-readline.c
@@ -2,6 +2,8 @@
#include <errno.h>
#include <stdio.h>
+#include <dirent.h>
+#include <sys/types.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <readline/rltypedefs.h>
@@ -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 !#$&'()*;<=>?@[\\]^`{|}~";
diff --git a/hash.c b/hash.c
@@ -316,3 +316,28 @@ extern void whatare_all_vars(bool showfn, bool showvar) {
if (fp[i].name != NULL && fp[i].name != dead)
prettyprint_fn(1, fp[i].name, fnlookup(fp[i].name));
}
+
+extern char *compl_name(const char *text, int state, char **p, size_t count, ssize_t inc) {
+ static char **n;
+ static size_t i, len;
+ char *name;
+
+ if (!state) {
+ n = p;
+ i = 0;
+ len = strlen(text);
+ }
+ for (name = NULL; name == NULL && i < count; i++, n += inc)
+ if (*n != NULL && strncmp(*n, text, len) == 0)
+ name = strdup(*n);
+ return name;
+}
+
+extern char *compl_fn(const char *text, int state) {
+ return compl_name(text, state, &fp[0].name, fsize, &fp[1].name - &fp[0].name);
+}
+
+extern char *compl_var(const char *text, int state) {
+ return compl_name(text, state, &vp[0].name, vsize, &vp[1].name - &vp[0].name);
+}
+
diff --git a/rc.h b/rc.h
@@ -181,6 +181,7 @@ extern int lineno;
extern builtin_t *isbuiltin(char *);
extern void b_exec(char **), funcall(char **), b_dot(char **), b_builtin(char **);
extern char *which(char *, bool);
+extern char *compl_builtin(const char *, int);
/* except.c */
extern bool nl_on_intr;
@@ -259,6 +260,9 @@ extern void whatare_all_vars(bool, bool);
extern void whatare_all_signals(void);
extern void prettyprint_var(int, char *, List *);
extern void prettyprint_fn(int, char *, Node *);
+extern char *compl_name(const char *, int, char **, size_t, ssize_t);
+extern char *compl_fn(const char *, int);
+extern char *compl_var(const char *, int);
/* heredoc.c */
extern int heredoc(int);