rc

[fork] interactive rc shell
Log | Files | Refs | README | LICENSE

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:
Mbuiltins.c | 5+++++
Medit-readline.c | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mhash.c | 25+++++++++++++++++++++++++
Mrc.h | 4++++
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);