common: command: Add support for $ auto-completion
authorBoris Brezillon <boris.brezillon@bootlin.com>
Wed, 5 Dec 2018 08:26:50 +0000 (09:26 +0100)
committerTom Rini <trini@konsulko.com>
Tue, 15 Jan 2019 20:38:28 +0000 (15:38 -0500)
Add the dollar_complete() function to auto-complete arguments starting
with a '$' and use it in the cmd_auto_complete() path such that all
args starting with a $ can be auto-completed based on the available env
vars.

Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
[trini: Fix some linking problems]
Signed-off-by: Tom Rini <trini@konsulko.com>
common/command.c
env/common.c
include/common.h
lib/Makefile

index 19f0534a76ea53cf146c042b79076d83a181b3b9..e14d1fa1d6b4c4425f9c5b74e65ada06a67524b5 100644 (file)
@@ -142,23 +142,38 @@ int cmd_usage(const cmd_tbl_t *cmdtp)
 }
 
 #ifdef CONFIG_AUTO_COMPLETE
+static char env_complete_buf[512];
 
 int var_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[])
 {
-       static char tmp_buf[512];
        int space;
 
        space = last_char == '\0' || isblank(last_char);
 
        if (space && argc == 1)
-               return env_complete("", maxv, cmdv, sizeof(tmp_buf), tmp_buf);
+               return env_complete("", maxv, cmdv, sizeof(env_complete_buf),
+                                   env_complete_buf, false);
 
        if (!space && argc == 2)
-               return env_complete(argv[1], maxv, cmdv, sizeof(tmp_buf), tmp_buf);
+               return env_complete(argv[1], maxv, cmdv,
+                                   sizeof(env_complete_buf),
+                                   env_complete_buf, false);
 
        return 0;
 }
 
+static int dollar_complete(int argc, char * const argv[], char last_char,
+                          int maxv, char *cmdv[])
+{
+       /* Make sure the last argument starts with a $. */
+       if (argc < 1 || argv[argc - 1][0] != '$' ||
+           last_char == '\0' || isblank(last_char))
+               return 0;
+
+       return env_complete(argv[argc - 1], maxv, cmdv, sizeof(env_complete_buf),
+                           env_complete_buf, true);
+}
+
 /*************************************************************************************/
 
 int complete_subcmdv(cmd_tbl_t *cmdtp, int count, int argc,
@@ -357,9 +372,14 @@ int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp)
        /* 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);
+       /* first try a $ completion */
+       i = dollar_complete(argc, argv, last_char,
+                           sizeof(cmdv) / sizeof(cmdv[0]), cmdv);
+       if (!i) {
+               /* 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) {
index 3317cef3552264cae2870df6ad1cedaa0096fb75..d1a6a5286013a6669f1b8c69e1fa4425a941714e 100644 (file)
@@ -240,32 +240,76 @@ void env_relocate(void)
        }
 }
 
-#if defined(CONFIG_AUTO_COMPLETE) && !defined(CONFIG_SPL_BUILD)
-int env_complete(char *var, int maxv, char *cmdv[], int bufsz, char *buf)
+#ifdef CONFIG_AUTO_COMPLETE
+int env_complete(char *var, int maxv, char *cmdv[], int bufsz, char *buf,
+                bool dollar_comp)
 {
        ENTRY *match;
        int found, idx;
 
+       if (dollar_comp) {
+               /*
+                * When doing $ completion, the first character should
+                * obviously be a '$'.
+                */
+               if (var[0] != '$')
+                       return 0;
+
+               var++;
+
+               /*
+                * The second one, if present, should be a '{', as some
+                * configuration of the u-boot shell expand ${var} but not
+                * $var.
+                */
+               if (var[0] == '{')
+                       var++;
+               else if (var[0] != '\0')
+                       return 0;
+       }
+
        idx = 0;
        found = 0;
        cmdv[0] = NULL;
 
+
        while ((idx = hmatch_r(var, idx, &match, &env_htab))) {
                int vallen = strlen(match->key) + 1;
 
-               if (found >= maxv - 2 || bufsz < vallen)
+               if (found >= maxv - 2 ||
+                   bufsz < vallen + (dollar_comp ? 3 : 0))
                        break;
 
                cmdv[found++] = buf;
+
+               /* Add the '${' prefix to each var when doing $ completion. */
+               if (dollar_comp) {
+                       strcpy(buf, "${");
+                       buf += 2;
+                       bufsz -= 3;
+               }
+
                memcpy(buf, match->key, vallen);
                buf += vallen;
                bufsz -= vallen;
+
+               if (dollar_comp) {
+                       /*
+                        * This one is a bit odd: vallen already contains the
+                        * '\0' character but we need to add the '}' suffix,
+                        * hence the buf - 1 here. strcpy() will add the '\0'
+                        * character just after '}'. buf is then incremented
+                        * to account for the extra '}' we just added.
+                        */
+                       strcpy(buf - 1, "}");
+                       buf++;
+               }
        }
 
        qsort(cmdv, found, sizeof(cmdv[0]), strcmp_compar);
 
        if (idx)
-               cmdv[found++] = "...";
+               cmdv[found++] = dollar_comp ? "${...}" : "...";
 
        cmdv[found] = NULL;
        return found;
index 657cc404cfaf3335fa206b1c1158ba39da19529b..18948b6bc2d28884246490e4de04ce8b631221c9 100644 (file)
@@ -248,7 +248,8 @@ static inline int env_set_addr(const char *varname, const void *addr)
 }
 
 #ifdef CONFIG_AUTO_COMPLETE
-int env_complete(char *var, int maxv, char *cmdv[], int maxsz, char *buf);
+int env_complete(char *var, int maxv, char *cmdv[], int maxsz, char *buf,
+                bool dollar_comp);
 #endif
 int get_env_id (void);
 
index a6dd928a9265f1a823965edb54095d968730b97a..f06d6316d4680509406555288d225d1d6cbb75ef 100644 (file)
@@ -41,7 +41,6 @@ obj-y += ldiv.o
 obj-$(CONFIG_MD5) += md5.o
 obj-y += net_utils.o
 obj-$(CONFIG_PHYSMEM) += physmem.o
-obj-y += qsort.o
 obj-y += rc4.o
 obj-$(CONFIG_SUPPORT_EMMC_RPMB) += sha256.o
 obj-$(CONFIG_RBTREE)   += rbtree.o
@@ -67,7 +66,6 @@ obj-$(CONFIG_$(SPL_)LZ4) += lz4_wrapper.o
 
 obj-$(CONFIG_LIBAVB) += libavb/
 
-obj-$(CONFIG_$(SPL_TPL_)SAVEENV) += qsort.o
 obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += libfdt/
 ifneq ($(CONFIG_$(SPL_TPL_)BUILD)$(CONFIG_$(SPL_TPL_)OF_PLATDATA),yy)
 obj-$(CONFIG_$(SPL_TPL_)OF_CONTROL) += fdtdec_common.o
@@ -80,6 +78,7 @@ obj-$(CONFIG_$(SPL_TPL_)HASH_SUPPORT) += crc16.o
 obj-$(CONFIG_SPL_NET_SUPPORT) += net_utils.o
 endif
 obj-$(CONFIG_ADDR_MAP) += addr_map.o
+obj-y += qsort.o
 obj-y += hashtable.o
 obj-y += errno.o
 obj-y += display_options.o