+
+cmd_tbl_t *get_cmd_tbl_base(cmd_tbl_t *cmdtp)
+{
+ cmd_tbl_t *p = cmdtp;
+
+ while (p->name != NULL)
+ ++p;
+
+ return p->subcmd;
+}
+
+/*
+ * find command table entry for a command
+ */
+typedef struct {
+ size_t len;
+ uint_fast8_t level;
+ bool opt_debug;
+ } find_cmd_para_t;
+
+cmd_tbl_t *_find_cmd (const char *cmd, cmd_tbl_t *table, find_cmd_para_t *para)
+{
+ cmd_tbl_t *cmdtp_ret = NULL;
+ uint_fast8_t n_found = 0, sub_found = 0;
+
+ for (cmd_tbl_t *cmdtp = table; cmdtp->name != NULL; cmdtp++) {
+ if (strncmp_P(cmd, cmdtp->name, para->len) == 0 &&
+ (para->opt_debug || !(cmdtp->flags & CTBL_DBG))) {
+ if (para->len == strlen_P(cmdtp->name))
+ return cmdtp; /* full match */
+
+ cmdtp_ret = cmdtp; /* abbreviated command ? */
+ n_found++;
+ } else if (cmdtp->subcmd) {
+ cmd_tbl_t *sub = _find_cmd(cmd, cmdtp->subcmd, para);
+ if (sub && sub->flags & CTBL_SUBCMDAUTO) {
+ cmdtp_ret = sub;
+ ++n_found;
+ }
+ }
+ }
+ if (n_found == 1) { /* exactly one match */
+ if (sub_found)
+ para->level++;
+ return cmdtp_ret;
+ }
+
+ return NULL; /* not found or ambiguous command */
+}
+
+
+cmd_tbl_t *find_cmd (const char *cmd, cmd_tbl_t *table, uint_fast8_t *cmdlevel)
+{
+ find_cmd_para_t para;
+
+ if (!cmd)
+ return NULL;
+
+ char *optenv = getenv_str(PSTR("cmd"));
+ para.level = 0;
+ para.opt_debug = optenv && strstr_P(optenv, PSTR("debug")) != NULL;
+ para.len = strlen(cmd);
+
+ cmd_tbl_t *cmdtp = _find_cmd(cmd, table, ¶);
+
+ if (cmdlevel)
+ *cmdlevel = para.level;
+ return cmdtp;
+}
+
+cmd_tbl_t *find_cmd_sub(const char *cmd, cmd_tbl_t *table)
+{
+ cmd_tbl_t *entry = NULL;
+
+ for (cmd_tbl_t *tp = get_cmd_tbl_base(table); tp->name && entry == NULL; tp++)
+ if (tp->subcmd && tp->flags & CTBL_SUBCMDAUTO)
+ entry = find_cmd(cmd, tp->subcmd, NULL);
+
+ return entry;
+}
+