hush: Don't parse the contents of a dereferenced var

When a variable which contains a user-supplied value is dereferenced
(e.g. to be echo'ed), make sure that the value is not further parsed
by hush.

Set the hush local variable "HUSH_NO_EVAL=1" to enable this behavior.

Without this patch, a sequence like this occurs:

	Panda # env set my_user_string Bob\'s favorite device
	Panda # print my_user_string
	my_user_string=Bob's favorite device
	Panda # echo $my_user_string
	syntax error hush.c:3007

With this patch, it looks like this:

	Panda # HUSH_NO_EVAL=1
	Panda # env set my_user_string Bob\'s favorite device
	Panda # print my_user_string
	my_user_string=Bob's favorite device
	Panda # echo $my_user_string
	Bob's favorite device

Signed-off-by: Joe Hershberger <joe.hershberger@ni.com>
diff --git a/common/hush.c b/common/hush.c
index 39cf203..4c84c2f 100644
--- a/common/hush.c
+++ b/common/hush.c
@@ -127,6 +127,7 @@
 #endif
 #endif
 #define SPECIAL_VAR_SYMBOL 03
+#define SUBSTED_VAR_SYMBOL 04
 #ifndef __U_BOOT__
 #define FLAG_EXIT_FROM_LOOP 1
 #define FLAG_PARSE_SEMICOLON (1 << 1)		/* symbol ';' is special for parser */
@@ -499,6 +500,7 @@
 /*     local variable support */
 static char **make_list_in(char **inp, char *name);
 static char *insert_var_value(char *inp);
+static char *insert_var_value_sub(char *inp, int tag_subst);
 
 #ifndef __U_BOOT__
 /* Table of built-in functions.  They can be forked or not, depending on
@@ -3088,6 +3090,21 @@
 			return 1;
 			break;
 #endif
+		case SUBSTED_VAR_SYMBOL:
+			dest->nonnull = 1;
+			while (ch = b_getch(input), ch != EOF &&
+			    ch != SUBSTED_VAR_SYMBOL) {
+				debug_printf("subst, pass=%d\n", ch);
+				if (input->__promptme == 0)
+					return 1;
+				b_addchr(dest, ch);
+			}
+			debug_printf("subst, term=%d\n", ch);
+			if (ch == EOF) {
+				syntax();
+				return 1;
+			}
+			break;
 		default:
 			syntax();   /* this is really an internal logic error */
 			return 1;
@@ -3129,6 +3146,10 @@
 	mapset((uchar *)"\\$'\"`", 3);      /* never flow through */
 	mapset((uchar *)"<>;&|(){}#", 1);   /* flow through if quoted */
 #else
+	{
+		uchar subst[2] = {SUBSTED_VAR_SYMBOL, 0};
+		mapset(subst, 3);       /* never flow through */
+	}
 	mapset((uchar *)"\\$'\"", 3);       /* never flow through */
 	mapset((uchar *)";&|#", 1);         /* flow through if quoted */
 #endif
@@ -3468,25 +3489,57 @@
 
 static char *insert_var_value(char *inp)
 {
+	return insert_var_value_sub(inp, 0);
+}
+
+static char *insert_var_value_sub(char *inp, int tag_subst)
+{
 	int res_str_len = 0;
 	int len;
 	int done = 0;
 	char *p, *p1, *res_str = NULL;
 
 	while ((p = strchr(inp, SPECIAL_VAR_SYMBOL))) {
+		/* check the beginning of the string for normal charachters */
 		if (p != inp) {
+			/* copy any charachters to the result string */
 			len = p - inp;
 			res_str = xrealloc(res_str, (res_str_len + len));
 			strncpy((res_str + res_str_len), inp, len);
 			res_str_len += len;
 		}
 		inp = ++p;
+		/* find the ending marker */
 		p = strchr(inp, SPECIAL_VAR_SYMBOL);
 		*p = '\0';
+		/* look up the value to substitute */
 		if ((p1 = lookup_param(inp))) {
-			len = res_str_len + strlen(p1);
+			if (tag_subst)
+				len = res_str_len + strlen(p1) + 2;
+			else
+				len = res_str_len + strlen(p1);
 			res_str = xrealloc(res_str, (1 + len));
-			strcpy((res_str + res_str_len), p1);
+			if (tag_subst) {
+				/*
+				 * copy the variable value to the result
+				 * string
+				 */
+				strcpy((res_str + res_str_len + 1), p1);
+
+				/*
+				 * mark the replaced text to be accepted as
+				 * is
+				 */
+				res_str[res_str_len] = SUBSTED_VAR_SYMBOL;
+				res_str[res_str_len + 1 + strlen(p1)] =
+					SUBSTED_VAR_SYMBOL;
+			} else
+				/*
+				 * copy the variable value to the result
+				 * string
+				 */
+				strcpy((res_str + res_str_len), p1);
+
 			res_str_len = len;
 		}
 		*p = SPECIAL_VAR_SYMBOL;
@@ -3550,9 +3603,14 @@
 	char *str = NULL;
 	int n;
 	int len = 2;
+	char *noeval_str;
+	int noeval = 0;
 
+	noeval_str = get_local_var("HUSH_NO_EVAL");
+	if (noeval_str != NULL && *noeval_str != '0' && *noeval_str != '\0')
+		noeval = 1;
 	for (n = 0; inp[n]; n++) {
-		p = insert_var_value(inp[n]);
+		p = insert_var_value_sub(inp[n], noeval);
 		str = xrealloc(str, (len + strlen(p)));
 		if (n) {
 			strcat(str, " ");