xref: /nrf52832-nimble/rt-thread/components/dfs/filesystems/uffs/src/emu/cmdline.c (revision 104654410c56c573564690304ae786df310c91fc)
1 /*
2 This file is part of UFFS, the Ultra-low-cost Flash File System.
3 
4 Copyright (C) 2005-2009 Ricky Zheng <[email protected]>
5 
6 UFFS is free software; you can redistribute it and/or modify it under
7 the GNU Library General Public License as published by the Free Software
8 Foundation; either version 2 of the License, or (at your option) any
9 later version.
10 
11 UFFS is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 or GNU Library General Public License, as applicable, for more details.
15 
16 You should have received a copy of the GNU General Public License
17 and GNU Library General Public License along with UFFS; if not, write
18 to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA  02110-1301, USA.
20 
21 As a special exception, if other files instantiate templates or use
22 macros or inline functions from this file, or you compile this file
23 and link it with other works to produce a work based on this file,
24 this file does not by itself cause the resulting work to be covered
25 by the GNU General Public License. However the source code for this
26 file must still be made available in accordance with section (3) of
27 the GNU General Public License v2.
28 
29 This exception does not invalidate any other reasons why a work based
30 on this file might be covered by the GNU General Public License.
31 */
32 
33 /**
34 * \file cmdline.c
35 * \brief command line test interface
36 * \author Ricky Zheng, created in 22th Feb, 2007
37 */
38 
39 #include <string.h>
40 #include <stdio.h>
41 //#include <conio.h>
42 #include "uffs_config.h"
43 #include "cmdline.h"
44 #include "uffs/uffs_fs.h"
45 
46 #define PROMPT "UFFS>"
47 
48 #define PFX "cli : "
49 #define MSGLN(msg,...) uffs_Perror(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
50 #define MSG(msg,...) uffs_PerrorRaw(UFFS_MSG_NORMAL, msg, ## __VA_ARGS__)
51 
52 #define MAX_CLI_ARGS_BUF_LEN	120
53 #define MAX_CLI_ARGS_NUM		20
54 #define MAX_CLI_ENV_NUM			11		// '?', '0' - '9'
55 
56 
57 struct cli_arg {
58 	int argc;
59 	char *argv[MAX_CLI_ARGS_NUM];
60 	char _buf[MAX_CLI_ARGS_BUF_LEN];
61 };
62 
63 static BOOL m_exit = FALSE;
64 static BOOL m_abort = FALSE;
65 static struct cli_commandset *m_cmdset_head = NULL;
66 
67 // Note: last command return code stored in env 0.
68 static int m_cli_envs[MAX_CLI_ENV_NUM] = {0};	// cli environment variables
69 
70 static const struct cli_command * cli_find(const char *cmd);
71 static int cmd_help(int argc, char *argv[]);
72 
73 
74 #define FOR_EACH_CLI_CMD(set, cmd) \
75 	for (set = m_cmdset_head; set && set->cmds; set = set->next) \
76 		for (cmd = set->cmds; cmd && cmd->handler; cmd++)
77 
78 
79 /*** filter out leading and tailing spaces, discard comments
80  * return pointer to start of new command line
81  */
cli_process_line(char * p)82 static char * cli_process_line(char *p)
83 {
84 	char *s;
85 	char *x;
86 
87 	if (!p)
88 		return NULL;
89 
90 	// skip leading spaces
91 	while (p && (*p == ' ' || *p == '\t'))
92 		p++;
93 
94 	for (s = x = p; *p; x++, p++) {
95 		switch(*p) {
96 		case '\\':
97 			p++;
98 			if (*p) {
99 				switch(*p) {
100 				case 'n':
101 					*x = '\n';
102 					break;
103 				case 'r':
104 					*x = '\r';
105 					break;
106 				case 't':
107 					*x = '\t';
108 					break;
109 				case 'b':
110 					*x = '\b';
111 					break;
112 				default:
113 					if (*p >= '0' && *p <= '9')
114 						*x = *p - '0';
115 					else
116 						*x = *p;
117 					break;
118 				}
119 			}
120 			break;
121 		default:
122 			if (*p == '\r' || *p == '\n' || *p == '#') *p = '\0';
123 			*x = *p;
124 			break;
125 		}
126 
127 		if (*p == 0)
128 			break;
129 	}
130 
131 	// trim tailing spaces
132 	p--;
133 	while (p > s && (*p == ' ' || *p == '\t'))
134 		*p-- = '\0';
135 
136 	return s;
137 }
138 
cli_env_to_idx(char env)139 static int cli_env_to_idx(char env)
140 {
141 	int idx = -1;
142 
143 	if (env >= '0' && env <= '9') {
144 		idx = env - '0' + 1;
145 	}
146 	else if (env == '?') {
147 		idx = 0;
148 	}
149 
150 	return idx;
151 }
152 
cli_env_set(char env,int val)153 int cli_env_set(char env, int val)
154 {
155 	int idx = cli_env_to_idx(env);
156 
157 	if (idx >= 0) {
158 		m_cli_envs[idx] = val;
159 		return 0;
160 	}
161 	else
162 		return -1;
163 }
164 
cli_env_get(char env)165 int cli_env_get(char env)
166 {
167 	int idx = cli_env_to_idx(env);
168 
169 	return idx >= 0 ? m_cli_envs[idx] : 0;
170 }
171 
172 /** exec command <n> times:
173  *		exec <n> <cmd> [...]
174  */
cmd_exec(int argc,char * argv[])175 static int cmd_exec(int argc, char *argv[])
176 {
177 	int n = 0;
178 	const struct cli_command *cmd;
179 
180 	CHK_ARGC(3, 0);
181 
182 	if (sscanf(argv[1], "%d", &n) != 1)
183 		return CLI_INVALID_ARG;
184 	if (n <= 0)
185 		return CLI_INVALID_ARG;
186 
187 	cmd = cli_find(argv[2]);
188 	if (cmd == NULL) {
189 		MSG("Unknown command '%s'\n", argv[2]);
190 		return -1;
191 	}
192 	else {
193 		argv += 2;
194 		while (n-- >= 0) {
195 			if (cmd->handler(argc - 2, argv) != 0)
196 				return -1;
197 		}
198 	}
199 
200 	return 0;
201 }
202 
203 /**
204  * test expression
205  *	test <a> <op> <b>
206  * for example:
207  *	test 1 > 0    ==> return 0
208  *	test 1 <= 0   ==> return -1
209  */
cmd_test(int argc,char * argv[])210 static int cmd_test(int argc, char *argv[])
211 {
212 	int a, b;
213 	char *op;
214 	BOOL tst = FALSE;
215 
216 	CHK_ARGC(4, 4);
217 
218 	if (sscanf(argv[1], "%d", &a) != 1 ||
219 		sscanf(argv[3], "%d", &b) != 1)
220 	{
221 		return CLI_INVALID_ARG;
222 	}
223 
224 	op = argv[2];
225 	if (!strcmp(op, ">")) {
226 		tst = (a > b);
227 	}
228 	else if (!strcmp(op, "<")) {
229 		tst = (a < b);
230 	}
231 	else if (!strcmp(op, "==")) {
232 		tst = (a == b);
233 	}
234 	else if (!strcmp(op, ">=")) {
235 		tst = (a >= b);
236 	}
237 	else if (!strcmp(op, "<=")) {
238 		tst = (a <= b);
239 	}
240 	else if (!strcmp(op, "!=")) {
241 		tst = (a != b);
242 	}
243 	else {
244 		return CLI_INVALID_ARG;
245 	}
246 
247 	return tst ? 0 : -1;
248 }
249 
250 /** if last command failed (return != 0), run <cmd>
251  *    ! <cmd>
252  */
cmd_failed(int argc,char * argv[])253 static int cmd_failed(int argc, char *argv[])
254 {
255 	const struct cli_command *cmd;
256 
257 	CHK_ARGC(2, 0);
258 
259 	cmd = cli_find(argv[1]);
260 	if (cmd == NULL) {
261 		MSG("Unknown command '%s'\n", argv[1]);
262 		return -1;
263 	}
264 	else {
265 		argv++;
266 		return (cli_env_get('?') == 0 ? 0 : cmd->handler(argc - 1, argv));
267 	}
268 }
269 
270 /** print messages
271  *		echo [<arg> ...]
272  */
cmd_echo(int argc,char * argv[])273 static int cmd_echo(int argc, char *argv[])
274 {
275 	int i;
276 
277 	for (i = 1; i < argc; i++) {
278 		MSG("%s%s", i > 1 ? " " : "", argv[i]);
279 	}
280 	MSG("\n");
281 
282 	return 0;
283 }
284 
285 /** set cli environment variable
286  *		set <env> <value>
287  */
cmd_set(int argc,char * argv[])288 static int cmd_set(int argc, char *argv[])
289 {
290 	int val;
291 	int ret = -1;
292 
293 	CHK_ARGC(3, 0);
294 
295 	if (sscanf(argv[2], "%d", &val) == 1) {
296 		ret = cli_env_set(argv[1][0], val);
297 	}
298 
299 	return ret;
300 }
301 
302 /** evaluation the expresstion, result to $1
303  *		evl <value> <op> <value>
304  */
cmd_evl(int argc,char * argv[])305 static int cmd_evl(int argc, char *argv[])
306 {
307 	int val1, val2, result = 0;
308 	int ret = -1;
309 
310 	CHK_ARGC(4, 4);
311 
312 	if (sscanf(argv[1], "%d", &val1) == 1 &&
313 		sscanf(argv[3], "%d", &val2) == 1) {
314 		ret = 0;
315 		switch(argv[2][0]) {
316 			case '+':
317 				result = val1 + val2;
318 				break;
319 			case '-':
320 				result = val1 - val2;
321 				break;
322 			case '*':
323 				result = val1 * val2;
324 				break;
325 			case '/':
326 				if (val2 == 0)
327 					ret = -1;
328 				else
329 					result = val1 / val2;
330 				break;
331 			case '%':
332 				if (val2 == 0)
333 					ret = -1;
334 				else
335 					result = val1 % val2;
336 				break;
337 			default:
338 				ret = CLI_INVALID_ARG;
339 				break;
340 		}
341 	}
342 
343 	if (ret == 0)
344 		ret = cli_env_set('1', result);
345 
346 	return ret;
347 }
348 
cmd_exit(int argc,char * argv[])349 static int cmd_exit(int argc, char *argv[])
350 {
351 	m_exit = TRUE;
352 	return 0;
353 }
354 
355 /** Abort current script
356  *		abort [...]
357  */
cmd_abort(int argc,char * argv[])358 static int cmd_abort(int argc, char *argv[])
359 {
360 	if (argc > 1) {
361 		cmd_echo(argc, argv);
362 	}
363 
364 	m_abort = TRUE;
365 
366 	return 0;
367 }
368 
369 /** run local file system's script
370  *		script <filename>
371  */
cmd_script(int argc,char * argv[])372 static int cmd_script(int argc, char *argv[])
373 {
374 	char line_buf[256];
375 	char *p;
376 	FILE *fp;
377 	const char *name;
378 	int ret = 0;
379 	static int stack = 0;
380 
381 	CHK_ARGC(2, 0);
382 
383 	if (stack++ == 0)
384 		m_abort = FALSE;
385 
386 	name = argv[1];
387 	fp = fopen(name, "r");
388 
389 	if (fp) {
390 		memset(line_buf, 0, sizeof(line_buf));
391 		while (!m_abort && fgets(line_buf, sizeof(line_buf) - 1, fp)) {
392 			p = line_buf + sizeof(line_buf) - 1;
393 			while (*p == 0 && p > line_buf)
394 				p--;
395 			while ((*p == '\r' || *p == '\n') && p > line_buf) {
396 				*p-- = 0;
397 			}
398 			p = cli_process_line(line_buf);
399 			if (*p)
400 				ret = cli_interpret(p);
401 			memset(line_buf, 0, sizeof(line_buf));
402 		}
403 		fclose(fp);
404 	}
405 	else {
406 		MSG("Can't open host script file '%s' for read\n", name);
407 		ret = -1;
408 	}
409 
410 	stack--;
411 
412 	return ret;
413 }
414 
415 
416 
417 static const struct cli_command default_cmds[] =
418 {
419 	{ cmd_help,		"help|?",	"[<command>]",		"show commands or help on one command" },
420 	{ cmd_exit,		"exit",		NULL,				"exit command line" },
421 	{ cmd_exec,		"*",		"<n> <cmd> [...]>",	"run <cmd> <n> times" },
422 	{ cmd_failed,	"!",		"<cmd> [...]",		"run <cmd> if last command failed" },
423 	{ cmd_echo,		"echo",		"[...]",			"print messages" },
424 	{ cmd_set,		"set",		"<env> <val>",		"set env variable" },
425 	{ cmd_evl,		"evl",		"<val> <op> <val>",	"evaluation expresstion" },
426 	{ cmd_test,		"test",		"<a> <op> <b>",		"test expression: <a> <op> <b>" },
427 	{ cmd_script,   "script",   "<file>",           "run host script <file>" },
428 	{ cmd_abort,	"abort",	NULL,				"abort from the running script" },
429 	{ NULL, NULL, NULL, NULL }
430 };
431 
432 static struct cli_commandset default_cmdset = {
433 	default_cmds,
434 };
435 
match_cmd(const char * src,int start,int end,const char * des)436 static BOOL match_cmd(const char *src, int start, int end, const char *des)
437 {
438 	while (src[start] == ' ' && start < end)
439 		start++;
440 
441 	while (src[end] == ' ' && start < end)
442 		end--;
443 
444 	if ((int)strlen(des) == (end - start + 1)) {
445 		if (memcmp(src + start, des, end - start + 1) == 0) {
446 			return TRUE;
447 		}
448 	}
449 
450 	return FALSE;
451 }
452 
check_cmd(const char * cmds,const char * cmd)453 static BOOL check_cmd(const char *cmds, const char *cmd)
454 {
455 	int start, end;
456 
457 	for (start = end = 0; cmds[end] != 0 && cmds[end] != '|'; end++);
458 
459 	while (end > start) {
460 		if (match_cmd(cmds, start, end - 1, cmd) == TRUE)
461 			return TRUE;
462 		if (cmds[end] == 0)
463 			break;
464 		if (cmds[end] == '|') {
465 			end++;
466 			for (start = end; cmds[end] != 0 && cmds[end] != '|'; end++);
467 		}
468 	}
469 
470 	return FALSE;
471 }
472 
cli_find(const char * cmd)473 static const struct cli_command * cli_find(const char *cmd)
474 {
475 	struct cli_commandset *work;
476 	const struct cli_command *s;
477 
478 	FOR_EACH_CLI_CMD(work, s) {
479 		if (check_cmd(s->cmd, cmd) == TRUE)
480 			return s;
481 	}
482 
483 	return NULL;
484 }
485 
show_cmd_usage(const struct cli_command * cmd)486 static void show_cmd_usage(const struct cli_command *cmd)
487 {
488 	MSG("%s: %s\n", cmd->cmd, cmd->descr);
489 	MSG("Usage: %s %s\n", cmd->cmd, cmd->args ? cmd->args : "");
490 }
491 
cmd_help(int argc,char * argv[])492 static int cmd_help(int argc, char *argv[])
493 {
494 	const struct cli_command *cmd;
495 	struct cli_commandset *cmdset;
496 	int i, n;
497 
498 	if (argc < 2)  {
499 		MSG("Available commands:\n");
500 		n = 0;
501 		FOR_EACH_CLI_CMD(cmdset, cmd) {
502 			MSG("%s", cmd->cmd);
503 			for (i = strlen(cmd->cmd); i%10; i++, MSG(" "));
504 			if ((++n % 5) == 0) MSG("\n");
505 		}
506 		MSG("\n");
507 	}
508 	else {
509 		cmd = cli_find(argv[1]);
510 		if (cmd == NULL) {
511 			MSG("No such command\n");
512 			return -1;
513 		}
514 		else {
515 			show_cmd_usage(cmd);
516 		}
517 	}
518 
519 	return 0;
520 }
521 
cli_parse_args(const char * cmd,struct cli_arg * arg)522 static void cli_parse_args(const char *cmd, struct cli_arg *arg)
523 {
524 	char *p;
525 	int val;
526 
527 	if (arg) {
528 		arg->argc = 0;
529 		if (cmd) {
530 			p = arg->_buf;
531 			while (*cmd && arg->argc < MAX_CLI_ARGS_NUM && (p - arg->_buf < MAX_CLI_ARGS_BUF_LEN)) {
532 				while(*cmd && (*cmd == ' ' || *cmd == '\t'))
533 					cmd++;
534 
535 				arg->argv[arg->argc] = p;
536 				while (*cmd && (*cmd != ' ' && *cmd != '\t') && (p - arg->_buf < MAX_CLI_ARGS_BUF_LEN)) {
537 					if (*cmd == '$') {
538 						// command env replacement
539 						cmd++;
540 						val = cli_env_get(*cmd++);
541 						if (p - arg->_buf < MAX_CLI_ARGS_BUF_LEN - 12) {  // 12 is long enough for 32bit 'int'
542 							p += sprintf(p, "%d", val & 0xFFFFFFFF);
543 						}
544 					}
545 					else
546 						*p++ = *cmd++;
547 				}
548 				*p++ = '\0';
549 
550 				if (*(arg->argv[arg->argc]) == '\0')
551 					break;
552 				arg->argc++;
553 			}
554 		}
555 	}
556 }
557 
cli_interpret(const char * line)558 int cli_interpret(const char *line)
559 {
560 	struct cli_arg arg = {0};
561 	const struct cli_command *cmd;
562 	int ret = -1;
563 
564 	cli_parse_args(line, &arg);
565 
566 	if (arg.argc > 0) {
567 		cmd = cli_find(arg.argv[0]);
568 		if (cmd == NULL) {
569 			MSG("Unknown command '%s'\n", arg.argv[0]);
570 		}
571 		else {
572 			ret = cmd->handler(arg.argc, arg.argv);
573 			if (ret == CLI_INVALID_ARG) {
574 				MSG("\n");
575 				show_cmd_usage(cmd);
576 			}
577 		}
578 	}
579 
580 	cli_env_set('?', ret);	// $? = last command return code
581 
582 	return ret;
583 }
584 
cli_add_commandset(struct cli_commandset * set)585 void cli_add_commandset(struct cli_commandset *set)
586 {
587 	if (set) {
588 		set->next = m_cmdset_head;
589 		m_cmdset_head = set;
590 	}
591 }
592 
cli_main_entry()593 void cli_main_entry()
594 {
595 	char line[80];
596 	int linelen = 0;
597 	char *p;
598 
599 	MSG("$ ");
600 	cli_add_commandset(&default_cmdset);
601 
602 	while (!m_exit) {
603 		char ch;
604 		if (linelen >= sizeof(line))
605 			continue;
606 		ch = getc(stdin);
607 		switch (ch) {
608 		case 8:
609 		case 127:
610 			if (linelen > 0) {
611 				--linelen;
612 				MSG("\x08 \x08");
613 			}
614 			break;
615 
616 		case '\r':
617 		case '\n':
618 			//MSG("\r\n");
619 			if (linelen > 0) {
620 				line[linelen] = 0;
621 				p = cli_process_line(line);
622 				if (*p)
623 					cli_interpret(p);
624 				linelen = 0;
625 			}
626 			MSG("$ ");
627 			break;
628 
629 		case 21:
630 			while (linelen > 0) {
631 				--linelen;
632 				MSG("\x08 \x08");
633 			}
634 			break;
635 
636 		default:
637 			if (ch >= ' ' && ch < 127 && linelen < sizeof(line) - 1) {
638 				line[linelen++] = ch;
639 				//MSG("%c", ch);
640 			}
641 		}
642 	}
643 }
644