]> cloudbase.mooo.com Git - z180-stamp.git/blob - avr/command.c
error handling (WIP)
[z180-stamp.git] / avr / command.c
1 /*
2 * (C) Copyright 2014, 2016, 2018 Leo C. <erbl259-lmu@yahoo.de>
3 *
4 * (C) Copyright 2000-2009
5 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
6 *
7 * SPDX-License-Identifier: GPL-2.0
8 */
9
10 /*
11 * Command Processor Table
12 */
13
14 #include "command.h"
15 #include "common.h"
16 #include <ctype.h>
17 #include <setjmp.h>
18
19 #include "config.h"
20 #include "print-utils.h"
21 #include "con-utils.h"
22 #include "env.h"
23 #include "debug.h"
24 #include "getopt-min.h"
25
26 #define DEBUG_CMD 0 /* set to 1 to debug */
27
28 #define debug_cmd(fmt, args...) \
29 debug_cond(DEBUG_CMD, fmt, ##args)
30
31
32 jmp_buf cmd_jbuf;
33
34 char *strcasestr_P2(const FLASH char *haystack, const char *needle)
35 {
36 int nlen = strlen(needle);
37 int hlen = strlen_P(haystack) - nlen + 1;
38 int i;
39
40 for (i = 0; i < hlen; i++) {
41 int j;
42 for (j = 0; j < nlen; j++) {
43 unsigned char c1 = haystack[i+j];
44 unsigned char c2 = needle[j];
45 if (toupper(c1) != toupper(c2))
46 goto next;
47 }
48 return (char *) haystack + i;
49 next:
50 ;
51 }
52 return NULL;
53 }
54
55
56 #if 0
57 static int cmd_tbl_item_count(cmd_tbl_t *p)
58 {
59 int count = 0;
60
61 while (p->name != NULL) {
62 if (p->subcmd) {
63 cmd_tbl_t *sub = p->subcmd;
64 while (sub->name != NULL) {
65 if (sub->flags & CTBL_SUBCMDAUTO)
66 count++;
67 sub++;
68 }
69 }
70 if ((p->flags & CTBL_SUBCMDAUTO) == 0)
71 count++;
72 p++;
73 }
74 return count;
75 }
76 #endif
77
78 static cmd_tbl_t *get_cmd_tbl_base(cmd_tbl_t *cmdtp)
79 {
80 cmd_tbl_t *p = cmdtp;
81
82 while (p->name != NULL)
83 ++p;
84
85 return p->subcmd;
86 }
87
88 static cmd_tbl_t *get_cmd_tbl_parent(cmd_tbl_t *child)
89 {
90 if ((child->flags & CTBL_SUBCMDAUTO) == 0) {
91
92 cmd_tbl_t *tbl_start = get_cmd_tbl_base(child);
93 if (tbl_start != cmd_tbl) {
94 cmd_tbl_t *top = cmd_tbl;
95 while (top->subcmd != tbl_start)
96 ++top;
97 return top;
98 }
99 }
100 return NULL;
101 }
102
103 static int print_nameprefix(cmd_tbl_t *p)
104 {
105 cmd_tbl_t *top = get_cmd_tbl_parent(p);
106
107 int width = 0;
108 if (top) {
109 width = printf_P(PSTR("%S "), top->name);
110 }
111
112 return width;
113 }
114
115 static int print_prefixed_name(cmd_tbl_t *p)
116 {
117 int len;
118
119 len = print_nameprefix(p);
120 len += strlen_P(p->name);
121 my_puts_P(p->name);
122
123 return len;
124 }
125
126 static void print_usage_line(cmd_tbl_t *p, int width)
127 {
128 width -= print_prefixed_name(p);
129 if (width < 0)
130 width = 0;
131 print_blanks(width);
132 my_puts_P(PSTR(" - "));
133 puts_P(p->usage);
134 }
135
136 #if 0
137 static int strcmp_PP(const FLASH char *s1, const FLASH char *s2)
138 {
139 unsigned char c1, c2;
140
141 while ((c1 = *(const FLASH unsigned char *)s1++)
142 == (c2 = *(const FLASH unsigned char *)s2++))
143 if (c1 == 0)
144 return 0;
145
146 return c1 - c2;
147 }
148 #endif
149
150 #if 1
151 static int cmpstring_PP(const void *p1, const void *p2)
152 {
153 char s1[21] = {'\0'};
154 char s2[21] = {'\0'};
155 cmd_tbl_t *cp1 = *(cmd_tbl_t **) p1;
156 cmd_tbl_t *cp2 = *(cmd_tbl_t **) p2;
157 cmd_tbl_t *top1 = get_cmd_tbl_parent(*(cmd_tbl_t **) p1);
158 cmd_tbl_t *top2 = get_cmd_tbl_parent(*(cmd_tbl_t **) p2);
159
160 if (top1)
161 strlcpy_P(s1, top1->name, 21);
162 if (top2)
163 strlcpy_P(s2, top2->name, 21);
164
165 strlcat_P(s1, cp1->name, 21);
166 strlcat_P(s2, cp2->name, 21);
167
168 return strcmp(s1, s2);
169 }
170 #else
171 static int cmpstring_PP(const void *p1, const void *p2)
172 {
173 cmd_tbl_t *cp1 = *(cmd_tbl_t **) p1;
174 cmd_tbl_t *cp2 = *(cmd_tbl_t **) p2;
175 cmd_tbl_t *top1 = get_cmd_tbl_parent(*(cmd_tbl_t **) p1);
176 cmd_tbl_t *top2 = get_cmd_tbl_parent(*(cmd_tbl_t **) p2);
177 int res;
178
179 if (top1 && top2) {
180 res = strcmp_PP(top1->name, top2->name);
181 if (res == 0)
182 res = strcmp_PP(cp1->name, cp2->name);
183 return res;
184 }
185 if (top1) {
186 res = strcmp_PP(top1->name, cp2->name);
187 if (res == 0)
188 res = strcmp_PP(cp1->name, cp2->name);
189 return res;
190 }
191 if (top2) {
192 res = strcmp_PP(cp1->name, top2->name);
193 if (res == 0)
194 res = strcmp_PP(cp1->name, cp2->name);
195 return res;
196 }
197 return strcmp_PP(cp1->name, cp2->name);
198 }
199 #endif
200
201 /****************************************************************************/
202
203 /*
204 * find command table entry for a command
205 */
206
207 static cmd_tbl_t *find_cmd (const char *cmd, cmd_tbl_t *table)
208 {
209 if (!cmd)
210 return NULL;
211
212 char *optenv = getenv_str(PSTR("cmd"));
213 uint8_t opt_debug = optenv && strstr_P(optenv, PSTR("debug")) != NULL;
214
215 cmd_tbl_t *cmdtp_ret = NULL;
216 uint_fast8_t n_found = 0;
217 uint_fast8_t len = strlen(cmd);
218
219 for (cmd_tbl_t *cmdtp = table; cmdtp->name != NULL; cmdtp++) {
220 if (cmdtp->subcmd) {
221 for (cmd_tbl_t *sub = cmdtp->subcmd; sub->name != NULL; sub++) {
222 if (sub->flags & CTBL_SUBCMDAUTO &&
223 strncmp_P(cmd, sub->name, len) == 0 &&
224 (opt_debug || !(sub->flags & CTBL_DBG))) {
225 if (len == strlen_P(sub->name))
226 return sub; /* full match */
227 cmdtp_ret = sub; /* abbreviated command ? */
228 ++n_found;
229 }
230 }
231 }
232 if ((cmdtp->flags & CTBL_SUBCMDAUTO) == 0 &&
233 strncmp_P(cmd, cmdtp->name, len) == 0 &&
234 (opt_debug || !(cmdtp->flags & CTBL_DBG))) {
235 if (len == strlen_P(cmdtp->name))
236 return cmdtp; /* full match */
237 cmdtp_ret = cmdtp; /* abbreviated command ? */
238 ++n_found;
239 }
240 }
241
242 if (n_found == 1)
243 return cmdtp_ret; /* exactly one match */
244
245 return NULL; /* not found or ambiguous command */
246 }
247
248 /*
249 * Use puts() instead of printf() to avoid printf buffer overflow
250 * for long help messages
251 */
252
253 command_ret_t do_help(cmd_tbl_t *cmdtp, uint_fast8_t flag UNUSED, int argc, char * const argv[])
254 {
255 cmd_tbl_t *tbl_start = get_cmd_tbl_base(cmdtp);
256 command_ret_t res = CMD_RET_SUCCESS;
257 uint_fast8_t options = 0;
258 #define OPT_DBG_CMDS 0x01
259 #define OPT_ALL 0x02
260 #define OPT_NAME 0x04
261 #define OPT_USAGE 0x08
262 #define OPT_LONG 0x10
263
264 /* reset getopt() */
265 optind = 0;
266
267 int opt;
268 while ((opt = getopt(argc, argv, PSTR("afk"))) != -1) {
269 switch (opt) {
270 case 'a':
271 options |= OPT_ALL;
272 break;
273 case 'f':
274 options |= OPT_NAME | OPT_ALL;
275 break;
276 case 'k':
277 options |= OPT_USAGE | OPT_ALL;
278 break;
279 default: /* '?' */
280 return CMD_RET_USAGE;
281 }
282 }
283
284 /* remaining arguments */
285 argc -= optind;
286 argv += optind;
287
288 if ((options & (OPT_USAGE|OPT_NAME)) == (OPT_USAGE|OPT_NAME)) {
289 puts_P(PSTR("Inkompatible options: -f -k\n"
290 "Try 'help help'"));
291 return CMD_RET_USAGE;
292 }
293 if (options & OPT_USAGE)
294 options |= OPT_NAME;
295
296 if ((options & (OPT_NAME | OPT_USAGE)) == 0 && argc > 0)
297 options |= OPT_LONG;
298
299 char *optenv = getenv_str(PSTR("cmd"));
300 if (optenv && strstr_P(optenv, PSTR("debug")) != NULL)
301 options |= OPT_DBG_CMDS;
302
303 uint_fast8_t maxlen_cmd;
304 int cmd_items;
305 cmd_tbl_t **cmd_list = NULL;
306 for (uint_fast8_t pass = 0; pass < 2; ++pass) {
307 maxlen_cmd = 0;
308 if (pass == 0)
309 cmd_items = 0;
310 else {
311 cmd_list = (cmd_tbl_t **) malloc(cmd_items * sizeof(cmd_tbl_t *));
312 /* TODO: Check error */
313 }
314
315 /* Make array of commands */
316 cmd_tbl_t *tp = tbl_start;
317 int i = 0;
318 while (tp->name != NULL) {
319 if (tp->subcmd) {
320 cmd_tbl_t *sub = tp->subcmd;
321 while (sub->name != NULL) {
322 if (options & OPT_ALL || sub->flags & CTBL_SUBCMDAUTO) {
323 if (pass == 0)
324 ++cmd_items;
325 else {
326 cmd_list[i++] = sub;
327 uint_fast8_t len = strlen_P(sub->name);
328 if ((sub->flags & CTBL_SUBCMDAUTO) == 0) {
329 len += strlen_P(tp->name) + 1;
330 }
331 if (len > maxlen_cmd) {
332 maxlen_cmd = len;
333 }
334 //debug_cmd("### i:%3d, maxlen:%3d, tp: '%S', sub: '%S'\n", i, maxlen_cmd, tp->name, sub->name);
335 }
336 }
337 sub++;
338 }
339 }
340 if ((tp->flags & CTBL_SUBCMDAUTO) == 0) {
341 if (pass == 0)
342 ++cmd_items;
343 else {
344 cmd_list[i++] = tp;
345 uint_fast8_t len = strlen_P(tp->name);
346 cmd_tbl_t *top = get_cmd_tbl_parent(tp);
347 if (top)
348 len += strlen_P(top->name) + 1;
349 if (len > maxlen_cmd)
350 maxlen_cmd = len;
351 }
352 }
353 tp++;
354 }
355 //debug_cmd("### pass: %d, cmd_items: %d, i: %d, maxlen_cmd: %d\n", pass, cmd_items, i, maxlen_cmd);
356 }
357
358 /* Sort command list */
359 qsort(cmd_list, cmd_items, sizeof (cmd_tbl_t *), cmpstring_PP);
360
361 if ((options & OPT_LONG) == 0) {
362 /* print short help (usage) */
363 for (uint_fast8_t cmdi = 0; cmdi < cmd_items; cmdi++) {
364
365 /* allow user abort */
366 if (ctrlc ()) {
367 res = CMD_RET_FAILURE;
368 break;
369 }
370 if ((cmd_list[cmdi]->flags & CTBL_DBG) && !(options & OPT_DBG_CMDS))
371 continue;
372 if (cmd_list[cmdi]->usage == NULL)
373 continue;
374
375 if (argc == 0)
376 print_usage_line(cmd_list[cmdi], maxlen_cmd);
377 else {
378 for (uint_fast8_t argi = 0; argi < argc; argi++) {
379 if (((options & OPT_NAME) &&
380 strcasestr_P2(cmd_list[cmdi]->name, argv[argi])) ||
381 ((options & OPT_USAGE) &&
382 strcasestr_P2(cmd_list[cmdi]->usage, argv[argi]))) {
383 print_usage_line(cmd_list[cmdi], maxlen_cmd);
384 }
385 }
386 }
387 }
388 } else {
389 /* command help (long version) */
390 for (uint_fast8_t argi = 0; argi < argc; ++argi) {
391 uint_fast8_t got = 0;
392 cmd_tbl_t *tp = find_cmd(argv[argi], tbl_start);
393 if (tp) {
394 cmd_usage(tp);
395 got = 1;
396 }
397 if (options & OPT_ALL) {
398 for (cmd_tbl_t *sub = tbl_start; sub->name != NULL; sub++) {
399 if (sub->subcmd) {
400 tp = find_cmd(argv[argi], sub->subcmd);
401 if (tp) {
402 cmd_usage(tp);
403 got = 1;
404 }
405 }
406 }
407 }
408 if (!got) {
409 printf_P(PSTR("Unknown command '%s' - try 'help help'\n"),
410 argv[argi]);
411 res = CMD_RET_FAILURE;
412 break;
413 }
414 }
415 }
416 free(cmd_list);
417 return res;
418 }
419
420
421 command_ret_t cmd_usage(cmd_tbl_t *cmdtp)
422 {
423 print_usage_line(cmdtp, 0);
424 #ifdef CONFIG_SYS_LONGHELP
425 my_puts_P(PSTR("Usage:\n"));
426 print_prefixed_name(cmdtp);
427 my_puts_P(PSTR(" "));
428
429 if (cmdtp->help && *cmdtp->help != '\0')
430 puts_P(cmdtp->help);
431 else
432 my_puts_P(PSTR(" - No additional help available.\n"));
433 #endif /* CONFIG_SYS_LONGHELP */
434 return CMD_RET_FAILURE;
435 }
436
437 #ifdef CONFIG_AUTO_COMPLETE
438
439 int var_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[])
440 {
441 static char tmp_buf[CONFIG_SYS_CBSIZE];
442 int space;
443
444 space = last_char == '\0' || isblank(last_char);
445
446 if (space && argc == 1)
447 return env_complete("", maxv, cmdv, sizeof(tmp_buf), tmp_buf);
448
449 if (!space && argc == 2)
450 return env_complete(argv[1], maxv, cmdv, sizeof(tmp_buf), tmp_buf);
451
452 return 0;
453 }
454
455 /*************************************************************************************/
456
457 /* TODO: cmdtp points to FLASH */
458
459 static int complete_cmdv(int argc, char * const argv[], char last_char, int maxv, char *cmdv[])
460 {
461 cmd_tbl_t *cmdtp = cmd_tbl;
462 // const int count = ARRAY_SIZE(cmd_tbl);
463 // const cmd_tbl_t *cmdend = cmdtp + count;
464 // const char *p;
465 int len, clen;
466 int n_found = 0;
467 const char *cmd;
468
469 /* sanity? */
470 if (maxv < 2)
471 return -2;
472
473 cmdv[0] = NULL;
474
475 if (argc == 0) {
476 /* output full list of commands */
477 for (; cmdtp->name[0] != '\0'; cmdtp++) {
478 if (n_found >= maxv - 2) {
479 cmdv[n_found++] = "...";
480 break;
481 }
482 cmdv[n_found++] = cmdtp->name;
483 }
484 cmdv[n_found] = NULL;
485 return n_found;
486 }
487
488 /* more than one arg or one but the start of the next */
489 if (argc > 1 || (last_char == '\0' || isblank(last_char))) {
490 cmdtp = find_cmd(argv[0], cmd_tbl);
491 if (cmdtp == NULL || cmdtp->complete == NULL) {
492 cmdv[0] = NULL;
493 return 0;
494 }
495 return (*cmdtp->complete)(argc, argv, last_char, maxv, cmdv);
496 }
497
498 cmd = argv[0];
499
500 len = strlen(cmd);
501
502 /* return the partial matches */
503 for (; cmdtp->name[0] != '\0'; cmdtp++) {
504
505 clen = strlen(cmdtp->name);
506 if (clen < len)
507 continue;
508
509 if (memcmp(cmd, cmdtp->name, len) != 0)
510 continue;
511
512 /* too many! */
513 if (n_found >= maxv - 2) {
514 cmdv[n_found++] = "...";
515 break;
516 }
517
518 cmdv[n_found++] = cmdtp->name;
519 }
520
521 cmdv[n_found] = NULL;
522 return n_found;
523 }
524
525 static int make_argv(char *s, int argvsz, char *argv[])
526 {
527 int argc = 0;
528
529 /* split into argv */
530 while (argc < argvsz - 1) {
531
532 /* skip any white space */
533 while (isblank(*s))
534 ++s;
535
536 if (*s == '\0') /* end of s, no more args */
537 break;
538
539 argv[argc++] = s; /* begin of argument string */
540
541 /* find end of string */
542 while (*s && !isblank(*s))
543 ++s;
544
545 if (*s == '\0') /* end of s, no more args */
546 break;
547
548 *s++ = '\0'; /* terminate current arg */
549 }
550 argv[argc] = NULL;
551
552 return argc;
553 }
554
555 static void print_argv(const char *banner, const char *leader, const char *sep, int linemax, char * const argv[])
556 {
557 int ll = leader != NULL ? strlen(leader) : 0;
558 int sl = sep != NULL ? strlen(sep) : 0;
559 int len, i;
560
561 if (banner) {
562 my_puts_P(PSTR("\n"));
563 my_puts(banner);
564 }
565
566 i = linemax; /* force leader and newline */
567 while (*argv != NULL) {
568 len = strlen(*argv) + sl;
569 if (i + len >= linemax) {
570 my_puts_P(PSTR("\n"));
571 if (leader)
572 my_puts(leader);
573 i = ll - sl;
574 } else if (sep)
575 my_puts(sep);
576 my_puts(*argv++);
577 i += len;
578 }
579 my_puts_P(PSTR("\n"));
580 }
581
582 static int find_common_prefix(char * const argv[])
583 {
584 int i, len;
585 char *anchor, *s, *t;
586
587 if (*argv == NULL)
588 return 0;
589
590 /* begin with max */
591 anchor = *argv++;
592 len = strlen(anchor);
593 while ((t = *argv++) != NULL) {
594 s = anchor;
595 for (i = 0; i < len; i++, t++, s++) {
596 if (*t != *s)
597 break;
598 }
599 len = s - anchor;
600 }
601 return len;
602 }
603
604 static char tmp_buf[CONFIG_SYS_CBSIZE]; /* copy of console I/O buffer */
605
606
607 int cmd_auto_complete(const FLASH char *const prompt, char *buf, int *np, int *colp)
608 {
609 int n = *np, col = *colp;
610 char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
611 char *cmdv[20];
612 char *s, *t;
613 const char *sep;
614 int i, j, k, len, seplen, argc;
615 int cnt;
616 char last_char;
617
618 if (strcmp_PP(prompt, CONFIG_SYS_PROMPT) != 0)
619 return 0; /* not in normal console */
620
621 cnt = strlen(buf);
622 if (cnt >= 1)
623 last_char = buf[cnt - 1];
624 else
625 last_char = '\0';
626
627 /* copy to secondary buffer which will be affected */
628 strcpy(tmp_buf, buf);
629
630 /* separate into argv */
631 argc = make_argv(tmp_buf, sizeof(argv)/sizeof(argv[0]), argv);
632
633 /* do the completion and return the possible completions */
634 i = complete_cmdv(argc, argv, last_char, sizeof(cmdv)/sizeof(cmdv[0]), cmdv);
635
636 /* no match; bell and out */
637 if (i == 0) {
638 if (argc > 1) /* allow tab for non command */
639 return 0;
640 putchar('\a');
641 return 1;
642 }
643
644 s = NULL;
645 len = 0;
646 sep = NULL;
647 seplen = 0;
648 if (i == 1) { /* one match; perfect */
649 k = strlen(argv[argc - 1]);
650 s = cmdv[0] + k;
651 len = strlen(s);
652 sep = " ";
653 seplen = 1;
654 } else if (i > 1 && (j = find_common_prefix(cmdv)) != 0) { /* more */
655 k = strlen(argv[argc - 1]);
656 j -= k;
657 if (j > 0) {
658 s = cmdv[0] + k;
659 len = j;
660 }
661 }
662
663 if (s != NULL) {
664 k = len + seplen;
665 /* make sure it fits */
666 if (n + k >= CONFIG_SYS_CBSIZE - 2) {
667 putchar('\a');
668 return 1;
669 }
670
671 t = buf + cnt;
672 for (i = 0; i < len; i++)
673 *t++ = *s++;
674 if (sep != NULL)
675 for (i = 0; i < seplen; i++)
676 *t++ = sep[i];
677 *t = '\0';
678 n += k;
679 col += k;
680 my_puts(t - k);
681 if (sep == NULL)
682 putchar('\a');
683 *np = n;
684 *colp = col;
685 } else {
686 print_argv(NULL, " ", " ", 78, cmdv);
687
688 my_puts_P(prompt);
689 my_puts(buf);
690 }
691 return 1;
692 }
693
694 #endif /* CONFIG_AUTO_COMPLETE */
695
696
697 static cmd_tbl_t *cmd_invocation_ptr;
698
699 /**
700 * Call a command function. This should be the only route in U-Boot to call
701 * a command, so that we can track whether we are waiting for input or
702 * executing a command.
703 *
704 * @param cmdtp Pointer to the command to execute
705 * @param flag Some flags normally 0 (see CMD_FLAG_.. above)
706 * @param argc Number of arguments (arg 0 must be the command text)
707 * @param argv Arguments
708 * @return 0 if command succeeded, else non-zero (CMD_RET_...)
709 */
710 command_ret_t cmd_call(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
711 {
712 command_ret_t result;
713
714 result = (cmdtp->cmd)(cmdtp, flag, argc, argv);
715 // if (result != CMD_RET_SUCCESS)
716 // debug("Command failed, result=%d\n", result);
717 return result;
718 }
719
720 #pragma GCC diagnostic ignored "-Wclobbered"
721
722 command_ret_t cmd_process(uint_fast8_t flag, int argc, char * const argv[],
723 uint_fast8_t *repeatable)
724 {
725 command_ret_t rc = CMD_RET_SUCCESS;
726 cmd_tbl_t *cmdtp;
727
728 /* Look up command in command table */
729 cmdtp = find_cmd(argv[0], cmd_tbl);
730 if (cmdtp != NULL) {
731 /* Check if this command has subcommands */
732 if (cmdtp->subcmd && argc > 1) {
733
734 /* Look up subcommand in subcommand table */
735 cmd_tbl_t *cmdtpsub = find_cmd(argv[1], cmdtp->subcmd);
736 if (cmdtpsub == NULL) {
737 printf_P(PSTR("Unknown '%s' subcommand '%s' - try '%s help'\n"), argv[0], argv[1], argv[0]);
738 return CMD_RET_FAILURE;
739 }
740 cmdtp = cmdtpsub;
741 --argc;
742 ++argv;
743 }
744 }
745
746 if (cmdtp == NULL) {
747 printf_P(PSTR("Unknown command '%s' - try 'help'\n"), argv[0]);
748 return CMD_RET_FAILURE;
749 }
750
751 /* found - check max args */
752 if (argc > cmdtp->maxargs)
753 rc = CMD_RET_USAGE;
754
755 #if defined(CONFIG_CMD_BOOTD)
756 /* avoid "bootd" recursion */
757 else if (cmdtp->cmd == do_bootd) {
758 if (flag & CMD_FLAG_BOOTD) {
759 my_puts_P(PSTR("'bootd' recursion detected\n"));
760 rc = CMD_RET_FAILURE;
761 } else {
762 flag |= CMD_FLAG_BOOTD;
763 }
764 }
765 #endif
766
767 if (setjmp(cmd_jbuf) != 0)
768 return CMD_RET_FAILURE;
769
770 /* If OK so far, then do the command */
771 if (!rc) {
772 cmd_invocation_ptr = cmdtp;
773 rc = cmd_call(cmdtp, flag, argc, argv);
774 *repeatable &= (cmdtp->flags & CTBL_RPT) != 0;
775 }
776 if (rc == CMD_RET_USAGE)
777 rc = cmd_usage(cmdtp);
778 return rc;
779 }
780
781 int cmd_process_error(cmd_tbl_t *cmdtp, int err)
782 {
783 char buf[strlen_P(cmdtp->name) + 1];
784 strcpy_P(buf, cmdtp->name);
785
786 if (err) {
787 printf_P(PSTR("Command '%s' failed: Error %d\n"), buf, err);
788 return 1;
789 }
790
791 return 0;
792 }
793
794
795 void cmd_error(const char *fmt, ...)
796 {
797 va_list ap;
798 va_start(ap, fmt);
799 print_prefixed_name(cmd_invocation_ptr);
800 my_puts_P(PSTR(": "));
801 vfprintf_P(stdout, fmt, ap);
802 va_end(ap);
803 putchar('\n');
804 _delay_ms(20);
805 //command_ret = CMD_RET_FAILURE;
806 }