1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * (C) Copyright 2021
4  * Francis Laniel, Amarula Solutions, francis.laniel@amarulasolutions.com
5  */
6 
7 #include <command.h>
8 #include <env.h>
9 #include <env_attr.h>
10 #include <test/hush.h>
11 #include <test/ut.h>
12 #include <asm/global_data.h>
13 
14 DECLARE_GLOBAL_DATA_PTR;
15 
hush_test_simple_dollar(struct unit_test_state * uts)16 static int hush_test_simple_dollar(struct unit_test_state *uts)
17 {
18 	ut_assertok(run_command("echo $dollar_foo", 0));
19 	ut_assert_nextline_empty();
20 	ut_assert_console_end();
21 
22 	ut_assertok(run_command("echo ${dollar_foo}", 0));
23 	ut_assert_nextline_empty();
24 	ut_assert_console_end();
25 
26 	ut_assertok(run_command("dollar_foo=bar", 0));
27 
28 	ut_assertok(run_command("echo $dollar_foo", 0));
29 	ut_assert_nextline("bar");
30 	ut_assert_console_end();
31 
32 	ut_assertok(run_command("echo ${dollar_foo}", 0));
33 	ut_assert_nextline("bar");
34 	ut_assert_console_end();
35 
36 	ut_assertok(run_command("dollar_foo=\\$bar", 0));
37 
38 	ut_assertok(run_command("echo $dollar_foo", 0));
39 	ut_assert_nextline("$bar");
40 	ut_assert_console_end();
41 
42 	ut_assertok(run_command("dollar_foo='$bar'", 0));
43 
44 	ut_assertok(run_command("echo $dollar_foo", 0));
45 	ut_assert_nextline("$bar");
46 	ut_assert_console_end();
47 
48 	ut_asserteq(1, run_command("dollar_foo=bar quux", 0));
49 	/* Next line contains error message */
50 	ut_assert_skipline();
51 	ut_assert_console_end();
52 
53 	ut_asserteq(1, run_command("dollar_foo='bar quux", 0));
54 	/* Next line contains error message */
55 	ut_assert_skipline();
56 	ut_assert_console_end();
57 
58 	ut_asserteq(1, run_command("dollar_foo=bar quux\"", 0));
59 	/* Next line contains error message */
60 	ut_assert_skipline();
61 	/*
62 	 * Old parser prints the error message on two lines:
63 	 * Unknown command 'quux
64 	 * ' - try 'help'
65 	 * While the new only prints it on one:
66 	 * syntax error: unterminated \"
67 	 */
68 	if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
69 		ut_assert_skipline();
70 	}
71 	ut_assert_console_end();
72 
73 	ut_assertok(run_command("dollar_foo='bar \"quux'", 0));
74 
75 	ut_assertok(run_command("echo $dollar_foo", 0));
76 	/*
77 	 * This one is buggy.
78 	 * ut_assert_nextline("bar \"quux");
79 	 * ut_assert_console_end();
80 	 *
81 	 * So, let's reset output:
82 	 */
83 	console_record_reset_enable();
84 
85 	if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
86 		/*
87 		 * Old parser returns an error because it waits for closing
88 		 * '\'', but this behavior is wrong as the '\'' is surrounded by
89 		 * '"', so no need to wait for a closing one.
90 		 */
91 		ut_assertok(run_command("dollar_foo=\"bar 'quux\"", 0));
92 
93 		ut_assertok(run_command("echo $dollar_foo", 0));
94 		ut_assert_nextline("bar 'quux");
95 		ut_assert_console_end();
96 	} else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
97 		ut_asserteq(1, run_command("dollar_foo=\"bar 'quux\"", 0));
98 		/* Next line contains error message */
99 		ut_assert_skipline();
100 		ut_assert_console_end();
101 	}
102 
103 	ut_assertok(run_command("dollar_foo='bar quux'", 0));
104 	ut_assertok(run_command("echo $dollar_foo", 0));
105 	ut_assert_nextline("bar quux");
106 	ut_assert_console_end();
107 
108 	if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
109 		/* Reset local variable. */
110 		ut_assertok(run_command("dollar_foo=", 0));
111 	} else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
112 		puts("Beware: this test set local variable dollar_foo and it cannot be unset!\n");
113 	}
114 
115 	return 0;
116 }
117 HUSH_TEST(hush_test_simple_dollar, UTF_CONSOLE);
118 
hush_test_env_dollar(struct unit_test_state * uts)119 static int hush_test_env_dollar(struct unit_test_state *uts)
120 {
121 	env_set("env_foo", "bar");
122 
123 	ut_assertok(run_command("echo $env_foo", 0));
124 	ut_assert_nextline("bar");
125 	ut_assert_console_end();
126 
127 	ut_assertok(run_command("echo ${env_foo}", 0));
128 	ut_assert_nextline("bar");
129 	ut_assert_console_end();
130 
131 	/* Environment variables have priority over local variable */
132 	ut_assertok(run_command("env_foo=quux", 0));
133 	ut_assertok(run_command("echo ${env_foo}", 0));
134 	ut_assert_nextline("bar");
135 	ut_assert_console_end();
136 
137 	/* Clean up setting the variable */
138 	env_set("env_foo", NULL);
139 
140 	if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
141 		/* Reset local variable. */
142 		ut_assertok(run_command("env_foo=", 0));
143 	} else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
144 		puts("Beware: this test set local variable env_foo and it cannot be unset!\n");
145 	}
146 
147 	return 0;
148 }
149 HUSH_TEST(hush_test_env_dollar, UTF_CONSOLE);
150 
hush_test_command_dollar(struct unit_test_state * uts)151 static int hush_test_command_dollar(struct unit_test_state *uts)
152 {
153 	ut_assertok(run_command("dollar_bar=\"echo bar\"", 0));
154 
155 	ut_assertok(run_command("$dollar_bar", 0));
156 	ut_assert_nextline("bar");
157 	ut_assert_console_end();
158 
159 	ut_assertok(run_command("${dollar_bar}", 0));
160 	ut_assert_nextline("bar");
161 	ut_assert_console_end();
162 
163 	ut_assertok(run_command("dollar_bar=\"echo\nbar\"", 0));
164 
165 	ut_assertok(run_command("$dollar_bar", 0));
166 	ut_assert_nextline("bar");
167 	ut_assert_console_end();
168 
169 	ut_assertok(run_command("dollar_bar='echo bar\n'", 0));
170 
171 	ut_assertok(run_command("$dollar_bar", 0));
172 	ut_assert_nextline("bar");
173 	ut_assert_console_end();
174 
175 	ut_assertok(run_command("dollar_bar='echo bar\\n'", 0));
176 
177 	ut_assertok(run_command("$dollar_bar", 0));
178 
179 	if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
180 		/*
181 		 * This difference seems to come from a bug solved in Busybox
182 		 * hush.
183 		 * Behavior of hush 2021 is coherent with bash and other shells.
184 		 */
185 		ut_assert_nextline("bar\\n");
186 	} else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
187 		ut_assert_nextline("barn");
188 	}
189 
190 	ut_assert_console_end();
191 
192 	ut_assertok(run_command("dollar_bar='echo $bar'", 0));
193 
194 	ut_assertok(run_command("$dollar_bar", 0));
195 	ut_assert_nextline("$bar");
196 	ut_assert_console_end();
197 
198 	ut_assertok(run_command("dollar_quux=quux", 0));
199 	ut_assertok(run_command("dollar_bar=\"echo $dollar_quux\"", 0));
200 
201 	ut_assertok(run_command("$dollar_bar", 0));
202 	ut_assert_nextline("quux");
203 	ut_assert_console_end();
204 
205 	if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
206 		/* Reset local variables. */
207 		ut_assertok(run_command("dollar_bar=", 0));
208 		ut_assertok(run_command("dollar_quux=", 0));
209 	} else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
210 		puts("Beware: this test sets local variable dollar_bar and "
211 		     "dollar_quux and they cannot be unset!\n");
212 	}
213 
214 	return 0;
215 }
216 HUSH_TEST(hush_test_command_dollar, UTF_CONSOLE);
217