diff -ruNb u-boot-original/README u-boot-autocomplete/README --- u-boot-original/README 2004-03-30 14:37:28.395972064 +0300 +++ u-boot-autocomplete/README 2004-03-30 15:54:54.321684000 +0300 @@ -1231,6 +1231,10 @@ default value of 5 is used. - Command Interpreter: + CFG_AUTO_COMPLETE + + Enable auto completion of commands using TAB. + CFG_HUSH_PARSER Define this variable to enable the "hush" shell (from diff -ruNb u-boot-original/common/command.c u-boot-autocomplete/common/command.c --- u-boot-original/common/command.c 2004-03-30 14:37:28.665931024 +0300 +++ u-boot-autocomplete/common/command.c 2004-03-30 15:42:19.877376968 +0300 @@ -217,3 +217,284 @@ return NULL; /* not found or ambiguous command */ } + +#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[CFG_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[CFG_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, CFG_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; + putc('\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 >= CFG_CBSIZE - 2) { + putc('\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) + putc('\a'); + *np = n; + *colp = col; + } else { + print_argv(NULL, " ", " ", 78, cmdv); + + puts(prompt); + puts(buf); + } + return 1; +} + +#endif diff -ruNb u-boot-original/common/env_common.c u-boot-autocomplete/common/env_common.c --- u-boot-original/common/env_common.c 2004-03-30 14:37:28.668930568 +0300 +++ u-boot-autocomplete/common/env_common.c 2004-03-30 15:42:19.878376816 +0300 @@ -260,3 +260,44 @@ disable_nvram(); #endif } + +#ifdef CONFIG_AUTO_COMPLETE +int env_complete(char *var, int maxv, char *cmdv[], int bufsz, char *buf) +{ + int i, nxt, len, vallen, found; + const char *lval, *rval; + + found = 0; + cmdv[0] = NULL; + + len = strlen(var); + /* now iterate over the variables and select those that match */ + for (i=0; env_get_char(i) != '\0'; i=nxt+1) { + + for (nxt=i; env_get_char(nxt) != '\0'; ++nxt) + ; + + lval = env_get_addr(i); + rval = strchr(lval, '='); + if (rval != NULL) { + vallen = rval - lval; + rval++; + } else + vallen = strlen(lval); + + if (len > 0 && (vallen < len || memcmp(lval, var, len) != 0)) + continue; + + if (found >= maxv - 2 || bufsz < vallen + 1) { + cmdv[found++] = "..."; + break; + } + cmdv[found++] = buf; + memcpy(buf, lval, vallen); buf += vallen; bufsz -= vallen; + *buf++ = '\0'; bufsz--; + } + + cmdv[found] = NULL; + return found; +} +#endif diff -ruNb u-boot-original/common/main.c u-boot-autocomplete/common/main.c --- u-boot-original/common/main.c 2004-03-30 14:37:28.670930264 +0300 +++ u-boot-autocomplete/common/main.c 2004-03-30 15:42:19.879376664 +0300 @@ -365,6 +365,10 @@ u_boot_hush_start (); #endif +#ifdef CONFIG_AUTO_COMPLETE + install_auto_complete(); +#endif + #ifdef CONFIG_PREBOOT if ((p = getenv ("preboot")) != NULL) { # ifdef CONFIG_AUTOBOOT_KEYED @@ -608,6 +612,14 @@ */ if (n < CFG_CBSIZE-2) { if (c == '\t') { /* expand TABs */ +#ifdef CONFIG_AUTO_COMPLETE + /* if auto completion triggered just continue */ + *p = '\0'; + if (cmd_auto_complete(prompt, console_buffer, &n, &col)) { + p = console_buffer + n; /* reset */ + continue; + } +#endif puts (tab_seq+(col&07)); col += 8 - (col&07); } else { diff -ruNb u-boot-original/include/command.h u-boot-autocomplete/include/command.h --- u-boot-original/include/command.h 2004-03-30 14:37:28.749918256 +0300 +++ u-boot-autocomplete/include/command.h 2004-03-30 15:42:19.880376512 +0300 @@ -46,6 +46,10 @@ #ifdef CFG_LONGHELP char *help; /* Help message (long) */ #endif +#ifdef CONFIG_AUTO_COMPLETE + /* do auto completion on the arguments */ + int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]); +#endif }; typedef struct cmd_tbl_s cmd_tbl_t; @@ -57,6 +61,11 @@ /* common/command.c */ cmd_tbl_t *find_cmd(const char *cmd); +#ifdef CONFIG_AUTO_COMPLETE +extern void install_auto_complete(void); +extern int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp); +#endif + /* * Monitor Command * diff -ruNb u-boot-original/include/common.h u-boot-autocomplete/include/common.h --- u-boot-original/include/common.h 2004-03-30 14:37:28.750918104 +0300 +++ u-boot-autocomplete/include/common.h 2004-03-30 15:42:19.880376512 +0300 @@ -196,6 +196,10 @@ # include #endif /* CONFIG_I386 */ +#ifdef CONFIG_AUTO_COMPLETE +int env_complete(char *var, int maxv, char *cmdv[], int maxsz, char *buf); +#endif + void pci_init (void); void pci_init_board(void); void pciinfo (int, int);