diff options
-rw-r--r-- | common/Kconfig | 1 | ||||
-rw-r--r-- | common/Makefile | 1 | ||||
-rw-r--r-- | common/command.c | 286 | ||||
-rw-r--r-- | common/complete.c | 210 | ||||
-rw-r--r-- | include/complete.h | 15 | ||||
-rw-r--r-- | lib/readline.c | 23 | ||||
-rw-r--r-- | lib/readline_simple.c | 10 |
7 files changed, 252 insertions, 294 deletions
diff --git a/common/Kconfig b/common/Kconfig index 14dbb90fe2..7fcf7f1b4f 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -81,6 +81,7 @@ config CMDLINE_EDITING config AUTO_COMPLETE bool + depends on CMDLINE_EDITING prompt "Enable auto completion" config SIMPLE_READLINE diff --git a/common/Makefile b/common/Makefile index 25128b1653..7aeba86c57 100644 --- a/common/Makefile +++ b/common/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_OF_FLAT_TREE) += ft_build.o obj-$(CONFIG_MODULE) += module.o obj-$(CONFIG_KALLSYMS) += kallsyms.o obj-$(CONFIG_ENV_HANDLING) += environment.o +obj-$(CONFIG_AUTO_COMPLETE) += complete.o obj-y += dlmalloc.o obj-y += clock.o diff --git a/common/command.c b/common/command.c index fdd7444e6d..94a8f28787 100644 --- a/common/command.c +++ b/common/command.c @@ -32,6 +32,7 @@ #include <environment.h> #include <list.h> #include <init.h> +#include <complete.h> LIST_HEAD(command_list); EXPORT_SYMBOL(command_list); @@ -176,10 +177,6 @@ int register_command(cmd_tbl_t *cmd) debug("register command %s\n", cmd->name); - /* - * Would be nice to have some kind of list_add_sort - * to keep the command list in order - */ list_add_sort(&cmd->list, &command_list, compare); if (cmd->aliases) { @@ -256,284 +253,3 @@ static int init_command_list(void) late_initcall(init_command_list); -#ifdef CONFIG_AUTO_COMPLETE - -int var_complete(int argc, char *argv[], char last_char, int maxv, char *cmdv[]) -{ - static char tmp_buf[512]; - int space; - - space = last_char == '\0' || last_char == ' ' || last_char == '\t'; - - if (space && argc == 1) - return env_complete("", maxv, cmdv, sizeof(tmp_buf), tmp_buf); - - if (!space && argc == 2) - return env_complete(argv[1], maxv, cmdv, sizeof(tmp_buf), tmp_buf); - - return 0; -} - -static void install_auto_complete_handler(const char *cmd, - int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[])) -{ - cmd_tbl_t *cmdtp; - - cmdtp = find_cmd(cmd); - if (cmdtp == NULL) - return; - - cmdtp->complete = complete; -} - -void install_auto_complete(void) -{ - install_auto_complete_handler("printenv", var_complete); - install_auto_complete_handler("setenv", var_complete); -#if (CONFIG_COMMANDS & CFG_CMD_RUN) - install_auto_complete_handler("run", var_complete); -#endif -} - -/*************************************************************************************/ - -static int complete_cmdv(int argc, char *argv[], char last_char, int maxv, char *cmdv[]) -{ - cmd_tbl_t *cmdtp; - const char *p; - int len, clen; - int n_found = 0; - const char *cmd; - - /* sanity? */ - if (maxv < 2) - return -2; - - cmdv[0] = NULL; - - if (argc == 0) { - /* output full list of commands */ - for (cmdtp = &__u_boot_cmd_start; cmdtp != &__u_boot_cmd_end; cmdtp++) { - if (n_found >= maxv - 2) { - cmdv[n_found++] = "..."; - break; - } - cmdv[n_found++] = cmdtp->name; - } - cmdv[n_found] = NULL; - return n_found; - } - - /* more than one arg or one but the start of the next */ - if (argc > 1 || (last_char == '\0' || last_char == ' ' || last_char == '\t')) { - cmdtp = find_cmd(argv[0]); - if (cmdtp == NULL || cmdtp->complete == NULL) { - cmdv[0] = NULL; - return 0; - } - return (*cmdtp->complete)(argc, argv, last_char, maxv, cmdv); - } - - cmd = argv[0]; - /* - * Some commands allow length modifiers (like "cp.b"); - * compare command name only until first dot. - */ - p = strchr(cmd, '.'); - if (p == NULL) - len = strlen(cmd); - else - len = p - cmd; - - /* return the partial matches */ - for (cmdtp = &__u_boot_cmd_start; cmdtp != &__u_boot_cmd_end; cmdtp++) { - - clen = strlen(cmdtp->name); - if (clen < len) - continue; - - if (memcmp(cmd, cmdtp->name, len) != 0) - continue; - - /* too many! */ - if (n_found >= maxv - 2) { - cmdv[n_found++] = "..."; - break; - } - - cmdv[n_found++] = cmdtp->name; - } - - cmdv[n_found] = NULL; - return n_found; -} - -static int make_argv(char *s, int argvsz, char *argv[]) -{ - int argc = 0; - - /* split into argv */ - while (argc < argvsz - 1) { - - /* skip any white space */ - while ((*s == ' ') || (*s == '\t')) - ++s; - - if (*s == '\0') /* end of s, no more args */ - break; - - argv[argc++] = s; /* begin of argument string */ - - /* find end of string */ - while (*s && (*s != ' ') && (*s != '\t')) - ++s; - - if (*s == '\0') /* end of s, no more args */ - break; - - *s++ = '\0'; /* terminate current arg */ - } - argv[argc] = NULL; - - return argc; -} - -static void print_argv(const char *banner, const char *leader, const char *sep, int linemax, char *argv[]) -{ - int ll = leader != NULL ? strlen(leader) : 0; - int sl = sep != NULL ? strlen(sep) : 0; - int len, i; - - if (banner) { - puts("\n"); - puts(banner); - } - - i = linemax; /* force leader and newline */ - while (*argv != NULL) { - len = strlen(*argv) + sl; - if (i + len >= linemax) { - puts("\n"); - if (leader) - puts(leader); - i = ll - sl; - } else if (sep) - puts(sep); - puts(*argv++); - i += len; - } - printf("\n"); -} - -static int find_common_prefix(char *argv[]) -{ - int i, len; - char *anchor, *s, *t; - - if (*argv == NULL) - return 0; - - /* begin with max */ - anchor = *argv++; - len = strlen(anchor); - while ((t = *argv++) != NULL) { - s = anchor; - for (i = 0; i < len; i++, t++, s++) { - if (*t != *s) - break; - } - len = s - anchor; - } - return len; -} - -static char tmp_buf[CONFIG_CBSIZE]; /* copy of console I/O buffer */ - -int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp) -{ - int n = *np, col = *colp; - char *argv[CONFIG_MAXARGS + 1]; /* NULL terminated */ - char *cmdv[20]; - char *s, *t; - const char *sep; - int i, j, k, len, seplen, argc; - int cnt; - char last_char; - - if (strcmp(prompt, CONFIG_PROMPT) != 0) - return 0; /* not in normal console */ - - cnt = strlen(buf); - if (cnt >= 1) - last_char = buf[cnt - 1]; - else - last_char = '\0'; - - /* copy to secondary buffer which will be affected */ - strcpy(tmp_buf, buf); - - /* separate into argv */ - argc = make_argv(tmp_buf, sizeof(argv)/sizeof(argv[0]), argv); - - /* do the completion and return the possible completions */ - i = complete_cmdv(argc, argv, last_char, sizeof(cmdv)/sizeof(cmdv[0]), cmdv); - - /* no match; bell and out */ - if (i == 0) { - if (argc > 1) /* allow tab for non command */ - return 0; - putchar('\a'); - return 1; - } - - s = NULL; - len = 0; - sep = NULL; - seplen = 0; - if (i == 1) { /* one match; perfect */ - k = strlen(argv[argc - 1]); - s = cmdv[0] + k; - len = strlen(s); - sep = " "; - seplen = 1; - } else if (i > 1 && (j = find_common_prefix(cmdv)) != 0) { /* more */ - k = strlen(argv[argc - 1]); - j -= k; - if (j > 0) { - s = cmdv[0] + k; - len = j; - } - } - - if (s != NULL) { - k = len + seplen; - /* make sure it fits */ - if (n + k >= CONFIG_CBSIZE - 2) { - putchar('\a'); - return 1; - } - - t = buf + cnt; - for (i = 0; i < len; i++) - *t++ = *s++; - if (sep != NULL) - for (i = 0; i < seplen; i++) - *t++ = sep[i]; - *t = '\0'; - n += k; - col += k; - puts(t - k); - if (sep == NULL) - putchar('\a'); - *np = n; - *colp = col; - } else { - print_argv(NULL, " ", " ", 78, cmdv); - - puts(prompt); - puts(buf); - } - return 1; -} - -#endif diff --git a/common/complete.c b/common/complete.c new file mode 100644 index 0000000000..211dc2e09d --- /dev/null +++ b/common/complete.c @@ -0,0 +1,210 @@ +/* + * complete.c - functions for TAB completion + * + * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <common.h> +#include <complete.h> +#include <xfuncs.h> +#include <list.h> +#include <malloc.h> +#include <fs.h> +#include <linux/stat.h> +#include <libgen.h> +#include <command.h> + +static int complete_push(struct complete_handle *handle, char *str) +{ + struct complete_handle *new; + + new = xmalloc(sizeof(struct complete_handle) + strlen(str) + 1); + + strcpy(new->str, str); + + list_add_tail(&new->list, &handle->list); + + return 0; +} + +static int file_complete(struct complete_handle *handle, char *instr) +{ + char *path = strdup(instr); + struct stat s; + DIR *dir; + struct dirent *d; + char tmp[PATH_MAX]; + char *base, *dirn; + + base = basename(instr); + dirn = dirname(path); + + dir = opendir(dirn); + if (!dir) + goto out; + + while ((d = readdir(dir))) { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + + if (!strncmp(base, d->d_name, strlen(base))) { + strcpy(tmp, instr); + strcat(tmp, d->d_name + strlen(base)); + if (!stat(tmp, &s) && S_ISDIR(s.st_mode)) + strcat(tmp, "/"); + else + strcat(tmp, " "); + complete_push(handle, tmp); + } + } + + closedir(dir); + +out: + free(path); + + return 0; +} + +static int command_complete(struct complete_handle *handle, char *instr) +{ + cmd_tbl_t *cmdtp; + char cmd[128]; + + for_each_command(cmdtp) { + if (!strncmp(instr, cmdtp->name, strlen(instr))) { + strcpy(cmd, cmdtp->name); + cmd[strlen(cmdtp->name)] = ' '; + cmd[strlen(cmdtp->name) + 1] = 0; + complete_push(handle, cmd); + } + } + + return 0; +} + +static int tab_pressed = 0; + +void complete_reset(void) +{ + tab_pressed = 0; +} + +int complete(char *instr, char **outstr) +{ + struct complete_handle c, *entry, *first_entry, *safe; + int pos; + char ch; + int changed; + static char out[256]; + int outpos = 0; + int reprint = 0; + char *t; + + INIT_LIST_HEAD(&c.list); + + /* advance to the last command */ + t = strrchr(instr, ';'); + if (!t) + t = instr; + else + t++; + + while (*t == ' ') + t++; + + instr = t; + + /* get the completion possibilities */ + if ((t = strrchr(t, ' '))) { + t++; + file_complete(&c, t); + instr = t; + } else + command_complete(&c, instr); + + pos = strlen(instr); + + *outstr = ""; + if (list_empty(&c.list)) + return reprint; + + out[0] = 0; + + first_entry = list_first_entry(&c.list, struct complete_handle, list); + + while (1) { + entry = first_entry; + ch = entry->str[pos]; + if (!ch) + break; + + changed = 0; + list_for_each_entry(entry, &c.list, list) { + if (!entry->str[pos]) + break; + if (ch != entry->str[pos]) { + changed = 1; + break; + } + } + + if (changed) + break; + out[outpos++] = ch; + pos++; + } + + if (!list_is_last(&first_entry->list, &c.list) && !outpos && tab_pressed) { + int len = 0, num, i; + + printf("\n"); + + list_for_each_entry(entry, &c.list, list) { + int l = strlen(entry->str) + 4; + if (l > len) + len = l; + } + + num = 80 / len; + if (len == 0) + len = 1; + + i = 0; + list_for_each_entry(entry, &c.list, list) { + printf("%-*s ", len, entry->str); + if (!(++i % num)) + printf("\n"); + } + if (i % num) + printf("\n"); + reprint = 1; + } + + out[outpos++] = 0; + *outstr = out; + + if (*out == 0) + tab_pressed = 1; + else + tab_pressed = 0; + + list_for_each_entry_safe(entry, safe, &c.list, list) + free(entry); + + return reprint; +} + diff --git a/include/complete.h b/include/complete.h new file mode 100644 index 0000000000..87332b8160 --- /dev/null +++ b/include/complete.h @@ -0,0 +1,15 @@ +#ifndef __COMPLETE_ +#define __COMPLETE_ + +#include <list.h> + +int complete(char *instr, char **outstr); +void complete_reset(void); + +struct complete_handle { + struct list_head list; + char str[0]; +}; + +#endif /* __COMPLETE_ */ + diff --git a/lib/readline.c b/lib/readline.c index 2cf90eca8d..1bdfe61b9d 100644 --- a/lib/readline.c +++ b/lib/readline.c @@ -2,6 +2,7 @@ #include <readkey.h> #include <init.h> #include <xfuncs.h> +#include <complete.h> /* * cmdline-editing related codes from vivi. @@ -179,7 +180,13 @@ int readline(const char *prompt, char *buf, int len) char ichar; int insert = 1; int rc = 0; +#ifdef CONFIG_AUTO_COMPLETE + char tmp; + int reprint, i; + char *completestr; + complete_reset(); +#endif puts (prompt); while (1) { @@ -192,6 +199,22 @@ int readline(const char *prompt, char *buf, int len) } switch (ichar) { + case '\t': +#ifdef CONFIG_AUTO_COMPLETE + tmp = buf[num]; + buf[num] = 0; + reprint = complete(buf, &completestr); + if (reprint) + printf("%s%s", prompt, buf); + + i = 0; + while (completestr[i]) + cread_add_char(completestr[i++], insert, &num, &eol_num, buf, len); + + buf[num] = tmp; +#endif + break; + case KEY_HOME: BEGINNING_OF_LINE(); break; diff --git a/lib/readline_simple.c b/lib/readline_simple.c index b1ac64a545..9dd21545e2 100644 --- a/lib/readline_simple.c +++ b/lib/readline_simple.c @@ -118,14 +118,6 @@ int readline (const char *prompt, char *line, int len) */ if (n < CONFIG_CBSIZE-2) { if (c == '\t') { /* expand TABs */ -#ifdef CONFIG_AUTO_COMPLETE - /* if auto completion triggered just continue */ - *p = '\0'; - if (cmd_auto_complete(prompt, line, &n, &col)) { - p = line + n; /* reset */ - continue; - } -#endif puts (tab_seq+(col&07)); col += 8 - (col&07); } else { @@ -155,4 +147,4 @@ int readline (const char *prompt, char *line, int len) * Enable the "Simple parser" in "General Settings", "Select your shell" to * get back the old console feeling. * - */
\ No newline at end of file + */ |