Commit 1992dbfdb9886c3dfc4a505091895c851f736f3a
1 parent
fd37dac9eb
Exists in
v2017.01-smarct4x
and in
40 other branches
Make 'run' use run_command_list() instead of run_command()
In the case where an environment variable spans multiple lines, we should use run_command_list() so that all lines are executed. This shold be backwards compatible with existing behaviour for existing scripts. Signed-off-by: Simon Glass <sjg@chromium.org>
Showing 1 changed file with 1 additions and 1 deletions Inline Diff
common/main.c
1 | /* | 1 | /* |
2 | * (C) Copyright 2000 | 2 | * (C) Copyright 2000 |
3 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | 3 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. |
4 | * | 4 | * |
5 | * Add to readline cmdline-editing by | 5 | * Add to readline cmdline-editing by |
6 | * (C) Copyright 2005 | 6 | * (C) Copyright 2005 |
7 | * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com> | 7 | * JinHua Luo, GuangDong Linux Center, <luo.jinhua@gd-linux.com> |
8 | * | 8 | * |
9 | * SPDX-License-Identifier: GPL-2.0+ | 9 | * SPDX-License-Identifier: GPL-2.0+ |
10 | */ | 10 | */ |
11 | 11 | ||
12 | /* #define DEBUG */ | 12 | /* #define DEBUG */ |
13 | 13 | ||
14 | #include <common.h> | 14 | #include <common.h> |
15 | #include <command.h> | 15 | #include <command.h> |
16 | #include <fdtdec.h> | 16 | #include <fdtdec.h> |
17 | #include <hush.h> | 17 | #include <hush.h> |
18 | #include <malloc.h> | 18 | #include <malloc.h> |
19 | #include <menu.h> | 19 | #include <menu.h> |
20 | #include <post.h> | 20 | #include <post.h> |
21 | #include <version.h> | 21 | #include <version.h> |
22 | #include <watchdog.h> | 22 | #include <watchdog.h> |
23 | #include <linux/ctype.h> | 23 | #include <linux/ctype.h> |
24 | 24 | ||
25 | DECLARE_GLOBAL_DATA_PTR; | 25 | DECLARE_GLOBAL_DATA_PTR; |
26 | 26 | ||
27 | /* | 27 | /* |
28 | * Board-specific Platform code can reimplement show_boot_progress () if needed | 28 | * Board-specific Platform code can reimplement show_boot_progress () if needed |
29 | */ | 29 | */ |
30 | void inline __show_boot_progress (int val) {} | 30 | void inline __show_boot_progress (int val) {} |
31 | void show_boot_progress (int val) __attribute__((weak, alias("__show_boot_progress"))); | 31 | void show_boot_progress (int val) __attribute__((weak, alias("__show_boot_progress"))); |
32 | 32 | ||
33 | #define MAX_DELAY_STOP_STR 32 | 33 | #define MAX_DELAY_STOP_STR 32 |
34 | 34 | ||
35 | #define DEBUG_PARSER 0 /* set to 1 to debug */ | 35 | #define DEBUG_PARSER 0 /* set to 1 to debug */ |
36 | 36 | ||
37 | #define debug_parser(fmt, args...) \ | 37 | #define debug_parser(fmt, args...) \ |
38 | debug_cond(DEBUG_PARSER, fmt, ##args) | 38 | debug_cond(DEBUG_PARSER, fmt, ##args) |
39 | 39 | ||
40 | #ifndef DEBUG_BOOTKEYS | 40 | #ifndef DEBUG_BOOTKEYS |
41 | #define DEBUG_BOOTKEYS 0 | 41 | #define DEBUG_BOOTKEYS 0 |
42 | #endif | 42 | #endif |
43 | #define debug_bootkeys(fmt, args...) \ | 43 | #define debug_bootkeys(fmt, args...) \ |
44 | debug_cond(DEBUG_BOOTKEYS, fmt, ##args) | 44 | debug_cond(DEBUG_BOOTKEYS, fmt, ##args) |
45 | 45 | ||
46 | char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */ | 46 | char console_buffer[CONFIG_SYS_CBSIZE + 1]; /* console I/O buffer */ |
47 | 47 | ||
48 | static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen); | 48 | static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen); |
49 | static const char erase_seq[] = "\b \b"; /* erase sequence */ | 49 | static const char erase_seq[] = "\b \b"; /* erase sequence */ |
50 | static const char tab_seq[] = " "; /* used to expand TABs */ | 50 | static const char tab_seq[] = " "; /* used to expand TABs */ |
51 | 51 | ||
52 | #ifdef CONFIG_BOOT_RETRY_TIME | 52 | #ifdef CONFIG_BOOT_RETRY_TIME |
53 | static uint64_t endtime = 0; /* must be set, default is instant timeout */ | 53 | static uint64_t endtime = 0; /* must be set, default is instant timeout */ |
54 | static int retry_time = -1; /* -1 so can call readline before main_loop */ | 54 | static int retry_time = -1; /* -1 so can call readline before main_loop */ |
55 | #endif | 55 | #endif |
56 | 56 | ||
57 | #define endtick(seconds) (get_ticks() + (uint64_t)(seconds) * get_tbclk()) | 57 | #define endtick(seconds) (get_ticks() + (uint64_t)(seconds) * get_tbclk()) |
58 | 58 | ||
59 | #ifndef CONFIG_BOOT_RETRY_MIN | 59 | #ifndef CONFIG_BOOT_RETRY_MIN |
60 | #define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME | 60 | #define CONFIG_BOOT_RETRY_MIN CONFIG_BOOT_RETRY_TIME |
61 | #endif | 61 | #endif |
62 | 62 | ||
63 | #ifdef CONFIG_MODEM_SUPPORT | 63 | #ifdef CONFIG_MODEM_SUPPORT |
64 | int do_mdm_init = 0; | 64 | int do_mdm_init = 0; |
65 | extern void mdm_init(void); /* defined in board.c */ | 65 | extern void mdm_init(void); /* defined in board.c */ |
66 | #endif | 66 | #endif |
67 | 67 | ||
68 | /*************************************************************************** | 68 | /*************************************************************************** |
69 | * Watch for 'delay' seconds for autoboot stop or autoboot delay string. | 69 | * Watch for 'delay' seconds for autoboot stop or autoboot delay string. |
70 | * returns: 0 - no key string, allow autoboot 1 - got key string, abort | 70 | * returns: 0 - no key string, allow autoboot 1 - got key string, abort |
71 | */ | 71 | */ |
72 | #if defined(CONFIG_BOOTDELAY) | 72 | #if defined(CONFIG_BOOTDELAY) |
73 | # if defined(CONFIG_AUTOBOOT_KEYED) | 73 | # if defined(CONFIG_AUTOBOOT_KEYED) |
74 | static int abortboot_keyed(int bootdelay) | 74 | static int abortboot_keyed(int bootdelay) |
75 | { | 75 | { |
76 | int abort = 0; | 76 | int abort = 0; |
77 | uint64_t etime = endtick(bootdelay); | 77 | uint64_t etime = endtick(bootdelay); |
78 | struct { | 78 | struct { |
79 | char* str; | 79 | char* str; |
80 | u_int len; | 80 | u_int len; |
81 | int retry; | 81 | int retry; |
82 | } | 82 | } |
83 | delaykey [] = { | 83 | delaykey [] = { |
84 | { str: getenv ("bootdelaykey"), retry: 1 }, | 84 | { str: getenv ("bootdelaykey"), retry: 1 }, |
85 | { str: getenv ("bootdelaykey2"), retry: 1 }, | 85 | { str: getenv ("bootdelaykey2"), retry: 1 }, |
86 | { str: getenv ("bootstopkey"), retry: 0 }, | 86 | { str: getenv ("bootstopkey"), retry: 0 }, |
87 | { str: getenv ("bootstopkey2"), retry: 0 }, | 87 | { str: getenv ("bootstopkey2"), retry: 0 }, |
88 | }; | 88 | }; |
89 | 89 | ||
90 | char presskey [MAX_DELAY_STOP_STR]; | 90 | char presskey [MAX_DELAY_STOP_STR]; |
91 | u_int presskey_len = 0; | 91 | u_int presskey_len = 0; |
92 | u_int presskey_max = 0; | 92 | u_int presskey_max = 0; |
93 | u_int i; | 93 | u_int i; |
94 | 94 | ||
95 | #ifndef CONFIG_ZERO_BOOTDELAY_CHECK | 95 | #ifndef CONFIG_ZERO_BOOTDELAY_CHECK |
96 | if (bootdelay == 0) | 96 | if (bootdelay == 0) |
97 | return 0; | 97 | return 0; |
98 | #endif | 98 | #endif |
99 | 99 | ||
100 | # ifdef CONFIG_AUTOBOOT_PROMPT | 100 | # ifdef CONFIG_AUTOBOOT_PROMPT |
101 | printf(CONFIG_AUTOBOOT_PROMPT); | 101 | printf(CONFIG_AUTOBOOT_PROMPT); |
102 | # endif | 102 | # endif |
103 | 103 | ||
104 | # ifdef CONFIG_AUTOBOOT_DELAY_STR | 104 | # ifdef CONFIG_AUTOBOOT_DELAY_STR |
105 | if (delaykey[0].str == NULL) | 105 | if (delaykey[0].str == NULL) |
106 | delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR; | 106 | delaykey[0].str = CONFIG_AUTOBOOT_DELAY_STR; |
107 | # endif | 107 | # endif |
108 | # ifdef CONFIG_AUTOBOOT_DELAY_STR2 | 108 | # ifdef CONFIG_AUTOBOOT_DELAY_STR2 |
109 | if (delaykey[1].str == NULL) | 109 | if (delaykey[1].str == NULL) |
110 | delaykey[1].str = CONFIG_AUTOBOOT_DELAY_STR2; | 110 | delaykey[1].str = CONFIG_AUTOBOOT_DELAY_STR2; |
111 | # endif | 111 | # endif |
112 | # ifdef CONFIG_AUTOBOOT_STOP_STR | 112 | # ifdef CONFIG_AUTOBOOT_STOP_STR |
113 | if (delaykey[2].str == NULL) | 113 | if (delaykey[2].str == NULL) |
114 | delaykey[2].str = CONFIG_AUTOBOOT_STOP_STR; | 114 | delaykey[2].str = CONFIG_AUTOBOOT_STOP_STR; |
115 | # endif | 115 | # endif |
116 | # ifdef CONFIG_AUTOBOOT_STOP_STR2 | 116 | # ifdef CONFIG_AUTOBOOT_STOP_STR2 |
117 | if (delaykey[3].str == NULL) | 117 | if (delaykey[3].str == NULL) |
118 | delaykey[3].str = CONFIG_AUTOBOOT_STOP_STR2; | 118 | delaykey[3].str = CONFIG_AUTOBOOT_STOP_STR2; |
119 | # endif | 119 | # endif |
120 | 120 | ||
121 | for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) { | 121 | for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) { |
122 | delaykey[i].len = delaykey[i].str == NULL ? | 122 | delaykey[i].len = delaykey[i].str == NULL ? |
123 | 0 : strlen (delaykey[i].str); | 123 | 0 : strlen (delaykey[i].str); |
124 | delaykey[i].len = delaykey[i].len > MAX_DELAY_STOP_STR ? | 124 | delaykey[i].len = delaykey[i].len > MAX_DELAY_STOP_STR ? |
125 | MAX_DELAY_STOP_STR : delaykey[i].len; | 125 | MAX_DELAY_STOP_STR : delaykey[i].len; |
126 | 126 | ||
127 | presskey_max = presskey_max > delaykey[i].len ? | 127 | presskey_max = presskey_max > delaykey[i].len ? |
128 | presskey_max : delaykey[i].len; | 128 | presskey_max : delaykey[i].len; |
129 | 129 | ||
130 | debug_bootkeys("%s key:<%s>\n", | 130 | debug_bootkeys("%s key:<%s>\n", |
131 | delaykey[i].retry ? "delay" : "stop", | 131 | delaykey[i].retry ? "delay" : "stop", |
132 | delaykey[i].str ? delaykey[i].str : "NULL"); | 132 | delaykey[i].str ? delaykey[i].str : "NULL"); |
133 | } | 133 | } |
134 | 134 | ||
135 | /* In order to keep up with incoming data, check timeout only | 135 | /* In order to keep up with incoming data, check timeout only |
136 | * when catch up. | 136 | * when catch up. |
137 | */ | 137 | */ |
138 | do { | 138 | do { |
139 | if (tstc()) { | 139 | if (tstc()) { |
140 | if (presskey_len < presskey_max) { | 140 | if (presskey_len < presskey_max) { |
141 | presskey [presskey_len ++] = getc(); | 141 | presskey [presskey_len ++] = getc(); |
142 | } | 142 | } |
143 | else { | 143 | else { |
144 | for (i = 0; i < presskey_max - 1; i ++) | 144 | for (i = 0; i < presskey_max - 1; i ++) |
145 | presskey [i] = presskey [i + 1]; | 145 | presskey [i] = presskey [i + 1]; |
146 | 146 | ||
147 | presskey [i] = getc(); | 147 | presskey [i] = getc(); |
148 | } | 148 | } |
149 | } | 149 | } |
150 | 150 | ||
151 | for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) { | 151 | for (i = 0; i < sizeof(delaykey) / sizeof(delaykey[0]); i ++) { |
152 | if (delaykey[i].len > 0 && | 152 | if (delaykey[i].len > 0 && |
153 | presskey_len >= delaykey[i].len && | 153 | presskey_len >= delaykey[i].len && |
154 | memcmp (presskey + presskey_len - delaykey[i].len, | 154 | memcmp (presskey + presskey_len - delaykey[i].len, |
155 | delaykey[i].str, | 155 | delaykey[i].str, |
156 | delaykey[i].len) == 0) { | 156 | delaykey[i].len) == 0) { |
157 | debug_bootkeys("got %skey\n", | 157 | debug_bootkeys("got %skey\n", |
158 | delaykey[i].retry ? "delay" : | 158 | delaykey[i].retry ? "delay" : |
159 | "stop"); | 159 | "stop"); |
160 | 160 | ||
161 | # ifdef CONFIG_BOOT_RETRY_TIME | 161 | # ifdef CONFIG_BOOT_RETRY_TIME |
162 | /* don't retry auto boot */ | 162 | /* don't retry auto boot */ |
163 | if (! delaykey[i].retry) | 163 | if (! delaykey[i].retry) |
164 | retry_time = -1; | 164 | retry_time = -1; |
165 | # endif | 165 | # endif |
166 | abort = 1; | 166 | abort = 1; |
167 | } | 167 | } |
168 | } | 168 | } |
169 | } while (!abort && get_ticks() <= etime); | 169 | } while (!abort && get_ticks() <= etime); |
170 | 170 | ||
171 | if (!abort) | 171 | if (!abort) |
172 | debug_bootkeys("key timeout\n"); | 172 | debug_bootkeys("key timeout\n"); |
173 | 173 | ||
174 | #ifdef CONFIG_SILENT_CONSOLE | 174 | #ifdef CONFIG_SILENT_CONSOLE |
175 | if (abort) | 175 | if (abort) |
176 | gd->flags &= ~GD_FLG_SILENT; | 176 | gd->flags &= ~GD_FLG_SILENT; |
177 | #endif | 177 | #endif |
178 | 178 | ||
179 | return abort; | 179 | return abort; |
180 | } | 180 | } |
181 | 181 | ||
182 | # else /* !defined(CONFIG_AUTOBOOT_KEYED) */ | 182 | # else /* !defined(CONFIG_AUTOBOOT_KEYED) */ |
183 | 183 | ||
184 | #ifdef CONFIG_MENUKEY | 184 | #ifdef CONFIG_MENUKEY |
185 | static int menukey = 0; | 185 | static int menukey = 0; |
186 | #endif | 186 | #endif |
187 | 187 | ||
188 | static int abortboot_normal(int bootdelay) | 188 | static int abortboot_normal(int bootdelay) |
189 | { | 189 | { |
190 | int abort = 0; | 190 | int abort = 0; |
191 | unsigned long ts; | 191 | unsigned long ts; |
192 | 192 | ||
193 | #ifdef CONFIG_MENUPROMPT | 193 | #ifdef CONFIG_MENUPROMPT |
194 | printf(CONFIG_MENUPROMPT); | 194 | printf(CONFIG_MENUPROMPT); |
195 | #else | 195 | #else |
196 | if (bootdelay >= 0) | 196 | if (bootdelay >= 0) |
197 | printf("Hit any key to stop autoboot: %2d ", bootdelay); | 197 | printf("Hit any key to stop autoboot: %2d ", bootdelay); |
198 | #endif | 198 | #endif |
199 | 199 | ||
200 | #if defined CONFIG_ZERO_BOOTDELAY_CHECK | 200 | #if defined CONFIG_ZERO_BOOTDELAY_CHECK |
201 | /* | 201 | /* |
202 | * Check if key already pressed | 202 | * Check if key already pressed |
203 | * Don't check if bootdelay < 0 | 203 | * Don't check if bootdelay < 0 |
204 | */ | 204 | */ |
205 | if (bootdelay >= 0) { | 205 | if (bootdelay >= 0) { |
206 | if (tstc()) { /* we got a key press */ | 206 | if (tstc()) { /* we got a key press */ |
207 | (void) getc(); /* consume input */ | 207 | (void) getc(); /* consume input */ |
208 | puts ("\b\b\b 0"); | 208 | puts ("\b\b\b 0"); |
209 | abort = 1; /* don't auto boot */ | 209 | abort = 1; /* don't auto boot */ |
210 | } | 210 | } |
211 | } | 211 | } |
212 | #endif | 212 | #endif |
213 | 213 | ||
214 | while ((bootdelay > 0) && (!abort)) { | 214 | while ((bootdelay > 0) && (!abort)) { |
215 | --bootdelay; | 215 | --bootdelay; |
216 | /* delay 1000 ms */ | 216 | /* delay 1000 ms */ |
217 | ts = get_timer(0); | 217 | ts = get_timer(0); |
218 | do { | 218 | do { |
219 | if (tstc()) { /* we got a key press */ | 219 | if (tstc()) { /* we got a key press */ |
220 | abort = 1; /* don't auto boot */ | 220 | abort = 1; /* don't auto boot */ |
221 | bootdelay = 0; /* no more delay */ | 221 | bootdelay = 0; /* no more delay */ |
222 | # ifdef CONFIG_MENUKEY | 222 | # ifdef CONFIG_MENUKEY |
223 | menukey = getc(); | 223 | menukey = getc(); |
224 | # else | 224 | # else |
225 | (void) getc(); /* consume input */ | 225 | (void) getc(); /* consume input */ |
226 | # endif | 226 | # endif |
227 | break; | 227 | break; |
228 | } | 228 | } |
229 | udelay(10000); | 229 | udelay(10000); |
230 | } while (!abort && get_timer(ts) < 1000); | 230 | } while (!abort && get_timer(ts) < 1000); |
231 | 231 | ||
232 | printf("\b\b\b%2d ", bootdelay); | 232 | printf("\b\b\b%2d ", bootdelay); |
233 | } | 233 | } |
234 | 234 | ||
235 | putc('\n'); | 235 | putc('\n'); |
236 | 236 | ||
237 | #ifdef CONFIG_SILENT_CONSOLE | 237 | #ifdef CONFIG_SILENT_CONSOLE |
238 | if (abort) | 238 | if (abort) |
239 | gd->flags &= ~GD_FLG_SILENT; | 239 | gd->flags &= ~GD_FLG_SILENT; |
240 | #endif | 240 | #endif |
241 | 241 | ||
242 | return abort; | 242 | return abort; |
243 | } | 243 | } |
244 | # endif /* CONFIG_AUTOBOOT_KEYED */ | 244 | # endif /* CONFIG_AUTOBOOT_KEYED */ |
245 | 245 | ||
246 | static int abortboot(int bootdelay) | 246 | static int abortboot(int bootdelay) |
247 | { | 247 | { |
248 | #ifdef CONFIG_AUTOBOOT_KEYED | 248 | #ifdef CONFIG_AUTOBOOT_KEYED |
249 | return abortboot_keyed(bootdelay); | 249 | return abortboot_keyed(bootdelay); |
250 | #else | 250 | #else |
251 | return abortboot_normal(bootdelay); | 251 | return abortboot_normal(bootdelay); |
252 | #endif | 252 | #endif |
253 | } | 253 | } |
254 | #endif /* CONFIG_BOOTDELAY */ | 254 | #endif /* CONFIG_BOOTDELAY */ |
255 | 255 | ||
256 | /* | 256 | /* |
257 | * Runs the given boot command securely. Specifically: | 257 | * Runs the given boot command securely. Specifically: |
258 | * - Doesn't run the command with the shell (run_command or parse_string_outer), | 258 | * - Doesn't run the command with the shell (run_command or parse_string_outer), |
259 | * since that's a lot of code surface that an attacker might exploit. | 259 | * since that's a lot of code surface that an attacker might exploit. |
260 | * Because of this, we don't do any argument parsing--the secure boot command | 260 | * Because of this, we don't do any argument parsing--the secure boot command |
261 | * has to be a full-fledged u-boot command. | 261 | * has to be a full-fledged u-boot command. |
262 | * - Doesn't check for keypresses before booting, since that could be a | 262 | * - Doesn't check for keypresses before booting, since that could be a |
263 | * security hole; also disables Ctrl-C. | 263 | * security hole; also disables Ctrl-C. |
264 | * - Doesn't allow the command to return. | 264 | * - Doesn't allow the command to return. |
265 | * | 265 | * |
266 | * Upon any failures, this function will drop into an infinite loop after | 266 | * Upon any failures, this function will drop into an infinite loop after |
267 | * printing the error message to console. | 267 | * printing the error message to console. |
268 | */ | 268 | */ |
269 | 269 | ||
270 | #if defined(CONFIG_BOOTDELAY) && defined(CONFIG_OF_CONTROL) | 270 | #if defined(CONFIG_BOOTDELAY) && defined(CONFIG_OF_CONTROL) |
271 | static void secure_boot_cmd(char *cmd) | 271 | static void secure_boot_cmd(char *cmd) |
272 | { | 272 | { |
273 | cmd_tbl_t *cmdtp; | 273 | cmd_tbl_t *cmdtp; |
274 | int rc; | 274 | int rc; |
275 | 275 | ||
276 | if (!cmd) { | 276 | if (!cmd) { |
277 | printf("## Error: Secure boot command not specified\n"); | 277 | printf("## Error: Secure boot command not specified\n"); |
278 | goto err; | 278 | goto err; |
279 | } | 279 | } |
280 | 280 | ||
281 | /* Disable Ctrl-C just in case some command is used that checks it. */ | 281 | /* Disable Ctrl-C just in case some command is used that checks it. */ |
282 | disable_ctrlc(1); | 282 | disable_ctrlc(1); |
283 | 283 | ||
284 | /* Find the command directly. */ | 284 | /* Find the command directly. */ |
285 | cmdtp = find_cmd(cmd); | 285 | cmdtp = find_cmd(cmd); |
286 | if (!cmdtp) { | 286 | if (!cmdtp) { |
287 | printf("## Error: \"%s\" not defined\n", cmd); | 287 | printf("## Error: \"%s\" not defined\n", cmd); |
288 | goto err; | 288 | goto err; |
289 | } | 289 | } |
290 | 290 | ||
291 | /* Run the command, forcing no flags and faking argc and argv. */ | 291 | /* Run the command, forcing no flags and faking argc and argv. */ |
292 | rc = (cmdtp->cmd)(cmdtp, 0, 1, &cmd); | 292 | rc = (cmdtp->cmd)(cmdtp, 0, 1, &cmd); |
293 | 293 | ||
294 | /* Shouldn't ever return from boot command. */ | 294 | /* Shouldn't ever return from boot command. */ |
295 | printf("## Error: \"%s\" returned (code %d)\n", cmd, rc); | 295 | printf("## Error: \"%s\" returned (code %d)\n", cmd, rc); |
296 | 296 | ||
297 | err: | 297 | err: |
298 | /* | 298 | /* |
299 | * Not a whole lot to do here. Rebooting won't help much, since we'll | 299 | * Not a whole lot to do here. Rebooting won't help much, since we'll |
300 | * just end up right back here. Just loop. | 300 | * just end up right back here. Just loop. |
301 | */ | 301 | */ |
302 | hang(); | 302 | hang(); |
303 | } | 303 | } |
304 | 304 | ||
305 | static void process_fdt_options(const void *blob) | 305 | static void process_fdt_options(const void *blob) |
306 | { | 306 | { |
307 | ulong addr; | 307 | ulong addr; |
308 | 308 | ||
309 | /* Add an env variable to point to a kernel payload, if available */ | 309 | /* Add an env variable to point to a kernel payload, if available */ |
310 | addr = fdtdec_get_config_int(gd->fdt_blob, "kernel-offset", 0); | 310 | addr = fdtdec_get_config_int(gd->fdt_blob, "kernel-offset", 0); |
311 | if (addr) | 311 | if (addr) |
312 | setenv_addr("kernaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr)); | 312 | setenv_addr("kernaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr)); |
313 | 313 | ||
314 | /* Add an env variable to point to a root disk, if available */ | 314 | /* Add an env variable to point to a root disk, if available */ |
315 | addr = fdtdec_get_config_int(gd->fdt_blob, "rootdisk-offset", 0); | 315 | addr = fdtdec_get_config_int(gd->fdt_blob, "rootdisk-offset", 0); |
316 | if (addr) | 316 | if (addr) |
317 | setenv_addr("rootaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr)); | 317 | setenv_addr("rootaddr", (void *)(CONFIG_SYS_TEXT_BASE + addr)); |
318 | } | 318 | } |
319 | #endif /* CONFIG_OF_CONTROL */ | 319 | #endif /* CONFIG_OF_CONTROL */ |
320 | 320 | ||
321 | #ifdef CONFIG_BOOTDELAY | 321 | #ifdef CONFIG_BOOTDELAY |
322 | static void process_boot_delay(void) | 322 | static void process_boot_delay(void) |
323 | { | 323 | { |
324 | #ifdef CONFIG_OF_CONTROL | 324 | #ifdef CONFIG_OF_CONTROL |
325 | char *env; | 325 | char *env; |
326 | #endif | 326 | #endif |
327 | char *s; | 327 | char *s; |
328 | int bootdelay; | 328 | int bootdelay; |
329 | #ifdef CONFIG_BOOTCOUNT_LIMIT | 329 | #ifdef CONFIG_BOOTCOUNT_LIMIT |
330 | unsigned long bootcount = 0; | 330 | unsigned long bootcount = 0; |
331 | unsigned long bootlimit = 0; | 331 | unsigned long bootlimit = 0; |
332 | #endif /* CONFIG_BOOTCOUNT_LIMIT */ | 332 | #endif /* CONFIG_BOOTCOUNT_LIMIT */ |
333 | 333 | ||
334 | #ifdef CONFIG_BOOTCOUNT_LIMIT | 334 | #ifdef CONFIG_BOOTCOUNT_LIMIT |
335 | bootcount = bootcount_load(); | 335 | bootcount = bootcount_load(); |
336 | bootcount++; | 336 | bootcount++; |
337 | bootcount_store (bootcount); | 337 | bootcount_store (bootcount); |
338 | setenv_ulong("bootcount", bootcount); | 338 | setenv_ulong("bootcount", bootcount); |
339 | bootlimit = getenv_ulong("bootlimit", 10, 0); | 339 | bootlimit = getenv_ulong("bootlimit", 10, 0); |
340 | #endif /* CONFIG_BOOTCOUNT_LIMIT */ | 340 | #endif /* CONFIG_BOOTCOUNT_LIMIT */ |
341 | 341 | ||
342 | s = getenv ("bootdelay"); | 342 | s = getenv ("bootdelay"); |
343 | bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; | 343 | bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; |
344 | 344 | ||
345 | #ifdef CONFIG_OF_CONTROL | 345 | #ifdef CONFIG_OF_CONTROL |
346 | bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay", | 346 | bootdelay = fdtdec_get_config_int(gd->fdt_blob, "bootdelay", |
347 | bootdelay); | 347 | bootdelay); |
348 | #endif | 348 | #endif |
349 | 349 | ||
350 | debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay); | 350 | debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay); |
351 | 351 | ||
352 | #if defined(CONFIG_MENU_SHOW) | 352 | #if defined(CONFIG_MENU_SHOW) |
353 | bootdelay = menu_show(bootdelay); | 353 | bootdelay = menu_show(bootdelay); |
354 | #endif | 354 | #endif |
355 | # ifdef CONFIG_BOOT_RETRY_TIME | 355 | # ifdef CONFIG_BOOT_RETRY_TIME |
356 | init_cmd_timeout (); | 356 | init_cmd_timeout (); |
357 | # endif /* CONFIG_BOOT_RETRY_TIME */ | 357 | # endif /* CONFIG_BOOT_RETRY_TIME */ |
358 | 358 | ||
359 | #ifdef CONFIG_POST | 359 | #ifdef CONFIG_POST |
360 | if (gd->flags & GD_FLG_POSTFAIL) { | 360 | if (gd->flags & GD_FLG_POSTFAIL) { |
361 | s = getenv("failbootcmd"); | 361 | s = getenv("failbootcmd"); |
362 | } | 362 | } |
363 | else | 363 | else |
364 | #endif /* CONFIG_POST */ | 364 | #endif /* CONFIG_POST */ |
365 | #ifdef CONFIG_BOOTCOUNT_LIMIT | 365 | #ifdef CONFIG_BOOTCOUNT_LIMIT |
366 | if (bootlimit && (bootcount > bootlimit)) { | 366 | if (bootlimit && (bootcount > bootlimit)) { |
367 | printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n", | 367 | printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n", |
368 | (unsigned)bootlimit); | 368 | (unsigned)bootlimit); |
369 | s = getenv ("altbootcmd"); | 369 | s = getenv ("altbootcmd"); |
370 | } | 370 | } |
371 | else | 371 | else |
372 | #endif /* CONFIG_BOOTCOUNT_LIMIT */ | 372 | #endif /* CONFIG_BOOTCOUNT_LIMIT */ |
373 | s = getenv ("bootcmd"); | 373 | s = getenv ("bootcmd"); |
374 | #ifdef CONFIG_OF_CONTROL | 374 | #ifdef CONFIG_OF_CONTROL |
375 | /* Allow the fdt to override the boot command */ | 375 | /* Allow the fdt to override the boot command */ |
376 | env = fdtdec_get_config_string(gd->fdt_blob, "bootcmd"); | 376 | env = fdtdec_get_config_string(gd->fdt_blob, "bootcmd"); |
377 | if (env) | 377 | if (env) |
378 | s = env; | 378 | s = env; |
379 | 379 | ||
380 | process_fdt_options(gd->fdt_blob); | 380 | process_fdt_options(gd->fdt_blob); |
381 | 381 | ||
382 | /* | 382 | /* |
383 | * If the bootsecure option was chosen, use secure_boot_cmd(). | 383 | * If the bootsecure option was chosen, use secure_boot_cmd(). |
384 | * Always use 'env' in this case, since bootsecure requres that the | 384 | * Always use 'env' in this case, since bootsecure requres that the |
385 | * bootcmd was specified in the FDT too. | 385 | * bootcmd was specified in the FDT too. |
386 | */ | 386 | */ |
387 | if (fdtdec_get_config_int(gd->fdt_blob, "bootsecure", 0)) | 387 | if (fdtdec_get_config_int(gd->fdt_blob, "bootsecure", 0)) |
388 | secure_boot_cmd(env); | 388 | secure_boot_cmd(env); |
389 | 389 | ||
390 | #endif /* CONFIG_OF_CONTROL */ | 390 | #endif /* CONFIG_OF_CONTROL */ |
391 | 391 | ||
392 | debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); | 392 | debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); |
393 | 393 | ||
394 | if (bootdelay != -1 && s && !abortboot(bootdelay)) { | 394 | if (bootdelay != -1 && s && !abortboot(bootdelay)) { |
395 | #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC) | 395 | #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC) |
396 | int prev = disable_ctrlc(1); /* disable Control C checking */ | 396 | int prev = disable_ctrlc(1); /* disable Control C checking */ |
397 | #endif | 397 | #endif |
398 | 398 | ||
399 | run_command_list(s, -1, 0); | 399 | run_command_list(s, -1, 0); |
400 | 400 | ||
401 | #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC) | 401 | #if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC) |
402 | disable_ctrlc(prev); /* restore Control C checking */ | 402 | disable_ctrlc(prev); /* restore Control C checking */ |
403 | #endif | 403 | #endif |
404 | } | 404 | } |
405 | 405 | ||
406 | #ifdef CONFIG_MENUKEY | 406 | #ifdef CONFIG_MENUKEY |
407 | if (menukey == CONFIG_MENUKEY) { | 407 | if (menukey == CONFIG_MENUKEY) { |
408 | s = getenv("menucmd"); | 408 | s = getenv("menucmd"); |
409 | if (s) | 409 | if (s) |
410 | run_command_list(s, -1, 0); | 410 | run_command_list(s, -1, 0); |
411 | } | 411 | } |
412 | #endif /* CONFIG_MENUKEY */ | 412 | #endif /* CONFIG_MENUKEY */ |
413 | } | 413 | } |
414 | #endif /* CONFIG_BOOTDELAY */ | 414 | #endif /* CONFIG_BOOTDELAY */ |
415 | 415 | ||
416 | void main_loop(void) | 416 | void main_loop(void) |
417 | { | 417 | { |
418 | #ifndef CONFIG_SYS_HUSH_PARSER | 418 | #ifndef CONFIG_SYS_HUSH_PARSER |
419 | static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; | 419 | static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; |
420 | int len; | 420 | int len; |
421 | int rc = 1; | 421 | int rc = 1; |
422 | int flag; | 422 | int flag; |
423 | #endif | 423 | #endif |
424 | #ifdef CONFIG_PREBOOT | 424 | #ifdef CONFIG_PREBOOT |
425 | char *p; | 425 | char *p; |
426 | #endif | 426 | #endif |
427 | 427 | ||
428 | bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop"); | 428 | bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop"); |
429 | 429 | ||
430 | #ifndef CONFIG_SYS_GENERIC_BOARD | 430 | #ifndef CONFIG_SYS_GENERIC_BOARD |
431 | puts("Warning: Your board does not use generic board. Please read\n"); | 431 | puts("Warning: Your board does not use generic board. Please read\n"); |
432 | puts("doc/README.generic-board and take action. Boards not\n"); | 432 | puts("doc/README.generic-board and take action. Boards not\n"); |
433 | puts("upgraded by the late 2014 may break or be removed.\n"); | 433 | puts("upgraded by the late 2014 may break or be removed.\n"); |
434 | #endif | 434 | #endif |
435 | 435 | ||
436 | #ifdef CONFIG_MODEM_SUPPORT | 436 | #ifdef CONFIG_MODEM_SUPPORT |
437 | debug("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init); | 437 | debug("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init); |
438 | if (do_mdm_init) { | 438 | if (do_mdm_init) { |
439 | char *str = strdup(getenv("mdm_cmd")); | 439 | char *str = strdup(getenv("mdm_cmd")); |
440 | setenv("preboot", str); /* set or delete definition */ | 440 | setenv("preboot", str); /* set or delete definition */ |
441 | if (str != NULL) | 441 | if (str != NULL) |
442 | free(str); | 442 | free(str); |
443 | mdm_init(); /* wait for modem connection */ | 443 | mdm_init(); /* wait for modem connection */ |
444 | } | 444 | } |
445 | #endif /* CONFIG_MODEM_SUPPORT */ | 445 | #endif /* CONFIG_MODEM_SUPPORT */ |
446 | 446 | ||
447 | #ifdef CONFIG_VERSION_VARIABLE | 447 | #ifdef CONFIG_VERSION_VARIABLE |
448 | { | 448 | { |
449 | setenv("ver", version_string); /* set version variable */ | 449 | setenv("ver", version_string); /* set version variable */ |
450 | } | 450 | } |
451 | #endif /* CONFIG_VERSION_VARIABLE */ | 451 | #endif /* CONFIG_VERSION_VARIABLE */ |
452 | 452 | ||
453 | #ifdef CONFIG_SYS_HUSH_PARSER | 453 | #ifdef CONFIG_SYS_HUSH_PARSER |
454 | u_boot_hush_start(); | 454 | u_boot_hush_start(); |
455 | #endif | 455 | #endif |
456 | 456 | ||
457 | #if defined(CONFIG_HUSH_INIT_VAR) | 457 | #if defined(CONFIG_HUSH_INIT_VAR) |
458 | hush_init_var(); | 458 | hush_init_var(); |
459 | #endif | 459 | #endif |
460 | 460 | ||
461 | #ifdef CONFIG_PREBOOT | 461 | #ifdef CONFIG_PREBOOT |
462 | p = getenv("preboot"); | 462 | p = getenv("preboot"); |
463 | if (p != NULL) { | 463 | if (p != NULL) { |
464 | # ifdef CONFIG_AUTOBOOT_KEYED | 464 | # ifdef CONFIG_AUTOBOOT_KEYED |
465 | int prev = disable_ctrlc(1); /* disable Control C checking */ | 465 | int prev = disable_ctrlc(1); /* disable Control C checking */ |
466 | # endif | 466 | # endif |
467 | 467 | ||
468 | run_command_list(p, -1, 0); | 468 | run_command_list(p, -1, 0); |
469 | 469 | ||
470 | # ifdef CONFIG_AUTOBOOT_KEYED | 470 | # ifdef CONFIG_AUTOBOOT_KEYED |
471 | disable_ctrlc(prev); /* restore Control C checking */ | 471 | disable_ctrlc(prev); /* restore Control C checking */ |
472 | # endif | 472 | # endif |
473 | } | 473 | } |
474 | #endif /* CONFIG_PREBOOT */ | 474 | #endif /* CONFIG_PREBOOT */ |
475 | 475 | ||
476 | #if defined(CONFIG_UPDATE_TFTP) | 476 | #if defined(CONFIG_UPDATE_TFTP) |
477 | update_tftp(0UL); | 477 | update_tftp(0UL); |
478 | #endif /* CONFIG_UPDATE_TFTP */ | 478 | #endif /* CONFIG_UPDATE_TFTP */ |
479 | 479 | ||
480 | #ifdef CONFIG_BOOTDELAY | 480 | #ifdef CONFIG_BOOTDELAY |
481 | process_boot_delay(); | 481 | process_boot_delay(); |
482 | #endif | 482 | #endif |
483 | /* | 483 | /* |
484 | * Main Loop for Monitor Command Processing | 484 | * Main Loop for Monitor Command Processing |
485 | */ | 485 | */ |
486 | #ifdef CONFIG_SYS_HUSH_PARSER | 486 | #ifdef CONFIG_SYS_HUSH_PARSER |
487 | parse_file_outer(); | 487 | parse_file_outer(); |
488 | /* This point is never reached */ | 488 | /* This point is never reached */ |
489 | for (;;); | 489 | for (;;); |
490 | #else | 490 | #else |
491 | for (;;) { | 491 | for (;;) { |
492 | #ifdef CONFIG_BOOT_RETRY_TIME | 492 | #ifdef CONFIG_BOOT_RETRY_TIME |
493 | if (rc >= 0) { | 493 | if (rc >= 0) { |
494 | /* Saw enough of a valid command to | 494 | /* Saw enough of a valid command to |
495 | * restart the timeout. | 495 | * restart the timeout. |
496 | */ | 496 | */ |
497 | reset_cmd_timeout(); | 497 | reset_cmd_timeout(); |
498 | } | 498 | } |
499 | #endif | 499 | #endif |
500 | len = readline (CONFIG_SYS_PROMPT); | 500 | len = readline (CONFIG_SYS_PROMPT); |
501 | 501 | ||
502 | flag = 0; /* assume no special flags for now */ | 502 | flag = 0; /* assume no special flags for now */ |
503 | if (len > 0) | 503 | if (len > 0) |
504 | strcpy (lastcommand, console_buffer); | 504 | strcpy (lastcommand, console_buffer); |
505 | else if (len == 0) | 505 | else if (len == 0) |
506 | flag |= CMD_FLAG_REPEAT; | 506 | flag |= CMD_FLAG_REPEAT; |
507 | #ifdef CONFIG_BOOT_RETRY_TIME | 507 | #ifdef CONFIG_BOOT_RETRY_TIME |
508 | else if (len == -2) { | 508 | else if (len == -2) { |
509 | /* -2 means timed out, retry autoboot | 509 | /* -2 means timed out, retry autoboot |
510 | */ | 510 | */ |
511 | puts ("\nTimed out waiting for command\n"); | 511 | puts ("\nTimed out waiting for command\n"); |
512 | # ifdef CONFIG_RESET_TO_RETRY | 512 | # ifdef CONFIG_RESET_TO_RETRY |
513 | /* Reinit board to run initialization code again */ | 513 | /* Reinit board to run initialization code again */ |
514 | do_reset (NULL, 0, 0, NULL); | 514 | do_reset (NULL, 0, 0, NULL); |
515 | # else | 515 | # else |
516 | return; /* retry autoboot */ | 516 | return; /* retry autoboot */ |
517 | # endif | 517 | # endif |
518 | } | 518 | } |
519 | #endif | 519 | #endif |
520 | 520 | ||
521 | if (len == -1) | 521 | if (len == -1) |
522 | puts ("<INTERRUPT>\n"); | 522 | puts ("<INTERRUPT>\n"); |
523 | else | 523 | else |
524 | rc = run_command(lastcommand, flag); | 524 | rc = run_command(lastcommand, flag); |
525 | 525 | ||
526 | if (rc <= 0) { | 526 | if (rc <= 0) { |
527 | /* invalid command or not repeatable, forget it */ | 527 | /* invalid command or not repeatable, forget it */ |
528 | lastcommand[0] = 0; | 528 | lastcommand[0] = 0; |
529 | } | 529 | } |
530 | } | 530 | } |
531 | #endif /*CONFIG_SYS_HUSH_PARSER*/ | 531 | #endif /*CONFIG_SYS_HUSH_PARSER*/ |
532 | } | 532 | } |
533 | 533 | ||
534 | #ifdef CONFIG_BOOT_RETRY_TIME | 534 | #ifdef CONFIG_BOOT_RETRY_TIME |
535 | /*************************************************************************** | 535 | /*************************************************************************** |
536 | * initialize command line timeout | 536 | * initialize command line timeout |
537 | */ | 537 | */ |
538 | void init_cmd_timeout(void) | 538 | void init_cmd_timeout(void) |
539 | { | 539 | { |
540 | char *s = getenv ("bootretry"); | 540 | char *s = getenv ("bootretry"); |
541 | 541 | ||
542 | if (s != NULL) | 542 | if (s != NULL) |
543 | retry_time = (int)simple_strtol(s, NULL, 10); | 543 | retry_time = (int)simple_strtol(s, NULL, 10); |
544 | else | 544 | else |
545 | retry_time = CONFIG_BOOT_RETRY_TIME; | 545 | retry_time = CONFIG_BOOT_RETRY_TIME; |
546 | 546 | ||
547 | if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN) | 547 | if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN) |
548 | retry_time = CONFIG_BOOT_RETRY_MIN; | 548 | retry_time = CONFIG_BOOT_RETRY_MIN; |
549 | } | 549 | } |
550 | 550 | ||
551 | /*************************************************************************** | 551 | /*************************************************************************** |
552 | * reset command line timeout to retry_time seconds | 552 | * reset command line timeout to retry_time seconds |
553 | */ | 553 | */ |
554 | void reset_cmd_timeout(void) | 554 | void reset_cmd_timeout(void) |
555 | { | 555 | { |
556 | endtime = endtick(retry_time); | 556 | endtime = endtick(retry_time); |
557 | } | 557 | } |
558 | #endif | 558 | #endif |
559 | 559 | ||
560 | #ifdef CONFIG_CMDLINE_EDITING | 560 | #ifdef CONFIG_CMDLINE_EDITING |
561 | 561 | ||
562 | /* | 562 | /* |
563 | * cmdline-editing related codes from vivi. | 563 | * cmdline-editing related codes from vivi. |
564 | * Author: Janghoon Lyu <nandy@mizi.com> | 564 | * Author: Janghoon Lyu <nandy@mizi.com> |
565 | */ | 565 | */ |
566 | 566 | ||
567 | #define putnstr(str,n) do { \ | 567 | #define putnstr(str,n) do { \ |
568 | printf ("%.*s", (int)n, str); \ | 568 | printf ("%.*s", (int)n, str); \ |
569 | } while (0) | 569 | } while (0) |
570 | 570 | ||
571 | #define CTL_CH(c) ((c) - 'a' + 1) | 571 | #define CTL_CH(c) ((c) - 'a' + 1) |
572 | #define CTL_BACKSPACE ('\b') | 572 | #define CTL_BACKSPACE ('\b') |
573 | #define DEL ((char)255) | 573 | #define DEL ((char)255) |
574 | #define DEL7 ((char)127) | 574 | #define DEL7 ((char)127) |
575 | #define CREAD_HIST_CHAR ('!') | 575 | #define CREAD_HIST_CHAR ('!') |
576 | 576 | ||
577 | #define getcmd_putch(ch) putc(ch) | 577 | #define getcmd_putch(ch) putc(ch) |
578 | #define getcmd_getch() getc() | 578 | #define getcmd_getch() getc() |
579 | #define getcmd_cbeep() getcmd_putch('\a') | 579 | #define getcmd_cbeep() getcmd_putch('\a') |
580 | 580 | ||
581 | #define HIST_MAX 20 | 581 | #define HIST_MAX 20 |
582 | #define HIST_SIZE CONFIG_SYS_CBSIZE | 582 | #define HIST_SIZE CONFIG_SYS_CBSIZE |
583 | 583 | ||
584 | static int hist_max; | 584 | static int hist_max; |
585 | static int hist_add_idx; | 585 | static int hist_add_idx; |
586 | static int hist_cur = -1; | 586 | static int hist_cur = -1; |
587 | static unsigned hist_num; | 587 | static unsigned hist_num; |
588 | 588 | ||
589 | static char *hist_list[HIST_MAX]; | 589 | static char *hist_list[HIST_MAX]; |
590 | static char hist_lines[HIST_MAX][HIST_SIZE + 1]; /* Save room for NULL */ | 590 | static char hist_lines[HIST_MAX][HIST_SIZE + 1]; /* Save room for NULL */ |
591 | 591 | ||
592 | #define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1) | 592 | #define add_idx_minus_one() ((hist_add_idx == 0) ? hist_max : hist_add_idx-1) |
593 | 593 | ||
594 | static void hist_init(void) | 594 | static void hist_init(void) |
595 | { | 595 | { |
596 | int i; | 596 | int i; |
597 | 597 | ||
598 | hist_max = 0; | 598 | hist_max = 0; |
599 | hist_add_idx = 0; | 599 | hist_add_idx = 0; |
600 | hist_cur = -1; | 600 | hist_cur = -1; |
601 | hist_num = 0; | 601 | hist_num = 0; |
602 | 602 | ||
603 | for (i = 0; i < HIST_MAX; i++) { | 603 | for (i = 0; i < HIST_MAX; i++) { |
604 | hist_list[i] = hist_lines[i]; | 604 | hist_list[i] = hist_lines[i]; |
605 | hist_list[i][0] = '\0'; | 605 | hist_list[i][0] = '\0'; |
606 | } | 606 | } |
607 | } | 607 | } |
608 | 608 | ||
609 | static void cread_add_to_hist(char *line) | 609 | static void cread_add_to_hist(char *line) |
610 | { | 610 | { |
611 | strcpy(hist_list[hist_add_idx], line); | 611 | strcpy(hist_list[hist_add_idx], line); |
612 | 612 | ||
613 | if (++hist_add_idx >= HIST_MAX) | 613 | if (++hist_add_idx >= HIST_MAX) |
614 | hist_add_idx = 0; | 614 | hist_add_idx = 0; |
615 | 615 | ||
616 | if (hist_add_idx > hist_max) | 616 | if (hist_add_idx > hist_max) |
617 | hist_max = hist_add_idx; | 617 | hist_max = hist_add_idx; |
618 | 618 | ||
619 | hist_num++; | 619 | hist_num++; |
620 | } | 620 | } |
621 | 621 | ||
622 | static char* hist_prev(void) | 622 | static char* hist_prev(void) |
623 | { | 623 | { |
624 | char *ret; | 624 | char *ret; |
625 | int old_cur; | 625 | int old_cur; |
626 | 626 | ||
627 | if (hist_cur < 0) | 627 | if (hist_cur < 0) |
628 | return NULL; | 628 | return NULL; |
629 | 629 | ||
630 | old_cur = hist_cur; | 630 | old_cur = hist_cur; |
631 | if (--hist_cur < 0) | 631 | if (--hist_cur < 0) |
632 | hist_cur = hist_max; | 632 | hist_cur = hist_max; |
633 | 633 | ||
634 | if (hist_cur == hist_add_idx) { | 634 | if (hist_cur == hist_add_idx) { |
635 | hist_cur = old_cur; | 635 | hist_cur = old_cur; |
636 | ret = NULL; | 636 | ret = NULL; |
637 | } else | 637 | } else |
638 | ret = hist_list[hist_cur]; | 638 | ret = hist_list[hist_cur]; |
639 | 639 | ||
640 | return (ret); | 640 | return (ret); |
641 | } | 641 | } |
642 | 642 | ||
643 | static char* hist_next(void) | 643 | static char* hist_next(void) |
644 | { | 644 | { |
645 | char *ret; | 645 | char *ret; |
646 | 646 | ||
647 | if (hist_cur < 0) | 647 | if (hist_cur < 0) |
648 | return NULL; | 648 | return NULL; |
649 | 649 | ||
650 | if (hist_cur == hist_add_idx) | 650 | if (hist_cur == hist_add_idx) |
651 | return NULL; | 651 | return NULL; |
652 | 652 | ||
653 | if (++hist_cur > hist_max) | 653 | if (++hist_cur > hist_max) |
654 | hist_cur = 0; | 654 | hist_cur = 0; |
655 | 655 | ||
656 | if (hist_cur == hist_add_idx) { | 656 | if (hist_cur == hist_add_idx) { |
657 | ret = ""; | 657 | ret = ""; |
658 | } else | 658 | } else |
659 | ret = hist_list[hist_cur]; | 659 | ret = hist_list[hist_cur]; |
660 | 660 | ||
661 | return (ret); | 661 | return (ret); |
662 | } | 662 | } |
663 | 663 | ||
664 | #ifndef CONFIG_CMDLINE_EDITING | 664 | #ifndef CONFIG_CMDLINE_EDITING |
665 | static void cread_print_hist_list(void) | 665 | static void cread_print_hist_list(void) |
666 | { | 666 | { |
667 | int i; | 667 | int i; |
668 | unsigned long n; | 668 | unsigned long n; |
669 | 669 | ||
670 | n = hist_num - hist_max; | 670 | n = hist_num - hist_max; |
671 | 671 | ||
672 | i = hist_add_idx + 1; | 672 | i = hist_add_idx + 1; |
673 | while (1) { | 673 | while (1) { |
674 | if (i > hist_max) | 674 | if (i > hist_max) |
675 | i = 0; | 675 | i = 0; |
676 | if (i == hist_add_idx) | 676 | if (i == hist_add_idx) |
677 | break; | 677 | break; |
678 | printf("%s\n", hist_list[i]); | 678 | printf("%s\n", hist_list[i]); |
679 | n++; | 679 | n++; |
680 | i++; | 680 | i++; |
681 | } | 681 | } |
682 | } | 682 | } |
683 | #endif /* CONFIG_CMDLINE_EDITING */ | 683 | #endif /* CONFIG_CMDLINE_EDITING */ |
684 | 684 | ||
685 | #define BEGINNING_OF_LINE() { \ | 685 | #define BEGINNING_OF_LINE() { \ |
686 | while (num) { \ | 686 | while (num) { \ |
687 | getcmd_putch(CTL_BACKSPACE); \ | 687 | getcmd_putch(CTL_BACKSPACE); \ |
688 | num--; \ | 688 | num--; \ |
689 | } \ | 689 | } \ |
690 | } | 690 | } |
691 | 691 | ||
692 | #define ERASE_TO_EOL() { \ | 692 | #define ERASE_TO_EOL() { \ |
693 | if (num < eol_num) { \ | 693 | if (num < eol_num) { \ |
694 | printf("%*s", (int)(eol_num - num), ""); \ | 694 | printf("%*s", (int)(eol_num - num), ""); \ |
695 | do { \ | 695 | do { \ |
696 | getcmd_putch(CTL_BACKSPACE); \ | 696 | getcmd_putch(CTL_BACKSPACE); \ |
697 | } while (--eol_num > num); \ | 697 | } while (--eol_num > num); \ |
698 | } \ | 698 | } \ |
699 | } | 699 | } |
700 | 700 | ||
701 | #define REFRESH_TO_EOL() { \ | 701 | #define REFRESH_TO_EOL() { \ |
702 | if (num < eol_num) { \ | 702 | if (num < eol_num) { \ |
703 | wlen = eol_num - num; \ | 703 | wlen = eol_num - num; \ |
704 | putnstr(buf + num, wlen); \ | 704 | putnstr(buf + num, wlen); \ |
705 | num = eol_num; \ | 705 | num = eol_num; \ |
706 | } \ | 706 | } \ |
707 | } | 707 | } |
708 | 708 | ||
709 | static void cread_add_char(char ichar, int insert, unsigned long *num, | 709 | static void cread_add_char(char ichar, int insert, unsigned long *num, |
710 | unsigned long *eol_num, char *buf, unsigned long len) | 710 | unsigned long *eol_num, char *buf, unsigned long len) |
711 | { | 711 | { |
712 | unsigned long wlen; | 712 | unsigned long wlen; |
713 | 713 | ||
714 | /* room ??? */ | 714 | /* room ??? */ |
715 | if (insert || *num == *eol_num) { | 715 | if (insert || *num == *eol_num) { |
716 | if (*eol_num > len - 1) { | 716 | if (*eol_num > len - 1) { |
717 | getcmd_cbeep(); | 717 | getcmd_cbeep(); |
718 | return; | 718 | return; |
719 | } | 719 | } |
720 | (*eol_num)++; | 720 | (*eol_num)++; |
721 | } | 721 | } |
722 | 722 | ||
723 | if (insert) { | 723 | if (insert) { |
724 | wlen = *eol_num - *num; | 724 | wlen = *eol_num - *num; |
725 | if (wlen > 1) { | 725 | if (wlen > 1) { |
726 | memmove(&buf[*num+1], &buf[*num], wlen-1); | 726 | memmove(&buf[*num+1], &buf[*num], wlen-1); |
727 | } | 727 | } |
728 | 728 | ||
729 | buf[*num] = ichar; | 729 | buf[*num] = ichar; |
730 | putnstr(buf + *num, wlen); | 730 | putnstr(buf + *num, wlen); |
731 | (*num)++; | 731 | (*num)++; |
732 | while (--wlen) { | 732 | while (--wlen) { |
733 | getcmd_putch(CTL_BACKSPACE); | 733 | getcmd_putch(CTL_BACKSPACE); |
734 | } | 734 | } |
735 | } else { | 735 | } else { |
736 | /* echo the character */ | 736 | /* echo the character */ |
737 | wlen = 1; | 737 | wlen = 1; |
738 | buf[*num] = ichar; | 738 | buf[*num] = ichar; |
739 | putnstr(buf + *num, wlen); | 739 | putnstr(buf + *num, wlen); |
740 | (*num)++; | 740 | (*num)++; |
741 | } | 741 | } |
742 | } | 742 | } |
743 | 743 | ||
744 | static void cread_add_str(char *str, int strsize, int insert, unsigned long *num, | 744 | static void cread_add_str(char *str, int strsize, int insert, unsigned long *num, |
745 | unsigned long *eol_num, char *buf, unsigned long len) | 745 | unsigned long *eol_num, char *buf, unsigned long len) |
746 | { | 746 | { |
747 | while (strsize--) { | 747 | while (strsize--) { |
748 | cread_add_char(*str, insert, num, eol_num, buf, len); | 748 | cread_add_char(*str, insert, num, eol_num, buf, len); |
749 | str++; | 749 | str++; |
750 | } | 750 | } |
751 | } | 751 | } |
752 | 752 | ||
753 | static int cread_line(const char *const prompt, char *buf, unsigned int *len, | 753 | static int cread_line(const char *const prompt, char *buf, unsigned int *len, |
754 | int timeout) | 754 | int timeout) |
755 | { | 755 | { |
756 | unsigned long num = 0; | 756 | unsigned long num = 0; |
757 | unsigned long eol_num = 0; | 757 | unsigned long eol_num = 0; |
758 | unsigned long wlen; | 758 | unsigned long wlen; |
759 | char ichar; | 759 | char ichar; |
760 | int insert = 1; | 760 | int insert = 1; |
761 | int esc_len = 0; | 761 | int esc_len = 0; |
762 | char esc_save[8]; | 762 | char esc_save[8]; |
763 | int init_len = strlen(buf); | 763 | int init_len = strlen(buf); |
764 | int first = 1; | 764 | int first = 1; |
765 | 765 | ||
766 | if (init_len) | 766 | if (init_len) |
767 | cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len); | 767 | cread_add_str(buf, init_len, 1, &num, &eol_num, buf, *len); |
768 | 768 | ||
769 | while (1) { | 769 | while (1) { |
770 | #ifdef CONFIG_BOOT_RETRY_TIME | 770 | #ifdef CONFIG_BOOT_RETRY_TIME |
771 | while (!tstc()) { /* while no incoming data */ | 771 | while (!tstc()) { /* while no incoming data */ |
772 | if (retry_time >= 0 && get_ticks() > endtime) | 772 | if (retry_time >= 0 && get_ticks() > endtime) |
773 | return (-2); /* timed out */ | 773 | return (-2); /* timed out */ |
774 | WATCHDOG_RESET(); | 774 | WATCHDOG_RESET(); |
775 | } | 775 | } |
776 | #endif | 776 | #endif |
777 | if (first && timeout) { | 777 | if (first && timeout) { |
778 | uint64_t etime = endtick(timeout); | 778 | uint64_t etime = endtick(timeout); |
779 | 779 | ||
780 | while (!tstc()) { /* while no incoming data */ | 780 | while (!tstc()) { /* while no incoming data */ |
781 | if (get_ticks() >= etime) | 781 | if (get_ticks() >= etime) |
782 | return -2; /* timed out */ | 782 | return -2; /* timed out */ |
783 | WATCHDOG_RESET(); | 783 | WATCHDOG_RESET(); |
784 | } | 784 | } |
785 | first = 0; | 785 | first = 0; |
786 | } | 786 | } |
787 | 787 | ||
788 | ichar = getcmd_getch(); | 788 | ichar = getcmd_getch(); |
789 | 789 | ||
790 | if ((ichar == '\n') || (ichar == '\r')) { | 790 | if ((ichar == '\n') || (ichar == '\r')) { |
791 | putc('\n'); | 791 | putc('\n'); |
792 | break; | 792 | break; |
793 | } | 793 | } |
794 | 794 | ||
795 | /* | 795 | /* |
796 | * handle standard linux xterm esc sequences for arrow key, etc. | 796 | * handle standard linux xterm esc sequences for arrow key, etc. |
797 | */ | 797 | */ |
798 | if (esc_len != 0) { | 798 | if (esc_len != 0) { |
799 | if (esc_len == 1) { | 799 | if (esc_len == 1) { |
800 | if (ichar == '[') { | 800 | if (ichar == '[') { |
801 | esc_save[esc_len] = ichar; | 801 | esc_save[esc_len] = ichar; |
802 | esc_len = 2; | 802 | esc_len = 2; |
803 | } else { | 803 | } else { |
804 | cread_add_str(esc_save, esc_len, insert, | 804 | cread_add_str(esc_save, esc_len, insert, |
805 | &num, &eol_num, buf, *len); | 805 | &num, &eol_num, buf, *len); |
806 | esc_len = 0; | 806 | esc_len = 0; |
807 | } | 807 | } |
808 | continue; | 808 | continue; |
809 | } | 809 | } |
810 | 810 | ||
811 | switch (ichar) { | 811 | switch (ichar) { |
812 | 812 | ||
813 | case 'D': /* <- key */ | 813 | case 'D': /* <- key */ |
814 | ichar = CTL_CH('b'); | 814 | ichar = CTL_CH('b'); |
815 | esc_len = 0; | 815 | esc_len = 0; |
816 | break; | 816 | break; |
817 | case 'C': /* -> key */ | 817 | case 'C': /* -> key */ |
818 | ichar = CTL_CH('f'); | 818 | ichar = CTL_CH('f'); |
819 | esc_len = 0; | 819 | esc_len = 0; |
820 | break; /* pass off to ^F handler */ | 820 | break; /* pass off to ^F handler */ |
821 | case 'H': /* Home key */ | 821 | case 'H': /* Home key */ |
822 | ichar = CTL_CH('a'); | 822 | ichar = CTL_CH('a'); |
823 | esc_len = 0; | 823 | esc_len = 0; |
824 | break; /* pass off to ^A handler */ | 824 | break; /* pass off to ^A handler */ |
825 | case 'A': /* up arrow */ | 825 | case 'A': /* up arrow */ |
826 | ichar = CTL_CH('p'); | 826 | ichar = CTL_CH('p'); |
827 | esc_len = 0; | 827 | esc_len = 0; |
828 | break; /* pass off to ^P handler */ | 828 | break; /* pass off to ^P handler */ |
829 | case 'B': /* down arrow */ | 829 | case 'B': /* down arrow */ |
830 | ichar = CTL_CH('n'); | 830 | ichar = CTL_CH('n'); |
831 | esc_len = 0; | 831 | esc_len = 0; |
832 | break; /* pass off to ^N handler */ | 832 | break; /* pass off to ^N handler */ |
833 | default: | 833 | default: |
834 | esc_save[esc_len++] = ichar; | 834 | esc_save[esc_len++] = ichar; |
835 | cread_add_str(esc_save, esc_len, insert, | 835 | cread_add_str(esc_save, esc_len, insert, |
836 | &num, &eol_num, buf, *len); | 836 | &num, &eol_num, buf, *len); |
837 | esc_len = 0; | 837 | esc_len = 0; |
838 | continue; | 838 | continue; |
839 | } | 839 | } |
840 | } | 840 | } |
841 | 841 | ||
842 | switch (ichar) { | 842 | switch (ichar) { |
843 | case 0x1b: | 843 | case 0x1b: |
844 | if (esc_len == 0) { | 844 | if (esc_len == 0) { |
845 | esc_save[esc_len] = ichar; | 845 | esc_save[esc_len] = ichar; |
846 | esc_len = 1; | 846 | esc_len = 1; |
847 | } else { | 847 | } else { |
848 | puts("impossible condition #876\n"); | 848 | puts("impossible condition #876\n"); |
849 | esc_len = 0; | 849 | esc_len = 0; |
850 | } | 850 | } |
851 | break; | 851 | break; |
852 | 852 | ||
853 | case CTL_CH('a'): | 853 | case CTL_CH('a'): |
854 | BEGINNING_OF_LINE(); | 854 | BEGINNING_OF_LINE(); |
855 | break; | 855 | break; |
856 | case CTL_CH('c'): /* ^C - break */ | 856 | case CTL_CH('c'): /* ^C - break */ |
857 | *buf = '\0'; /* discard input */ | 857 | *buf = '\0'; /* discard input */ |
858 | return (-1); | 858 | return (-1); |
859 | case CTL_CH('f'): | 859 | case CTL_CH('f'): |
860 | if (num < eol_num) { | 860 | if (num < eol_num) { |
861 | getcmd_putch(buf[num]); | 861 | getcmd_putch(buf[num]); |
862 | num++; | 862 | num++; |
863 | } | 863 | } |
864 | break; | 864 | break; |
865 | case CTL_CH('b'): | 865 | case CTL_CH('b'): |
866 | if (num) { | 866 | if (num) { |
867 | getcmd_putch(CTL_BACKSPACE); | 867 | getcmd_putch(CTL_BACKSPACE); |
868 | num--; | 868 | num--; |
869 | } | 869 | } |
870 | break; | 870 | break; |
871 | case CTL_CH('d'): | 871 | case CTL_CH('d'): |
872 | if (num < eol_num) { | 872 | if (num < eol_num) { |
873 | wlen = eol_num - num - 1; | 873 | wlen = eol_num - num - 1; |
874 | if (wlen) { | 874 | if (wlen) { |
875 | memmove(&buf[num], &buf[num+1], wlen); | 875 | memmove(&buf[num], &buf[num+1], wlen); |
876 | putnstr(buf + num, wlen); | 876 | putnstr(buf + num, wlen); |
877 | } | 877 | } |
878 | 878 | ||
879 | getcmd_putch(' '); | 879 | getcmd_putch(' '); |
880 | do { | 880 | do { |
881 | getcmd_putch(CTL_BACKSPACE); | 881 | getcmd_putch(CTL_BACKSPACE); |
882 | } while (wlen--); | 882 | } while (wlen--); |
883 | eol_num--; | 883 | eol_num--; |
884 | } | 884 | } |
885 | break; | 885 | break; |
886 | case CTL_CH('k'): | 886 | case CTL_CH('k'): |
887 | ERASE_TO_EOL(); | 887 | ERASE_TO_EOL(); |
888 | break; | 888 | break; |
889 | case CTL_CH('e'): | 889 | case CTL_CH('e'): |
890 | REFRESH_TO_EOL(); | 890 | REFRESH_TO_EOL(); |
891 | break; | 891 | break; |
892 | case CTL_CH('o'): | 892 | case CTL_CH('o'): |
893 | insert = !insert; | 893 | insert = !insert; |
894 | break; | 894 | break; |
895 | case CTL_CH('x'): | 895 | case CTL_CH('x'): |
896 | case CTL_CH('u'): | 896 | case CTL_CH('u'): |
897 | BEGINNING_OF_LINE(); | 897 | BEGINNING_OF_LINE(); |
898 | ERASE_TO_EOL(); | 898 | ERASE_TO_EOL(); |
899 | break; | 899 | break; |
900 | case DEL: | 900 | case DEL: |
901 | case DEL7: | 901 | case DEL7: |
902 | case 8: | 902 | case 8: |
903 | if (num) { | 903 | if (num) { |
904 | wlen = eol_num - num; | 904 | wlen = eol_num - num; |
905 | num--; | 905 | num--; |
906 | memmove(&buf[num], &buf[num+1], wlen); | 906 | memmove(&buf[num], &buf[num+1], wlen); |
907 | getcmd_putch(CTL_BACKSPACE); | 907 | getcmd_putch(CTL_BACKSPACE); |
908 | putnstr(buf + num, wlen); | 908 | putnstr(buf + num, wlen); |
909 | getcmd_putch(' '); | 909 | getcmd_putch(' '); |
910 | do { | 910 | do { |
911 | getcmd_putch(CTL_BACKSPACE); | 911 | getcmd_putch(CTL_BACKSPACE); |
912 | } while (wlen--); | 912 | } while (wlen--); |
913 | eol_num--; | 913 | eol_num--; |
914 | } | 914 | } |
915 | break; | 915 | break; |
916 | case CTL_CH('p'): | 916 | case CTL_CH('p'): |
917 | case CTL_CH('n'): | 917 | case CTL_CH('n'): |
918 | { | 918 | { |
919 | char * hline; | 919 | char * hline; |
920 | 920 | ||
921 | esc_len = 0; | 921 | esc_len = 0; |
922 | 922 | ||
923 | if (ichar == CTL_CH('p')) | 923 | if (ichar == CTL_CH('p')) |
924 | hline = hist_prev(); | 924 | hline = hist_prev(); |
925 | else | 925 | else |
926 | hline = hist_next(); | 926 | hline = hist_next(); |
927 | 927 | ||
928 | if (!hline) { | 928 | if (!hline) { |
929 | getcmd_cbeep(); | 929 | getcmd_cbeep(); |
930 | continue; | 930 | continue; |
931 | } | 931 | } |
932 | 932 | ||
933 | /* nuke the current line */ | 933 | /* nuke the current line */ |
934 | /* first, go home */ | 934 | /* first, go home */ |
935 | BEGINNING_OF_LINE(); | 935 | BEGINNING_OF_LINE(); |
936 | 936 | ||
937 | /* erase to end of line */ | 937 | /* erase to end of line */ |
938 | ERASE_TO_EOL(); | 938 | ERASE_TO_EOL(); |
939 | 939 | ||
940 | /* copy new line into place and display */ | 940 | /* copy new line into place and display */ |
941 | strcpy(buf, hline); | 941 | strcpy(buf, hline); |
942 | eol_num = strlen(buf); | 942 | eol_num = strlen(buf); |
943 | REFRESH_TO_EOL(); | 943 | REFRESH_TO_EOL(); |
944 | continue; | 944 | continue; |
945 | } | 945 | } |
946 | #ifdef CONFIG_AUTO_COMPLETE | 946 | #ifdef CONFIG_AUTO_COMPLETE |
947 | case '\t': { | 947 | case '\t': { |
948 | int num2, col; | 948 | int num2, col; |
949 | 949 | ||
950 | /* do not autocomplete when in the middle */ | 950 | /* do not autocomplete when in the middle */ |
951 | if (num < eol_num) { | 951 | if (num < eol_num) { |
952 | getcmd_cbeep(); | 952 | getcmd_cbeep(); |
953 | break; | 953 | break; |
954 | } | 954 | } |
955 | 955 | ||
956 | buf[num] = '\0'; | 956 | buf[num] = '\0'; |
957 | col = strlen(prompt) + eol_num; | 957 | col = strlen(prompt) + eol_num; |
958 | num2 = num; | 958 | num2 = num; |
959 | if (cmd_auto_complete(prompt, buf, &num2, &col)) { | 959 | if (cmd_auto_complete(prompt, buf, &num2, &col)) { |
960 | col = num2 - num; | 960 | col = num2 - num; |
961 | num += col; | 961 | num += col; |
962 | eol_num += col; | 962 | eol_num += col; |
963 | } | 963 | } |
964 | break; | 964 | break; |
965 | } | 965 | } |
966 | #endif | 966 | #endif |
967 | default: | 967 | default: |
968 | cread_add_char(ichar, insert, &num, &eol_num, buf, *len); | 968 | cread_add_char(ichar, insert, &num, &eol_num, buf, *len); |
969 | break; | 969 | break; |
970 | } | 970 | } |
971 | } | 971 | } |
972 | *len = eol_num; | 972 | *len = eol_num; |
973 | buf[eol_num] = '\0'; /* lose the newline */ | 973 | buf[eol_num] = '\0'; /* lose the newline */ |
974 | 974 | ||
975 | if (buf[0] && buf[0] != CREAD_HIST_CHAR) | 975 | if (buf[0] && buf[0] != CREAD_HIST_CHAR) |
976 | cread_add_to_hist(buf); | 976 | cread_add_to_hist(buf); |
977 | hist_cur = hist_add_idx; | 977 | hist_cur = hist_add_idx; |
978 | 978 | ||
979 | return 0; | 979 | return 0; |
980 | } | 980 | } |
981 | 981 | ||
982 | #endif /* CONFIG_CMDLINE_EDITING */ | 982 | #endif /* CONFIG_CMDLINE_EDITING */ |
983 | 983 | ||
984 | /****************************************************************************/ | 984 | /****************************************************************************/ |
985 | 985 | ||
986 | /* | 986 | /* |
987 | * Prompt for input and read a line. | 987 | * Prompt for input and read a line. |
988 | * If CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0, | 988 | * If CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0, |
989 | * time out when time goes past endtime (timebase time in ticks). | 989 | * time out when time goes past endtime (timebase time in ticks). |
990 | * Return: number of read characters | 990 | * Return: number of read characters |
991 | * -1 if break | 991 | * -1 if break |
992 | * -2 if timed out | 992 | * -2 if timed out |
993 | */ | 993 | */ |
994 | int readline (const char *const prompt) | 994 | int readline (const char *const prompt) |
995 | { | 995 | { |
996 | /* | 996 | /* |
997 | * If console_buffer isn't 0-length the user will be prompted to modify | 997 | * If console_buffer isn't 0-length the user will be prompted to modify |
998 | * it instead of entering it from scratch as desired. | 998 | * it instead of entering it from scratch as desired. |
999 | */ | 999 | */ |
1000 | console_buffer[0] = '\0'; | 1000 | console_buffer[0] = '\0'; |
1001 | 1001 | ||
1002 | return readline_into_buffer(prompt, console_buffer, 0); | 1002 | return readline_into_buffer(prompt, console_buffer, 0); |
1003 | } | 1003 | } |
1004 | 1004 | ||
1005 | 1005 | ||
1006 | int readline_into_buffer(const char *const prompt, char *buffer, int timeout) | 1006 | int readline_into_buffer(const char *const prompt, char *buffer, int timeout) |
1007 | { | 1007 | { |
1008 | char *p = buffer; | 1008 | char *p = buffer; |
1009 | #ifdef CONFIG_CMDLINE_EDITING | 1009 | #ifdef CONFIG_CMDLINE_EDITING |
1010 | unsigned int len = CONFIG_SYS_CBSIZE; | 1010 | unsigned int len = CONFIG_SYS_CBSIZE; |
1011 | int rc; | 1011 | int rc; |
1012 | static int initted = 0; | 1012 | static int initted = 0; |
1013 | 1013 | ||
1014 | /* | 1014 | /* |
1015 | * History uses a global array which is not | 1015 | * History uses a global array which is not |
1016 | * writable until after relocation to RAM. | 1016 | * writable until after relocation to RAM. |
1017 | * Revert to non-history version if still | 1017 | * Revert to non-history version if still |
1018 | * running from flash. | 1018 | * running from flash. |
1019 | */ | 1019 | */ |
1020 | if (gd->flags & GD_FLG_RELOC) { | 1020 | if (gd->flags & GD_FLG_RELOC) { |
1021 | if (!initted) { | 1021 | if (!initted) { |
1022 | hist_init(); | 1022 | hist_init(); |
1023 | initted = 1; | 1023 | initted = 1; |
1024 | } | 1024 | } |
1025 | 1025 | ||
1026 | if (prompt) | 1026 | if (prompt) |
1027 | puts (prompt); | 1027 | puts (prompt); |
1028 | 1028 | ||
1029 | rc = cread_line(prompt, p, &len, timeout); | 1029 | rc = cread_line(prompt, p, &len, timeout); |
1030 | return rc < 0 ? rc : len; | 1030 | return rc < 0 ? rc : len; |
1031 | 1031 | ||
1032 | } else { | 1032 | } else { |
1033 | #endif /* CONFIG_CMDLINE_EDITING */ | 1033 | #endif /* CONFIG_CMDLINE_EDITING */ |
1034 | char * p_buf = p; | 1034 | char * p_buf = p; |
1035 | int n = 0; /* buffer index */ | 1035 | int n = 0; /* buffer index */ |
1036 | int plen = 0; /* prompt length */ | 1036 | int plen = 0; /* prompt length */ |
1037 | int col; /* output column cnt */ | 1037 | int col; /* output column cnt */ |
1038 | char c; | 1038 | char c; |
1039 | 1039 | ||
1040 | /* print prompt */ | 1040 | /* print prompt */ |
1041 | if (prompt) { | 1041 | if (prompt) { |
1042 | plen = strlen (prompt); | 1042 | plen = strlen (prompt); |
1043 | puts (prompt); | 1043 | puts (prompt); |
1044 | } | 1044 | } |
1045 | col = plen; | 1045 | col = plen; |
1046 | 1046 | ||
1047 | for (;;) { | 1047 | for (;;) { |
1048 | #ifdef CONFIG_BOOT_RETRY_TIME | 1048 | #ifdef CONFIG_BOOT_RETRY_TIME |
1049 | while (!tstc()) { /* while no incoming data */ | 1049 | while (!tstc()) { /* while no incoming data */ |
1050 | if (retry_time >= 0 && get_ticks() > endtime) | 1050 | if (retry_time >= 0 && get_ticks() > endtime) |
1051 | return (-2); /* timed out */ | 1051 | return (-2); /* timed out */ |
1052 | WATCHDOG_RESET(); | 1052 | WATCHDOG_RESET(); |
1053 | } | 1053 | } |
1054 | #endif | 1054 | #endif |
1055 | WATCHDOG_RESET(); /* Trigger watchdog, if needed */ | 1055 | WATCHDOG_RESET(); /* Trigger watchdog, if needed */ |
1056 | 1056 | ||
1057 | #ifdef CONFIG_SHOW_ACTIVITY | 1057 | #ifdef CONFIG_SHOW_ACTIVITY |
1058 | while (!tstc()) { | 1058 | while (!tstc()) { |
1059 | show_activity(0); | 1059 | show_activity(0); |
1060 | WATCHDOG_RESET(); | 1060 | WATCHDOG_RESET(); |
1061 | } | 1061 | } |
1062 | #endif | 1062 | #endif |
1063 | c = getc(); | 1063 | c = getc(); |
1064 | 1064 | ||
1065 | /* | 1065 | /* |
1066 | * Special character handling | 1066 | * Special character handling |
1067 | */ | 1067 | */ |
1068 | switch (c) { | 1068 | switch (c) { |
1069 | case '\r': /* Enter */ | 1069 | case '\r': /* Enter */ |
1070 | case '\n': | 1070 | case '\n': |
1071 | *p = '\0'; | 1071 | *p = '\0'; |
1072 | puts ("\r\n"); | 1072 | puts ("\r\n"); |
1073 | return p - p_buf; | 1073 | return p - p_buf; |
1074 | 1074 | ||
1075 | case '\0': /* nul */ | 1075 | case '\0': /* nul */ |
1076 | continue; | 1076 | continue; |
1077 | 1077 | ||
1078 | case 0x03: /* ^C - break */ | 1078 | case 0x03: /* ^C - break */ |
1079 | p_buf[0] = '\0'; /* discard input */ | 1079 | p_buf[0] = '\0'; /* discard input */ |
1080 | return -1; | 1080 | return -1; |
1081 | 1081 | ||
1082 | case 0x15: /* ^U - erase line */ | 1082 | case 0x15: /* ^U - erase line */ |
1083 | while (col > plen) { | 1083 | while (col > plen) { |
1084 | puts (erase_seq); | 1084 | puts (erase_seq); |
1085 | --col; | 1085 | --col; |
1086 | } | 1086 | } |
1087 | p = p_buf; | 1087 | p = p_buf; |
1088 | n = 0; | 1088 | n = 0; |
1089 | continue; | 1089 | continue; |
1090 | 1090 | ||
1091 | case 0x17: /* ^W - erase word */ | 1091 | case 0x17: /* ^W - erase word */ |
1092 | p=delete_char(p_buf, p, &col, &n, plen); | 1092 | p=delete_char(p_buf, p, &col, &n, plen); |
1093 | while ((n > 0) && (*p != ' ')) { | 1093 | while ((n > 0) && (*p != ' ')) { |
1094 | p=delete_char(p_buf, p, &col, &n, plen); | 1094 | p=delete_char(p_buf, p, &col, &n, plen); |
1095 | } | 1095 | } |
1096 | continue; | 1096 | continue; |
1097 | 1097 | ||
1098 | case 0x08: /* ^H - backspace */ | 1098 | case 0x08: /* ^H - backspace */ |
1099 | case 0x7F: /* DEL - backspace */ | 1099 | case 0x7F: /* DEL - backspace */ |
1100 | p=delete_char(p_buf, p, &col, &n, plen); | 1100 | p=delete_char(p_buf, p, &col, &n, plen); |
1101 | continue; | 1101 | continue; |
1102 | 1102 | ||
1103 | default: | 1103 | default: |
1104 | /* | 1104 | /* |
1105 | * Must be a normal character then | 1105 | * Must be a normal character then |
1106 | */ | 1106 | */ |
1107 | if (n < CONFIG_SYS_CBSIZE-2) { | 1107 | if (n < CONFIG_SYS_CBSIZE-2) { |
1108 | if (c == '\t') { /* expand TABs */ | 1108 | if (c == '\t') { /* expand TABs */ |
1109 | #ifdef CONFIG_AUTO_COMPLETE | 1109 | #ifdef CONFIG_AUTO_COMPLETE |
1110 | /* if auto completion triggered just continue */ | 1110 | /* if auto completion triggered just continue */ |
1111 | *p = '\0'; | 1111 | *p = '\0'; |
1112 | if (cmd_auto_complete(prompt, console_buffer, &n, &col)) { | 1112 | if (cmd_auto_complete(prompt, console_buffer, &n, &col)) { |
1113 | p = p_buf + n; /* reset */ | 1113 | p = p_buf + n; /* reset */ |
1114 | continue; | 1114 | continue; |
1115 | } | 1115 | } |
1116 | #endif | 1116 | #endif |
1117 | puts (tab_seq+(col&07)); | 1117 | puts (tab_seq+(col&07)); |
1118 | col += 8 - (col&07); | 1118 | col += 8 - (col&07); |
1119 | } else { | 1119 | } else { |
1120 | char buf[2]; | 1120 | char buf[2]; |
1121 | 1121 | ||
1122 | /* | 1122 | /* |
1123 | * Echo input using puts() to force an | 1123 | * Echo input using puts() to force an |
1124 | * LCD flush if we are using an LCD | 1124 | * LCD flush if we are using an LCD |
1125 | */ | 1125 | */ |
1126 | ++col; | 1126 | ++col; |
1127 | buf[0] = c; | 1127 | buf[0] = c; |
1128 | buf[1] = '\0'; | 1128 | buf[1] = '\0'; |
1129 | puts(buf); | 1129 | puts(buf); |
1130 | } | 1130 | } |
1131 | *p++ = c; | 1131 | *p++ = c; |
1132 | ++n; | 1132 | ++n; |
1133 | } else { /* Buffer full */ | 1133 | } else { /* Buffer full */ |
1134 | putc ('\a'); | 1134 | putc ('\a'); |
1135 | } | 1135 | } |
1136 | } | 1136 | } |
1137 | } | 1137 | } |
1138 | #ifdef CONFIG_CMDLINE_EDITING | 1138 | #ifdef CONFIG_CMDLINE_EDITING |
1139 | } | 1139 | } |
1140 | #endif | 1140 | #endif |
1141 | } | 1141 | } |
1142 | 1142 | ||
1143 | /****************************************************************************/ | 1143 | /****************************************************************************/ |
1144 | 1144 | ||
1145 | static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen) | 1145 | static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen) |
1146 | { | 1146 | { |
1147 | char *s; | 1147 | char *s; |
1148 | 1148 | ||
1149 | if (*np == 0) { | 1149 | if (*np == 0) { |
1150 | return (p); | 1150 | return (p); |
1151 | } | 1151 | } |
1152 | 1152 | ||
1153 | if (*(--p) == '\t') { /* will retype the whole line */ | 1153 | if (*(--p) == '\t') { /* will retype the whole line */ |
1154 | while (*colp > plen) { | 1154 | while (*colp > plen) { |
1155 | puts (erase_seq); | 1155 | puts (erase_seq); |
1156 | (*colp)--; | 1156 | (*colp)--; |
1157 | } | 1157 | } |
1158 | for (s=buffer; s<p; ++s) { | 1158 | for (s=buffer; s<p; ++s) { |
1159 | if (*s == '\t') { | 1159 | if (*s == '\t') { |
1160 | puts (tab_seq+((*colp) & 07)); | 1160 | puts (tab_seq+((*colp) & 07)); |
1161 | *colp += 8 - ((*colp) & 07); | 1161 | *colp += 8 - ((*colp) & 07); |
1162 | } else { | 1162 | } else { |
1163 | ++(*colp); | 1163 | ++(*colp); |
1164 | putc (*s); | 1164 | putc (*s); |
1165 | } | 1165 | } |
1166 | } | 1166 | } |
1167 | } else { | 1167 | } else { |
1168 | puts (erase_seq); | 1168 | puts (erase_seq); |
1169 | (*colp)--; | 1169 | (*colp)--; |
1170 | } | 1170 | } |
1171 | (*np)--; | 1171 | (*np)--; |
1172 | return (p); | 1172 | return (p); |
1173 | } | 1173 | } |
1174 | 1174 | ||
1175 | /****************************************************************************/ | 1175 | /****************************************************************************/ |
1176 | 1176 | ||
1177 | int parse_line (char *line, char *argv[]) | 1177 | int parse_line (char *line, char *argv[]) |
1178 | { | 1178 | { |
1179 | int nargs = 0; | 1179 | int nargs = 0; |
1180 | 1180 | ||
1181 | debug_parser("parse_line: \"%s\"\n", line); | 1181 | debug_parser("parse_line: \"%s\"\n", line); |
1182 | while (nargs < CONFIG_SYS_MAXARGS) { | 1182 | while (nargs < CONFIG_SYS_MAXARGS) { |
1183 | 1183 | ||
1184 | /* skip any white space */ | 1184 | /* skip any white space */ |
1185 | while (isblank(*line)) | 1185 | while (isblank(*line)) |
1186 | ++line; | 1186 | ++line; |
1187 | 1187 | ||
1188 | if (*line == '\0') { /* end of line, no more args */ | 1188 | if (*line == '\0') { /* end of line, no more args */ |
1189 | argv[nargs] = NULL; | 1189 | argv[nargs] = NULL; |
1190 | debug_parser("parse_line: nargs=%d\n", nargs); | 1190 | debug_parser("parse_line: nargs=%d\n", nargs); |
1191 | return nargs; | 1191 | return nargs; |
1192 | } | 1192 | } |
1193 | 1193 | ||
1194 | argv[nargs++] = line; /* begin of argument string */ | 1194 | argv[nargs++] = line; /* begin of argument string */ |
1195 | 1195 | ||
1196 | /* find end of string */ | 1196 | /* find end of string */ |
1197 | while (*line && !isblank(*line)) | 1197 | while (*line && !isblank(*line)) |
1198 | ++line; | 1198 | ++line; |
1199 | 1199 | ||
1200 | if (*line == '\0') { /* end of line, no more args */ | 1200 | if (*line == '\0') { /* end of line, no more args */ |
1201 | argv[nargs] = NULL; | 1201 | argv[nargs] = NULL; |
1202 | debug_parser("parse_line: nargs=%d\n", nargs); | 1202 | debug_parser("parse_line: nargs=%d\n", nargs); |
1203 | return nargs; | 1203 | return nargs; |
1204 | } | 1204 | } |
1205 | 1205 | ||
1206 | *line++ = '\0'; /* terminate current arg */ | 1206 | *line++ = '\0'; /* terminate current arg */ |
1207 | } | 1207 | } |
1208 | 1208 | ||
1209 | printf ("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS); | 1209 | printf ("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS); |
1210 | 1210 | ||
1211 | debug_parser("parse_line: nargs=%d\n", nargs); | 1211 | debug_parser("parse_line: nargs=%d\n", nargs); |
1212 | return (nargs); | 1212 | return (nargs); |
1213 | } | 1213 | } |
1214 | 1214 | ||
1215 | /****************************************************************************/ | 1215 | /****************************************************************************/ |
1216 | 1216 | ||
1217 | #ifndef CONFIG_SYS_HUSH_PARSER | 1217 | #ifndef CONFIG_SYS_HUSH_PARSER |
1218 | static void process_macros (const char *input, char *output) | 1218 | static void process_macros (const char *input, char *output) |
1219 | { | 1219 | { |
1220 | char c, prev; | 1220 | char c, prev; |
1221 | const char *varname_start = NULL; | 1221 | const char *varname_start = NULL; |
1222 | int inputcnt = strlen (input); | 1222 | int inputcnt = strlen (input); |
1223 | int outputcnt = CONFIG_SYS_CBSIZE; | 1223 | int outputcnt = CONFIG_SYS_CBSIZE; |
1224 | int state = 0; /* 0 = waiting for '$' */ | 1224 | int state = 0; /* 0 = waiting for '$' */ |
1225 | 1225 | ||
1226 | /* 1 = waiting for '(' or '{' */ | 1226 | /* 1 = waiting for '(' or '{' */ |
1227 | /* 2 = waiting for ')' or '}' */ | 1227 | /* 2 = waiting for ')' or '}' */ |
1228 | /* 3 = waiting for ''' */ | 1228 | /* 3 = waiting for ''' */ |
1229 | char *output_start = output; | 1229 | char *output_start = output; |
1230 | 1230 | ||
1231 | debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input), | 1231 | debug_parser("[PROCESS_MACROS] INPUT len %zd: \"%s\"\n", strlen(input), |
1232 | input); | 1232 | input); |
1233 | 1233 | ||
1234 | prev = '\0'; /* previous character */ | 1234 | prev = '\0'; /* previous character */ |
1235 | 1235 | ||
1236 | while (inputcnt && outputcnt) { | 1236 | while (inputcnt && outputcnt) { |
1237 | c = *input++; | 1237 | c = *input++; |
1238 | inputcnt--; | 1238 | inputcnt--; |
1239 | 1239 | ||
1240 | if (state != 3) { | 1240 | if (state != 3) { |
1241 | /* remove one level of escape characters */ | 1241 | /* remove one level of escape characters */ |
1242 | if ((c == '\\') && (prev != '\\')) { | 1242 | if ((c == '\\') && (prev != '\\')) { |
1243 | if (inputcnt-- == 0) | 1243 | if (inputcnt-- == 0) |
1244 | break; | 1244 | break; |
1245 | prev = c; | 1245 | prev = c; |
1246 | c = *input++; | 1246 | c = *input++; |
1247 | } | 1247 | } |
1248 | } | 1248 | } |
1249 | 1249 | ||
1250 | switch (state) { | 1250 | switch (state) { |
1251 | case 0: /* Waiting for (unescaped) $ */ | 1251 | case 0: /* Waiting for (unescaped) $ */ |
1252 | if ((c == '\'') && (prev != '\\')) { | 1252 | if ((c == '\'') && (prev != '\\')) { |
1253 | state = 3; | 1253 | state = 3; |
1254 | break; | 1254 | break; |
1255 | } | 1255 | } |
1256 | if ((c == '$') && (prev != '\\')) { | 1256 | if ((c == '$') && (prev != '\\')) { |
1257 | state++; | 1257 | state++; |
1258 | } else { | 1258 | } else { |
1259 | *(output++) = c; | 1259 | *(output++) = c; |
1260 | outputcnt--; | 1260 | outputcnt--; |
1261 | } | 1261 | } |
1262 | break; | 1262 | break; |
1263 | case 1: /* Waiting for ( */ | 1263 | case 1: /* Waiting for ( */ |
1264 | if (c == '(' || c == '{') { | 1264 | if (c == '(' || c == '{') { |
1265 | state++; | 1265 | state++; |
1266 | varname_start = input; | 1266 | varname_start = input; |
1267 | } else { | 1267 | } else { |
1268 | state = 0; | 1268 | state = 0; |
1269 | *(output++) = '$'; | 1269 | *(output++) = '$'; |
1270 | outputcnt--; | 1270 | outputcnt--; |
1271 | 1271 | ||
1272 | if (outputcnt) { | 1272 | if (outputcnt) { |
1273 | *(output++) = c; | 1273 | *(output++) = c; |
1274 | outputcnt--; | 1274 | outputcnt--; |
1275 | } | 1275 | } |
1276 | } | 1276 | } |
1277 | break; | 1277 | break; |
1278 | case 2: /* Waiting for ) */ | 1278 | case 2: /* Waiting for ) */ |
1279 | if (c == ')' || c == '}') { | 1279 | if (c == ')' || c == '}') { |
1280 | int i; | 1280 | int i; |
1281 | char envname[CONFIG_SYS_CBSIZE], *envval; | 1281 | char envname[CONFIG_SYS_CBSIZE], *envval; |
1282 | int envcnt = input - varname_start - 1; /* Varname # of chars */ | 1282 | int envcnt = input - varname_start - 1; /* Varname # of chars */ |
1283 | 1283 | ||
1284 | /* Get the varname */ | 1284 | /* Get the varname */ |
1285 | for (i = 0; i < envcnt; i++) { | 1285 | for (i = 0; i < envcnt; i++) { |
1286 | envname[i] = varname_start[i]; | 1286 | envname[i] = varname_start[i]; |
1287 | } | 1287 | } |
1288 | envname[i] = 0; | 1288 | envname[i] = 0; |
1289 | 1289 | ||
1290 | /* Get its value */ | 1290 | /* Get its value */ |
1291 | envval = getenv (envname); | 1291 | envval = getenv (envname); |
1292 | 1292 | ||
1293 | /* Copy into the line if it exists */ | 1293 | /* Copy into the line if it exists */ |
1294 | if (envval != NULL) | 1294 | if (envval != NULL) |
1295 | while ((*envval) && outputcnt) { | 1295 | while ((*envval) && outputcnt) { |
1296 | *(output++) = *(envval++); | 1296 | *(output++) = *(envval++); |
1297 | outputcnt--; | 1297 | outputcnt--; |
1298 | } | 1298 | } |
1299 | /* Look for another '$' */ | 1299 | /* Look for another '$' */ |
1300 | state = 0; | 1300 | state = 0; |
1301 | } | 1301 | } |
1302 | break; | 1302 | break; |
1303 | case 3: /* Waiting for ' */ | 1303 | case 3: /* Waiting for ' */ |
1304 | if ((c == '\'') && (prev != '\\')) { | 1304 | if ((c == '\'') && (prev != '\\')) { |
1305 | state = 0; | 1305 | state = 0; |
1306 | } else { | 1306 | } else { |
1307 | *(output++) = c; | 1307 | *(output++) = c; |
1308 | outputcnt--; | 1308 | outputcnt--; |
1309 | } | 1309 | } |
1310 | break; | 1310 | break; |
1311 | } | 1311 | } |
1312 | prev = c; | 1312 | prev = c; |
1313 | } | 1313 | } |
1314 | 1314 | ||
1315 | if (outputcnt) | 1315 | if (outputcnt) |
1316 | *output = 0; | 1316 | *output = 0; |
1317 | else | 1317 | else |
1318 | *(output - 1) = 0; | 1318 | *(output - 1) = 0; |
1319 | 1319 | ||
1320 | debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n", | 1320 | debug_parser("[PROCESS_MACROS] OUTPUT len %zd: \"%s\"\n", |
1321 | strlen(output_start), output_start); | 1321 | strlen(output_start), output_start); |
1322 | } | 1322 | } |
1323 | 1323 | ||
1324 | /**************************************************************************** | 1324 | /**************************************************************************** |
1325 | * returns: | 1325 | * returns: |
1326 | * 1 - command executed, repeatable | 1326 | * 1 - command executed, repeatable |
1327 | * 0 - command executed but not repeatable, interrupted commands are | 1327 | * 0 - command executed but not repeatable, interrupted commands are |
1328 | * always considered not repeatable | 1328 | * always considered not repeatable |
1329 | * -1 - not executed (unrecognized, bootd recursion or too many args) | 1329 | * -1 - not executed (unrecognized, bootd recursion or too many args) |
1330 | * (If cmd is NULL or "" or longer than CONFIG_SYS_CBSIZE-1 it is | 1330 | * (If cmd is NULL or "" or longer than CONFIG_SYS_CBSIZE-1 it is |
1331 | * considered unrecognized) | 1331 | * considered unrecognized) |
1332 | * | 1332 | * |
1333 | * WARNING: | 1333 | * WARNING: |
1334 | * | 1334 | * |
1335 | * We must create a temporary copy of the command since the command we get | 1335 | * We must create a temporary copy of the command since the command we get |
1336 | * may be the result from getenv(), which returns a pointer directly to | 1336 | * may be the result from getenv(), which returns a pointer directly to |
1337 | * the environment data, which may change magicly when the command we run | 1337 | * the environment data, which may change magicly when the command we run |
1338 | * creates or modifies environment variables (like "bootp" does). | 1338 | * creates or modifies environment variables (like "bootp" does). |
1339 | */ | 1339 | */ |
1340 | static int builtin_run_command(const char *cmd, int flag) | 1340 | static int builtin_run_command(const char *cmd, int flag) |
1341 | { | 1341 | { |
1342 | char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd */ | 1342 | char cmdbuf[CONFIG_SYS_CBSIZE]; /* working copy of cmd */ |
1343 | char *token; /* start of token in cmdbuf */ | 1343 | char *token; /* start of token in cmdbuf */ |
1344 | char *sep; /* end of token (separator) in cmdbuf */ | 1344 | char *sep; /* end of token (separator) in cmdbuf */ |
1345 | char finaltoken[CONFIG_SYS_CBSIZE]; | 1345 | char finaltoken[CONFIG_SYS_CBSIZE]; |
1346 | char *str = cmdbuf; | 1346 | char *str = cmdbuf; |
1347 | char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */ | 1347 | char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */ |
1348 | int argc, inquotes; | 1348 | int argc, inquotes; |
1349 | int repeatable = 1; | 1349 | int repeatable = 1; |
1350 | int rc = 0; | 1350 | int rc = 0; |
1351 | 1351 | ||
1352 | debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd); | 1352 | debug_parser("[RUN_COMMAND] cmd[%p]=\"", cmd); |
1353 | if (DEBUG_PARSER) { | 1353 | if (DEBUG_PARSER) { |
1354 | /* use puts - string may be loooong */ | 1354 | /* use puts - string may be loooong */ |
1355 | puts(cmd ? cmd : "NULL"); | 1355 | puts(cmd ? cmd : "NULL"); |
1356 | puts("\"\n"); | 1356 | puts("\"\n"); |
1357 | } | 1357 | } |
1358 | clear_ctrlc(); /* forget any previous Control C */ | 1358 | clear_ctrlc(); /* forget any previous Control C */ |
1359 | 1359 | ||
1360 | if (!cmd || !*cmd) { | 1360 | if (!cmd || !*cmd) { |
1361 | return -1; /* empty command */ | 1361 | return -1; /* empty command */ |
1362 | } | 1362 | } |
1363 | 1363 | ||
1364 | if (strlen(cmd) >= CONFIG_SYS_CBSIZE) { | 1364 | if (strlen(cmd) >= CONFIG_SYS_CBSIZE) { |
1365 | puts ("## Command too long!\n"); | 1365 | puts ("## Command too long!\n"); |
1366 | return -1; | 1366 | return -1; |
1367 | } | 1367 | } |
1368 | 1368 | ||
1369 | strcpy (cmdbuf, cmd); | 1369 | strcpy (cmdbuf, cmd); |
1370 | 1370 | ||
1371 | /* Process separators and check for invalid | 1371 | /* Process separators and check for invalid |
1372 | * repeatable commands | 1372 | * repeatable commands |
1373 | */ | 1373 | */ |
1374 | 1374 | ||
1375 | debug_parser("[PROCESS_SEPARATORS] %s\n", cmd); | 1375 | debug_parser("[PROCESS_SEPARATORS] %s\n", cmd); |
1376 | while (*str) { | 1376 | while (*str) { |
1377 | 1377 | ||
1378 | /* | 1378 | /* |
1379 | * Find separator, or string end | 1379 | * Find separator, or string end |
1380 | * Allow simple escape of ';' by writing "\;" | 1380 | * Allow simple escape of ';' by writing "\;" |
1381 | */ | 1381 | */ |
1382 | for (inquotes = 0, sep = str; *sep; sep++) { | 1382 | for (inquotes = 0, sep = str; *sep; sep++) { |
1383 | if ((*sep=='\'') && | 1383 | if ((*sep=='\'') && |
1384 | (*(sep-1) != '\\')) | 1384 | (*(sep-1) != '\\')) |
1385 | inquotes=!inquotes; | 1385 | inquotes=!inquotes; |
1386 | 1386 | ||
1387 | if (!inquotes && | 1387 | if (!inquotes && |
1388 | (*sep == ';') && /* separator */ | 1388 | (*sep == ';') && /* separator */ |
1389 | ( sep != str) && /* past string start */ | 1389 | ( sep != str) && /* past string start */ |
1390 | (*(sep-1) != '\\')) /* and NOT escaped */ | 1390 | (*(sep-1) != '\\')) /* and NOT escaped */ |
1391 | break; | 1391 | break; |
1392 | } | 1392 | } |
1393 | 1393 | ||
1394 | /* | 1394 | /* |
1395 | * Limit the token to data between separators | 1395 | * Limit the token to data between separators |
1396 | */ | 1396 | */ |
1397 | token = str; | 1397 | token = str; |
1398 | if (*sep) { | 1398 | if (*sep) { |
1399 | str = sep + 1; /* start of command for next pass */ | 1399 | str = sep + 1; /* start of command for next pass */ |
1400 | *sep = '\0'; | 1400 | *sep = '\0'; |
1401 | } | 1401 | } |
1402 | else | 1402 | else |
1403 | str = sep; /* no more commands for next pass */ | 1403 | str = sep; /* no more commands for next pass */ |
1404 | debug_parser("token: \"%s\"\n", token); | 1404 | debug_parser("token: \"%s\"\n", token); |
1405 | 1405 | ||
1406 | /* find macros in this token and replace them */ | 1406 | /* find macros in this token and replace them */ |
1407 | process_macros (token, finaltoken); | 1407 | process_macros (token, finaltoken); |
1408 | 1408 | ||
1409 | /* Extract arguments */ | 1409 | /* Extract arguments */ |
1410 | if ((argc = parse_line (finaltoken, argv)) == 0) { | 1410 | if ((argc = parse_line (finaltoken, argv)) == 0) { |
1411 | rc = -1; /* no command at all */ | 1411 | rc = -1; /* no command at all */ |
1412 | continue; | 1412 | continue; |
1413 | } | 1413 | } |
1414 | 1414 | ||
1415 | if (cmd_process(flag, argc, argv, &repeatable, NULL)) | 1415 | if (cmd_process(flag, argc, argv, &repeatable, NULL)) |
1416 | rc = -1; | 1416 | rc = -1; |
1417 | 1417 | ||
1418 | /* Did the user stop this? */ | 1418 | /* Did the user stop this? */ |
1419 | if (had_ctrlc ()) | 1419 | if (had_ctrlc ()) |
1420 | return -1; /* if stopped then not repeatable */ | 1420 | return -1; /* if stopped then not repeatable */ |
1421 | } | 1421 | } |
1422 | 1422 | ||
1423 | return rc ? rc : repeatable; | 1423 | return rc ? rc : repeatable; |
1424 | } | 1424 | } |
1425 | #endif | 1425 | #endif |
1426 | 1426 | ||
1427 | /* | 1427 | /* |
1428 | * Run a command using the selected parser. | 1428 | * Run a command using the selected parser. |
1429 | * | 1429 | * |
1430 | * @param cmd Command to run | 1430 | * @param cmd Command to run |
1431 | * @param flag Execution flags (CMD_FLAG_...) | 1431 | * @param flag Execution flags (CMD_FLAG_...) |
1432 | * @return 0 on success, or != 0 on error. | 1432 | * @return 0 on success, or != 0 on error. |
1433 | */ | 1433 | */ |
1434 | int run_command(const char *cmd, int flag) | 1434 | int run_command(const char *cmd, int flag) |
1435 | { | 1435 | { |
1436 | #ifndef CONFIG_SYS_HUSH_PARSER | 1436 | #ifndef CONFIG_SYS_HUSH_PARSER |
1437 | /* | 1437 | /* |
1438 | * builtin_run_command can return 0 or 1 for success, so clean up | 1438 | * builtin_run_command can return 0 or 1 for success, so clean up |
1439 | * its result. | 1439 | * its result. |
1440 | */ | 1440 | */ |
1441 | if (builtin_run_command(cmd, flag) == -1) | 1441 | if (builtin_run_command(cmd, flag) == -1) |
1442 | return 1; | 1442 | return 1; |
1443 | 1443 | ||
1444 | return 0; | 1444 | return 0; |
1445 | #else | 1445 | #else |
1446 | return parse_string_outer(cmd, | 1446 | return parse_string_outer(cmd, |
1447 | FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP); | 1447 | FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP); |
1448 | #endif | 1448 | #endif |
1449 | } | 1449 | } |
1450 | 1450 | ||
1451 | #ifndef CONFIG_SYS_HUSH_PARSER | 1451 | #ifndef CONFIG_SYS_HUSH_PARSER |
1452 | /** | 1452 | /** |
1453 | * Execute a list of command separated by ; or \n using the built-in parser. | 1453 | * Execute a list of command separated by ; or \n using the built-in parser. |
1454 | * | 1454 | * |
1455 | * This function cannot take a const char * for the command, since if it | 1455 | * This function cannot take a const char * for the command, since if it |
1456 | * finds newlines in the string, it replaces them with \0. | 1456 | * finds newlines in the string, it replaces them with \0. |
1457 | * | 1457 | * |
1458 | * @param cmd String containing list of commands | 1458 | * @param cmd String containing list of commands |
1459 | * @param flag Execution flags (CMD_FLAG_...) | 1459 | * @param flag Execution flags (CMD_FLAG_...) |
1460 | * @return 0 on success, or != 0 on error. | 1460 | * @return 0 on success, or != 0 on error. |
1461 | */ | 1461 | */ |
1462 | static int builtin_run_command_list(char *cmd, int flag) | 1462 | static int builtin_run_command_list(char *cmd, int flag) |
1463 | { | 1463 | { |
1464 | char *line, *next; | 1464 | char *line, *next; |
1465 | int rcode = 0; | 1465 | int rcode = 0; |
1466 | 1466 | ||
1467 | /* | 1467 | /* |
1468 | * Break into individual lines, and execute each line; terminate on | 1468 | * Break into individual lines, and execute each line; terminate on |
1469 | * error. | 1469 | * error. |
1470 | */ | 1470 | */ |
1471 | line = next = cmd; | 1471 | line = next = cmd; |
1472 | while (*next) { | 1472 | while (*next) { |
1473 | if (*next == '\n') { | 1473 | if (*next == '\n') { |
1474 | *next = '\0'; | 1474 | *next = '\0'; |
1475 | /* run only non-empty commands */ | 1475 | /* run only non-empty commands */ |
1476 | if (*line) { | 1476 | if (*line) { |
1477 | debug("** exec: \"%s\"\n", line); | 1477 | debug("** exec: \"%s\"\n", line); |
1478 | if (builtin_run_command(line, 0) < 0) { | 1478 | if (builtin_run_command(line, 0) < 0) { |
1479 | rcode = 1; | 1479 | rcode = 1; |
1480 | break; | 1480 | break; |
1481 | } | 1481 | } |
1482 | } | 1482 | } |
1483 | line = next + 1; | 1483 | line = next + 1; |
1484 | } | 1484 | } |
1485 | ++next; | 1485 | ++next; |
1486 | } | 1486 | } |
1487 | if (rcode == 0 && *line) | 1487 | if (rcode == 0 && *line) |
1488 | rcode = (builtin_run_command(line, 0) >= 0); | 1488 | rcode = (builtin_run_command(line, 0) >= 0); |
1489 | 1489 | ||
1490 | return rcode; | 1490 | return rcode; |
1491 | } | 1491 | } |
1492 | #endif | 1492 | #endif |
1493 | 1493 | ||
1494 | int run_command_list(const char *cmd, int len, int flag) | 1494 | int run_command_list(const char *cmd, int len, int flag) |
1495 | { | 1495 | { |
1496 | int need_buff = 1; | 1496 | int need_buff = 1; |
1497 | char *buff = (char *)cmd; /* cast away const */ | 1497 | char *buff = (char *)cmd; /* cast away const */ |
1498 | int rcode = 0; | 1498 | int rcode = 0; |
1499 | 1499 | ||
1500 | if (len == -1) { | 1500 | if (len == -1) { |
1501 | len = strlen(cmd); | 1501 | len = strlen(cmd); |
1502 | #ifdef CONFIG_SYS_HUSH_PARSER | 1502 | #ifdef CONFIG_SYS_HUSH_PARSER |
1503 | /* hush will never change our string */ | 1503 | /* hush will never change our string */ |
1504 | need_buff = 0; | 1504 | need_buff = 0; |
1505 | #else | 1505 | #else |
1506 | /* the built-in parser will change our string if it sees \n */ | 1506 | /* the built-in parser will change our string if it sees \n */ |
1507 | need_buff = strchr(cmd, '\n') != NULL; | 1507 | need_buff = strchr(cmd, '\n') != NULL; |
1508 | #endif | 1508 | #endif |
1509 | } | 1509 | } |
1510 | if (need_buff) { | 1510 | if (need_buff) { |
1511 | buff = malloc(len + 1); | 1511 | buff = malloc(len + 1); |
1512 | if (!buff) | 1512 | if (!buff) |
1513 | return 1; | 1513 | return 1; |
1514 | memcpy(buff, cmd, len); | 1514 | memcpy(buff, cmd, len); |
1515 | buff[len] = '\0'; | 1515 | buff[len] = '\0'; |
1516 | } | 1516 | } |
1517 | #ifdef CONFIG_SYS_HUSH_PARSER | 1517 | #ifdef CONFIG_SYS_HUSH_PARSER |
1518 | rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON); | 1518 | rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON); |
1519 | #else | 1519 | #else |
1520 | /* | 1520 | /* |
1521 | * This function will overwrite any \n it sees with a \0, which | 1521 | * This function will overwrite any \n it sees with a \0, which |
1522 | * is why it can't work with a const char *. Here we are making | 1522 | * is why it can't work with a const char *. Here we are making |
1523 | * using of internal knowledge of this function, to avoid always | 1523 | * using of internal knowledge of this function, to avoid always |
1524 | * doing a malloc() which is actually required only in a case that | 1524 | * doing a malloc() which is actually required only in a case that |
1525 | * is pretty rare. | 1525 | * is pretty rare. |
1526 | */ | 1526 | */ |
1527 | rcode = builtin_run_command_list(buff, flag); | 1527 | rcode = builtin_run_command_list(buff, flag); |
1528 | if (need_buff) | 1528 | if (need_buff) |
1529 | free(buff); | 1529 | free(buff); |
1530 | #endif | 1530 | #endif |
1531 | 1531 | ||
1532 | return rcode; | 1532 | return rcode; |
1533 | } | 1533 | } |
1534 | 1534 | ||
1535 | /****************************************************************************/ | 1535 | /****************************************************************************/ |
1536 | 1536 | ||
1537 | #if defined(CONFIG_CMD_RUN) | 1537 | #if defined(CONFIG_CMD_RUN) |
1538 | int do_run (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) | 1538 | int do_run (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) |
1539 | { | 1539 | { |
1540 | int i; | 1540 | int i; |
1541 | 1541 | ||
1542 | if (argc < 2) | 1542 | if (argc < 2) |
1543 | return CMD_RET_USAGE; | 1543 | return CMD_RET_USAGE; |
1544 | 1544 | ||
1545 | for (i=1; i<argc; ++i) { | 1545 | for (i=1; i<argc; ++i) { |
1546 | char *arg; | 1546 | char *arg; |
1547 | 1547 | ||
1548 | if ((arg = getenv (argv[i])) == NULL) { | 1548 | if ((arg = getenv (argv[i])) == NULL) { |
1549 | printf ("## Error: \"%s\" not defined\n", argv[i]); | 1549 | printf ("## Error: \"%s\" not defined\n", argv[i]); |
1550 | return 1; | 1550 | return 1; |
1551 | } | 1551 | } |
1552 | 1552 | ||
1553 | if (run_command(arg, flag) != 0) | 1553 | if (run_command_list(arg, -1, flag) != 0) |
1554 | return 1; | 1554 | return 1; |
1555 | } | 1555 | } |
1556 | return 0; | 1556 | return 0; |
1557 | } | 1557 | } |
1558 | #endif | 1558 | #endif |
1559 | 1559 |