common: command: Add support for $ auto-completion

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>
diff --git a/env/common.c b/env/common.c
index 3317cef..d1a6a52 100644
--- a/env/common.c
+++ b/env/common.c
@@ -240,32 +240,76 @@
 	}
 }
 
-#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;