]> cloudbase.mooo.com Git - z180-stamp.git/blob - avr/command.c
8a4ca02886706005b3aa8b672cb402c7a16d597f
[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 <setjmp.h>
17
18 #include "config.h"
19 #include "print-utils.h"
20 #include "con-utils.h"
21 #include "env.h"
22 #include "debug.h"
23 #include "getopt-min.h"
24 #include "strerror.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 int opt;
265 while ((opt = getopt(argc, argv, PSTR("afk"))) != -1) {
266 switch (opt) {
267 case 'a':
268 options |= OPT_ALL;
269 break;
270 case 'f':
271 options |= OPT_NAME | OPT_ALL;
272 break;
273 case 'k':
274 options |= OPT_USAGE | OPT_ALL;
275 break;
276 default: /* '?' */
277 return CMD_RET_USAGE;
278 }
279 }
280
281 /* remaining arguments */
282 argc -= optind;
283 argv += optind;
284
285 if ((options & (OPT_USAGE|OPT_NAME)) == (OPT_USAGE|OPT_NAME)) {
286 puts_P(PSTR("Inkompatible options: -f -k\n"
287 "Try 'help help'"));
288 return CMD_RET_USAGE;
289 }
290 if (options & OPT_USAGE)
291 options |= OPT_NAME;
292
293 if ((options & (OPT_NAME | OPT_USAGE)) == 0 && argc > 0)
294 options |= OPT_LONG;
295
296 char *optenv = getenv_str(PSTR("cmd"));
297 if (optenv && strstr_P(optenv, PSTR("debug")) != NULL)
298 options |= OPT_DBG_CMDS;
299
300 uint_fast8_t maxlen_cmd;
301 int cmd_items;
302 cmd_tbl_t **cmd_list = NULL;
303 for (uint_fast8_t pass = 0; pass < 2; ++pass) {
304 maxlen_cmd = 0;
305 if (pass == 0)
306 cmd_items = 0;
307 else {
308 cmd_list = (cmd_tbl_t **) malloc(cmd_items * sizeof(cmd_tbl_t *));
309 /* TODO: Check error */
310 }
311
312 /* Make array of commands */
313 cmd_tbl_t *tp = tbl_start;
314 int i = 0;
315 while (tp->name != NULL) {
316 if (tp->subcmd) {
317 cmd_tbl_t *sub = tp->subcmd;
318 while (sub->name != NULL) {
319 if (options & OPT_ALL || sub->flags & CTBL_SUBCMDAUTO) {
320 if (pass == 0)
321 ++cmd_items;
322 else {
323 cmd_list[i++] = sub;
324 uint_fast8_t len = strlen_P(sub->name);
325 if ((sub->flags & CTBL_SUBCMDAUTO) == 0) {
326 len += strlen_P(tp->name) + 1;
327 }
328 if (len > maxlen_cmd) {
329 maxlen_cmd = len;
330 }
331 //debug_cmd("### i:%3d, maxlen:%3d, tp: '%S', sub: '%S'\n", i, maxlen_cmd, tp->name, sub->name);
332 }
333 }
334 sub++;
335 }
336 }
337 if ((tp->flags & CTBL_SUBCMDAUTO) == 0) {
338 if (pass == 0)
339 ++cmd_items;
340 else {
341 cmd_list[i++] = tp;
342 uint_fast8_t len = strlen_P(tp->name);
343 cmd_tbl_t *top = get_cmd_tbl_parent(tp);
344 if (top)
345 len += strlen_P(top->name) + 1;
346 if (len > maxlen_cmd)
347 maxlen_cmd = len;
348 }
349 }
350 tp++;
351 }
352 //debug_cmd("### pass: %d, cmd_items: %d, i: %d, maxlen_cmd: %d\n", pass, cmd_items, i, maxlen_cmd);
353 }
354
355 /* Sort command list */
356 qsort(cmd_list, cmd_items, sizeof (cmd_tbl_t *), cmpstring_PP);
357
358 if ((options & OPT_LONG) == 0) {
359 /* print short help (usage) */
360 for (uint_fast8_t cmdi = 0; cmdi < cmd_items; cmdi++) {
361
362 /* allow user abort */
363 if (ctrlc ()) {
364 res = CMD_RET_FAILURE;
365 break;
366 }
367 if ((cmd_list[cmdi]->flags & CTBL_DBG) && !(options & OPT_DBG_CMDS))
368 continue;
369 if (cmd_list[cmdi]->usage == NULL)
370 continue;
371
372 if (argc == 0)
373 print_usage_line(cmd_list[cmdi], maxlen_cmd);
374 else {
375 for (uint_fast8_t argi = 0; argi < argc; argi++) {
376 if (((options & OPT_NAME) &&
377 strcasestr_P2(cmd_list[cmdi]->name, argv[argi])) ||
378 ((options & OPT_USAGE) &&
379 strcasestr_P2(cmd_list[cmdi]->usage, argv[argi]))) {
380 print_usage_line(cmd_list[cmdi], maxlen_cmd);
381 }
382 }
383 }
384 }
385 } else {
386 /* command help (long version) */
387 for (uint_fast8_t argi = 0; argi < argc; ++argi) {
388 uint_fast8_t got = 0;
389 cmd_tbl_t *tp = find_cmd(argv[argi], tbl_start);
390 if (tp) {
391 cmd_usage(tp);
392 got = 1;
393 }
394 if (options & OPT_ALL) {
395 for (cmd_tbl_t *sub = tbl_start; sub->name != NULL; sub++) {
396 if (sub->subcmd) {
397 tp = find_cmd(argv[argi], sub->subcmd);
398 if (tp) {
399 cmd_usage(tp);
400 got = 1;
401 }
402 }
403 }
404 }
405 if (!got) {
406 printf_P(PSTR("Unknown command '%s' - try 'help help'\n"),
407 argv[argi]);
408 res = CMD_RET_FAILURE;
409 break;
410 }
411 }
412 }
413 free(cmd_list);
414 return res;
415 }
416
417
418 command_ret_t cmd_usage(cmd_tbl_t *cmdtp)
419 {
420 print_usage_line(cmdtp, 0);
421 #ifdef CONFIG_SYS_LONGHELP
422 my_puts_P(PSTR("Usage:\n"));
423 print_prefixed_name(cmdtp);
424 my_puts_P(PSTR(" "));
425
426 if (cmdtp->help && *cmdtp->help != '\0')
427 puts_P(cmdtp->help);
428 else
429 my_puts_P(PSTR(" - No additional help available.\n"));
430 #endif /* CONFIG_SYS_LONGHELP */
431 return CMD_RET_FAILURE;
432 }
433
434 #ifdef CONFIG_AUTO_COMPLETE
435
436 int var_complete(int argc, char * const argv[], char last_char, int maxv, char *cmdv[])
437 {
438 static char tmp_buf[CONFIG_SYS_CBSIZE];
439 int space;
440
441 space = last_char == '\0' || isblank(last_char);
442
443 if (space && argc == 1)
444 return env_complete("", maxv, cmdv, sizeof(tmp_buf), tmp_buf);
445
446 if (!space && argc == 2)
447 return env_complete(argv[1], maxv, cmdv, sizeof(tmp_buf), tmp_buf);
448
449 return 0;
450 }
451
452 /*************************************************************************************/
453
454 /* TODO: cmdtp points to FLASH */
455
456 static int complete_cmdv(int argc, char * const argv[], char last_char, int maxv, char *cmdv[])
457 {
458 cmd_tbl_t *cmdtp = cmd_tbl;
459 // const int count = ARRAY_SIZE(cmd_tbl);
460 // const cmd_tbl_t *cmdend = cmdtp + count;
461 // const char *p;
462 int len, clen;
463 int n_found = 0;
464 const char *cmd;
465
466 /* sanity? */
467 if (maxv < 2)
468 return -2;
469
470 cmdv[0] = NULL;
471
472 if (argc == 0) {
473 /* output full list of commands */
474 for (; cmdtp->name[0] != '\0'; cmdtp++) {
475 if (n_found >= maxv - 2) {
476 cmdv[n_found++] = "...";
477 break;
478 }
479 cmdv[n_found++] = cmdtp->name;
480 }
481 cmdv[n_found] = NULL;
482 return n_found;
483 }
484
485 /* more than one arg or one but the start of the next */
486 if (argc > 1 || (last_char == '\0' || isblank(last_char))) {
487 cmdtp = find_cmd(argv[0], cmd_tbl);
488 if (cmdtp == NULL || cmdtp->complete == NULL) {
489 cmdv[0] = NULL;
490 return 0;
491 }
492 return (*cmdtp->complete)(argc, argv, last_char, maxv, cmdv);
493 }
494
495 cmd = argv[0];
496
497 len = strlen(cmd);
498
499 /* return the partial matches */
500 for (; cmdtp->name[0] != '\0'; cmdtp++) {
501
502 clen = strlen(cmdtp->name);
503 if (clen < len)
504 continue;
505
506 if (memcmp(cmd, cmdtp->name, len) != 0)
507 continue;
508
509 /* too many! */
510 if (n_found >= maxv - 2) {
511 cmdv[n_found++] = "...";
512 break;
513 }
514
515 cmdv[n_found++] = cmdtp->name;
516 }
517
518 cmdv[n_found] = NULL;
519 return n_found;
520 }
521
522 static int make_argv(char *s, int argvsz, char *argv[])
523 {
524 int argc = 0;
525
526 /* split into argv */
527 while (argc < argvsz - 1) {
528
529 /* skip any white space */
530 while (isblank(*s))
531 ++s;
532
533 if (*s == '\0') /* end of s, no more args */
534 break;
535
536 argv[argc++] = s; /* begin of argument string */
537
538 /* find end of string */
539 while (*s && !isblank(*s))
540 ++s;
541
542 if (*s == '\0') /* end of s, no more args */
543 break;
544
545 *s++ = '\0'; /* terminate current arg */
546 }
547 argv[argc] = NULL;
548
549 return argc;
550 }
551
552 static void print_argv(const char *banner, const char *leader, const char *sep, int linemax, char * const argv[])
553 {
554 int ll = leader != NULL ? strlen(leader) : 0;
555 int sl = sep != NULL ? strlen(sep) : 0;
556 int len, i;
557
558 if (banner) {
559 my_puts_P(PSTR("\n"));
560 my_puts(banner);
561 }
562
563 i = linemax; /* force leader and newline */
564 while (*argv != NULL) {
565 len = strlen(*argv) + sl;
566 if (i + len >= linemax) {
567 my_puts_P(PSTR("\n"));
568 if (leader)
569 my_puts(leader);
570 i = ll - sl;
571 } else if (sep)
572 my_puts(sep);
573 my_puts(*argv++);
574 i += len;
575 }
576 my_puts_P(PSTR("\n"));
577 }
578
579 static int find_common_prefix(char * const argv[])
580 {
581 int i, len;
582 char *anchor, *s, *t;
583
584 if (*argv == NULL)
585 return 0;
586
587 /* begin with max */
588 anchor = *argv++;
589 len = strlen(anchor);
590 while ((t = *argv++) != NULL) {
591 s = anchor;
592 for (i = 0; i < len; i++, t++, s++) {
593 if (*t != *s)
594 break;
595 }
596 len = s - anchor;
597 }
598 return len;
599 }
600
601 static char tmp_buf[CONFIG_SYS_CBSIZE]; /* copy of console I/O buffer */
602
603
604 int cmd_auto_complete(const FLASH char *const prompt, char *buf, int *np, int *colp)
605 {
606 int n = *np, col = *colp;
607 char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
608 char *cmdv[20];
609 char *s, *t;
610 const char *sep;
611 int i, j, k, len, seplen, argc;
612 int cnt;
613 char last_char;
614
615 if (strcmp_PP(prompt, CONFIG_SYS_PROMPT) != 0)
616 return 0; /* not in normal console */
617
618 cnt = strlen(buf);
619 if (cnt >= 1)
620 last_char = buf[cnt - 1];
621 else
622 last_char = '\0';
623
624 /* copy to secondary buffer which will be affected */
625 strcpy(tmp_buf, buf);
626
627 /* separate into argv */
628 argc = make_argv(tmp_buf, sizeof(argv)/sizeof(argv[0]), argv);
629
630 /* do the completion and return the possible completions */
631 i = complete_cmdv(argc, argv, last_char, sizeof(cmdv)/sizeof(cmdv[0]), cmdv);
632
633 /* no match; bell and out */
634 if (i == 0) {
635 if (argc > 1) /* allow tab for non command */
636 return 0;
637 putchar('\a');
638 return 1;
639 }
640
641 s = NULL;
642 len = 0;
643 sep = NULL;
644 seplen = 0;
645 if (i == 1) { /* one match; perfect */
646 k = strlen(argv[argc - 1]);
647 s = cmdv[0] + k;
648 len = strlen(s);
649 sep = " ";
650 seplen = 1;
651 } else if (i > 1 && (j = find_common_prefix(cmdv)) != 0) { /* more */
652 k = strlen(argv[argc - 1]);
653 j -= k;
654 if (j > 0) {
655 s = cmdv[0] + k;
656 len = j;
657 }
658 }
659
660 if (s != NULL) {
661 k = len + seplen;
662 /* make sure it fits */
663 if (n + k >= CONFIG_SYS_CBSIZE - 2) {
664 putchar('\a');
665 return 1;
666 }
667
668 t = buf + cnt;
669 for (i = 0; i < len; i++)
670 *t++ = *s++;
671 if (sep != NULL)
672 for (i = 0; i < seplen; i++)
673 *t++ = sep[i];
674 *t = '\0';
675 n += k;
676 col += k;
677 my_puts(t - k);
678 if (sep == NULL)
679 putchar('\a');
680 *np = n;
681 *colp = col;
682 } else {
683 print_argv(NULL, " ", " ", 78, cmdv);
684
685 my_puts_P(prompt);
686 my_puts(buf);
687 }
688 return 1;
689 }
690
691 #endif /* CONFIG_AUTO_COMPLETE */
692
693
694 static cmd_tbl_t *cmd_invocation_ptr;
695
696 /**
697 * Call a command function. This should be the only route in U-Boot to call
698 * a command, so that we can track whether we are waiting for input or
699 * executing a command.
700 *
701 * @param cmdtp Pointer to the command to execute
702 * @param flag Some flags normally 0 (see CMD_FLAG_.. above)
703 * @param argc Number of arguments (arg 0 must be the command text)
704 * @param argv Arguments
705 * @return 0 if command succeeded, else non-zero (CMD_RET_...)
706 */
707 static command_ret_t cmd_call(cmd_tbl_t *cmdtp, uint_fast8_t flag, int argc, char * const argv[])
708 {
709 command_ret_t result;
710
711 result = (cmdtp->cmd)(cmdtp, flag, argc, argv);
712 // if (result != CMD_RET_SUCCESS)
713 // debug("Command failed, result=%d\n", result);
714 return result;
715 }
716
717 #pragma GCC diagnostic ignored "-Wclobbered"
718
719 command_ret_t cmd_process(uint_fast8_t flag, int argc, char * const argv[],
720 uint_fast8_t *repeatable)
721 {
722 command_ret_t rc = CMD_RET_SUCCESS;
723 cmd_tbl_t *cmdtp;
724
725 /* Look up command in command table */
726 cmdtp = find_cmd(argv[0], cmd_tbl);
727 if (cmdtp != NULL) {
728 /* Check if this command has subcommands */
729 if (cmdtp->subcmd && argc > 1) {
730
731 /* Look up subcommand in subcommand table */
732 cmd_tbl_t *cmdtpsub = find_cmd(argv[1], cmdtp->subcmd);
733 if (cmdtpsub == NULL) {
734 printf_P(PSTR("Unknown '%s' subcommand '%s' - try '%s help'\n"), argv[0], argv[1], argv[0]);
735 return CMD_RET_FAILURE;
736 }
737 cmdtp = cmdtpsub;
738 --argc;
739 ++argv;
740 }
741 }
742
743 if (cmdtp == NULL) {
744 printf_P(PSTR("Unknown command '%s' - try 'help'\n"), argv[0]);
745 return CMD_RET_FAILURE;
746 }
747
748 /* found - check max args */
749 if (argc > cmdtp->maxargs)
750 rc = CMD_RET_USAGE;
751
752 #if defined(CONFIG_CMD_BOOTD)
753 /* avoid "bootd" recursion */
754 else if (cmdtp->cmd == do_bootd) {
755 if (flag & CMD_FLAG_BOOTD) {
756 my_puts_P(PSTR("'bootd' recursion detected\n"));
757 rc = CMD_RET_FAILURE;
758 } else {
759 flag |= CMD_FLAG_BOOTD;
760 }
761 }
762 #endif
763
764 if (setjmp(cmd_jbuf) != 0)
765 return CMD_RET_FAILURE;
766
767 /* If OK so far, then do the command */
768 if (!rc) {
769 optind = 0; /* reset getopt() */
770 cmd_invocation_ptr = cmdtp;
771 rc = cmd_call(cmdtp, flag, argc, argv);
772 *repeatable &= (cmdtp->flags & CTBL_RPT) != 0;
773 }
774 if (rc == CMD_RET_USAGE)
775 rc = cmd_usage(cmdtp);
776 return rc;
777 }
778
779 int cmd_process_error(cmd_tbl_t *cmdtp, int err)
780 {
781 char buf[strlen_P(cmdtp->name) + 1];
782 strcpy_P(buf, cmdtp->name);
783
784 if (err) {
785 printf_P(PSTR("Command '%s' failed: Error %d\n"), buf, err);
786 return 1;
787 }
788
789 return 0;
790 }
791
792 void cmd_error(int status, int errnum, const char *fmt, ...)
793 {
794 va_list ap;
795 va_start(ap, fmt);
796 print_prefixed_name(cmd_invocation_ptr);
797 if (fmt != NULL) {
798 my_puts_P(PSTR(": "));
799 vfprintf_P(stdout, fmt, ap);
800 }
801 va_end(ap);
802
803 if (errnum != 0)
804 printf_P(PSTR(": %S"), my_strerror_P(errnum));
805
806 putchar('\n');
807 _delay_ms(20);
808
809 if (status != 0) {
810 longjmp(cmd_jbuf, 1);
811 }
812 }