+ cmd_tbl_t *tbl_start = get_cmd_tbl_base(cmdtp);
+ command_ret_t res = CMD_RET_SUCCESS;
+ uint_fast8_t options = 0;
+#define OPT_DBG_CMDS 0x01
+#define OPT_ALL 0x02
+#define OPT_NAME 0x04
+#define OPT_USAGE 0x08
+#define OPT_LONG 0x10
+
+ int opt;
+ while ((opt = getopt(argc, argv, PSTR("afk"))) != -1) {
+ switch (opt) {
+ case 'a':
+ options |= OPT_ALL;
+ break;
+ case 'f':
+ options |= OPT_NAME | OPT_ALL;
+ break;
+ case 'k':
+ options |= OPT_USAGE | OPT_ALL;
+ break;
+ default: /* '?' */
+ return CMD_RET_USAGE;
+ }
+ }
+
+ /* remaining arguments */
+ argc -= optind;
+ argv += optind;
+
+ if ((options & (OPT_USAGE|OPT_NAME)) == (OPT_USAGE|OPT_NAME)) {
+ puts_P(PSTR("Inkompatible options: -f -k\n"
+ "Try 'help help'"));
+ return CMD_RET_USAGE;
+ }
+ if (options & OPT_USAGE)
+ options |= OPT_NAME;
+
+ if ((options & (OPT_NAME | OPT_USAGE)) == 0 && argc > 0)
+ options |= OPT_LONG;
+
+ char *optenv = getenv_str(PSTR("cmd"));
+ if (optenv && strstr_P(optenv, PSTR("debug")) != NULL)
+ options |= OPT_DBG_CMDS;
+
+ uint_fast8_t maxlen_cmd;
+ int cmd_items;
+ cmd_tbl_t **cmd_list = NULL;
+ for (uint_fast8_t pass = 0; pass < 2; ++pass) {
+ maxlen_cmd = 0;
+ if (pass == 0)
+ cmd_items = 0;
+ else {
+ cmd_list = (cmd_tbl_t **) malloc(cmd_items * sizeof(cmd_tbl_t *));
+ /* TODO: Check error */
+ }
+
+ /* Make array of commands */
+ cmd_tbl_t *tp = tbl_start;
+ int i = 0;
+ while (tp->name != NULL) {
+ if (tp->subcmd) {
+ cmd_tbl_t *sub = tp->subcmd;
+ while (sub->name != NULL) {
+ if (options & OPT_ALL || sub->flags & CTBL_SUBCMDAUTO) {
+ if (pass == 0)
+ ++cmd_items;
+ else {
+ cmd_list[i++] = sub;
+ uint_fast8_t len = strlen_P(sub->name);
+ if ((sub->flags & CTBL_SUBCMDAUTO) == 0) {
+ len += strlen_P(tp->name) + 1;
+ }
+ if (len > maxlen_cmd) {
+ maxlen_cmd = len;
+ }
+//debug_cmd("### i:%3d, maxlen:%3d, tp: '%S', sub: '%S'\n", i, maxlen_cmd, tp->name, sub->name);
+ }
+ }
+ sub++;
+ }
+ }
+ if ((tp->flags & CTBL_SUBCMDAUTO) == 0) {
+ if (pass == 0)
+ ++cmd_items;
+ else {
+ cmd_list[i++] = tp;
+ uint_fast8_t len = strlen_P(tp->name);
+ cmd_tbl_t *top = get_cmd_tbl_parent(tp);
+ if (top)
+ len += strlen_P(top->name) + 1;
+ if (len > maxlen_cmd)
+ maxlen_cmd = len;
+ }
+ }
+ tp++;
+ }
+//debug_cmd("### pass: %d, cmd_items: %d, i: %d, maxlen_cmd: %d\n", pass, cmd_items, i, maxlen_cmd);
+ }
+
+ /* Sort command list */
+ qsort(cmd_list, cmd_items, sizeof (cmd_tbl_t *), cmpstring_PP);
+
+ if ((options & OPT_LONG) == 0) {
+ /* print short help (usage) */
+ for (uint_fast8_t cmdi = 0; cmdi < cmd_items; cmdi++) {
+
+ /* allow user abort */
+ if (ctrlc ()) {
+ res = CMD_RET_FAILURE;
+ break;
+ }
+ if ((cmd_list[cmdi]->flags & CTBL_DBG) && !(options & OPT_DBG_CMDS))
+ continue;
+ if (cmd_list[cmdi]->usage == NULL)
+ continue;
+
+ if (argc == 0)
+ print_usage_line(cmd_list[cmdi], maxlen_cmd);
+ else {
+ for (uint_fast8_t argi = 0; argi < argc; argi++) {
+ if (((options & OPT_NAME) &&
+ strcasestr_P2(cmd_list[cmdi]->name, argv[argi])) ||
+ ((options & OPT_USAGE) &&
+ strcasestr_P2(cmd_list[cmdi]->usage, argv[argi]))) {
+ print_usage_line(cmd_list[cmdi], maxlen_cmd);
+ }
+ }
+ }
+ }
+ } else {
+ /* command help (long version) */
+ for (uint_fast8_t argi = 0; argi < argc; ++argi) {
+ uint_fast8_t got = 0;
+ cmd_tbl_t *tp = find_cmd(argv[argi], tbl_start);
+ if (tp) {
+ cmd_usage(tp);
+ got = 1;
+ }
+ if (options & OPT_ALL) {
+ for (cmd_tbl_t *sub = tbl_start; sub->name != NULL; sub++) {
+ if (sub->subcmd) {
+ tp = find_cmd(argv[argi], sub->subcmd);
+ if (tp) {
+ cmd_usage(tp);
+ got = 1;
+ }
+ }
+ }
+ }
+ if (!got) {
+ printf_P(PSTR("Unknown command '%s' - try 'help help'\n"),
+ argv[argi]);
+ res = CMD_RET_FAILURE;
+ break;
+ }
+ }
+ }
+ free(cmd_list);
+ return res;