int cmdline_complete(struct cmdline *cl, const char *buf, int *state, char *dst, unsigned int size) { const char *partial_tok = buf; unsigned int inst_num = 0; cmdline_parse_inst_t *inst; cmdline_parse_token_hdr_t *token_p; struct cmdline_token_hdr token_hdr; char tmpbuf[CMDLINE_BUFFER_SIZE], comp_buf[CMDLINE_BUFFER_SIZE]; unsigned int partial_tok_len; int comp_len = -1; int tmp_len = -1; int nb_token = 0; unsigned int i, n; int l; unsigned int nb_completable; unsigned int nb_non_completable; int local_state = 0; const char *help_str; cmdline_parse_ctx_t *ctx; if (!cl || !buf || !state || !dst) return -1; ctx = cl->ctx; debug_printf("%s called\n", __func__); memset(&token_hdr, 0, sizeof(token_hdr)); /* count the number of complete token to parse */ for (i=0 ; buf[i] ; i++) { if (!isblank2(buf[i]) && isblank2(buf[i+1])) nb_token++; if (isblank2(buf[i]) && !isblank2(buf[i+1])) partial_tok = buf+i+1; } partial_tok_len = strnlen(partial_tok, RDLINE_BUF_SIZE); /* first call -> do a first pass */ if (*state <= 0) { debug_printf("try complete <%s>\n", buf); debug_printf("there is %d complete tokens, <%s> is incomplete\n", nb_token, partial_tok); nb_completable = 0; nb_non_completable = 0; inst = ctx[inst_num]; while (inst) { /* parse the first tokens of the inst */ if (nb_token && match_inst(inst, buf, nb_token, NULL, 0)) goto next; debug_printf("instruction match\n"); token_p = get_token(inst, nb_token); if (token_p) memcpy(&token_hdr, token_p, sizeof(token_hdr)); /* non completable */ if (!token_p || !token_hdr.ops->complete_get_nb || !token_hdr.ops->complete_get_elt || (n = token_hdr.ops->complete_get_nb(token_p)) == 0) { nb_non_completable++; goto next; } debug_printf("%d choices for this token\n", n); for (i=0 ; i<n ; i++) { if (token_hdr.ops->complete_get_elt(token_p, i, tmpbuf, sizeof(tmpbuf)) < 0) continue; /* we have at least room for one char */ tmp_len = strnlen(tmpbuf, sizeof(tmpbuf)); if (tmp_len < CMDLINE_BUFFER_SIZE - 1) { tmpbuf[tmp_len] = ' '; tmpbuf[tmp_len+1] = 0; } debug_printf(" choice <%s>\n", tmpbuf); /* does the completion match the * beginning of the word ? */ if (!strncmp(partial_tok, tmpbuf, partial_tok_len)) { if (comp_len == -1) { snprintf(comp_buf, sizeof(comp_buf), "%s", tmpbuf + partial_tok_len); comp_len = strnlen(tmpbuf + partial_tok_len, sizeof(tmpbuf) - partial_tok_len); } else { comp_len = nb_common_chars(comp_buf, tmpbuf+partial_tok_len); comp_buf[comp_len] = 0; } nb_completable++; } } next: debug_printf("next\n"); inst_num ++; inst = ctx[inst_num]; } debug_printf("total choices %d for this completion\n", nb_completable); /* no possible completion */ if (nb_completable == 0 && nb_non_completable == 0) return 0; /* if multichoice is not required */ if (*state == 0 && partial_tok_len > 0) { /* one or several choices starting with the same chars */ if (comp_len > 0) { if ((unsigned)(comp_len + 1) > size) return 0; snprintf(dst, size, "%s", comp_buf); dst[comp_len] = 0; return 2; } } } /* init state correctly */ if (*state == -1) *state = 0; debug_printf("Multiple choice STATE=%d\n", *state); inst_num = 0; inst = ctx[inst_num]; while (inst) { /* we need to redo it */ inst = ctx[inst_num]; if (nb_token && match_inst(inst, buf, nb_token, NULL, 0)) goto next2; token_p = get_token(inst, nb_token); if (token_p) memcpy(&token_hdr, token_p, sizeof(token_hdr)); /* one choice for this token */ if (!token_p || !token_hdr.ops->complete_get_nb || !token_hdr.ops->complete_get_elt || (n = token_hdr.ops->complete_get_nb(token_p)) == 0) { if (local_state < *state) { local_state++; goto next2; } (*state)++; if (token_p && token_hdr.ops->get_help) { token_hdr.ops->get_help(token_p, tmpbuf, sizeof(tmpbuf)); help_str = inst->help_str; if (help_str) snprintf(dst, size, "[%s]: %s", tmpbuf, help_str); else snprintf(dst, size, "[%s]: No help", tmpbuf); } else { snprintf(dst, size, "[RETURN]"); } return 1; } /* several choices */ for (i=0 ; i<n ; i++) { if (token_hdr.ops->complete_get_elt(token_p, i, tmpbuf, sizeof(tmpbuf)) < 0) continue; /* we have at least room for one char */ tmp_len = strnlen(tmpbuf, sizeof(tmpbuf)); if (tmp_len < CMDLINE_BUFFER_SIZE - 1) { tmpbuf[tmp_len] = ' '; tmpbuf[tmp_len + 1] = 0; } debug_printf(" choice <%s>\n", tmpbuf); /* does the completion match the beginning of * the word ? */ if (!strncmp(partial_tok, tmpbuf, partial_tok_len)) { if (local_state < *state) { local_state++; continue; } (*state)++; l=snprintf(dst, size, "%s", tmpbuf); if (l>=0 && token_hdr.ops->get_help) { token_hdr.ops->get_help(token_p, tmpbuf, sizeof(tmpbuf)); help_str = inst->help_str; if (help_str) snprintf(dst+l, size-l, "[%s]: %s", tmpbuf, help_str); else snprintf(dst+l, size-l, "[%s]: No help", tmpbuf); } return 1; } } next2: inst_num ++; inst = ctx[inst_num]; } return 0; }
int8_t complete(parse_pgm_ctx_t ctx[], const char *buf, int16_t *state, char *dst, uint8_t size) { const char * incomplete_token = buf; uint8_t inst_num = 0; parse_pgm_inst_t *inst; parse_pgm_token_hdr_t *token_p; struct token_hdr token_hdr; char tmpbuf[64], completion_buf[64]; uint8_t incomplete_token_len; int8_t completion_len = -1; int8_t nb_token = 0; uint8_t i, n; int8_t l; uint8_t nb_completable; uint8_t nb_non_completable; uint16_t local_state=0; prog_char *help_str; debug_printf("%s called\n", __FUNCTION__); /* count the number of complete token to parse */ for (i=0 ; buf[i] ; i++) { if (!isblank(buf[i]) && isblank(buf[i+1])) nb_token++; if (isblank(buf[i]) && !isblank(buf[i+1])) incomplete_token = buf+i+1; } incomplete_token_len = strlen(incomplete_token); /* first call -> do a first pass */ if (*state <= 0) { debug_printf("try complete <%s>\n", buf); debug_printf("there is %d complete tokens, <%s> is incomplete\n", nb_token, incomplete_token); nb_completable = 0; nb_non_completable = 0; inst = (parse_pgm_inst_t *)pgm_read_word(ctx+inst_num); while (inst) { /* parse the first tokens of the inst */ if (nb_token && match_inst(inst, buf, nb_token, NULL)) goto next; debug_printf("instruction match \n"); token_p = (parse_pgm_token_hdr_t *) pgm_read_word(&inst->tokens[nb_token]); if (token_p) memcpy_P(&token_hdr, token_p, sizeof(token_hdr)); /* non completable */ if (!token_p || !token_hdr.ops->complete_get_nb || !token_hdr.ops->complete_get_elt || (n = token_hdr.ops->complete_get_nb(token_p)) == 0) { nb_non_completable++; goto next; } debug_printf("%d choices for this token\n", n); for (i=0 ; i<n ; i++) { if (token_hdr.ops->complete_get_elt(token_p, i, tmpbuf, sizeof(tmpbuf)) < 0) continue; strcat_P(tmpbuf, PSTR(" ")); /* we have at least room for one char */ debug_printf(" choice <%s>\n", tmpbuf); /* does the completion match the beginning of the word ? */ if (!strncmp(incomplete_token, tmpbuf, incomplete_token_len)) { if (completion_len == -1) { strcpy(completion_buf, tmpbuf+incomplete_token_len); completion_len = strlen(tmpbuf+incomplete_token_len); } else { completion_len = nb_common_chars(completion_buf, tmpbuf+incomplete_token_len); completion_buf[completion_len] = 0; } nb_completable++; } } next: inst_num ++; inst = (parse_pgm_inst_t *)pgm_read_word(ctx+inst_num); } debug_printf("total choices %d for this completion\n", nb_completable); /* no possible completion */ if (nb_completable == 0 && nb_non_completable == 0) return 0; /* if multichoice is not required */ if (*state == 0 && incomplete_token_len > 0) { /* one or several choices starting with the same chars */ if (completion_len > 0) { if (completion_len + 1 > size) return 0; strcpy(dst, completion_buf); return 2; } } } /* init state correctly */ if (*state == -1) *state = 0; debug_printf("Multiple choice STATE=%d\n", *state); inst_num = 0; inst = (parse_pgm_inst_t *)pgm_read_word(ctx+inst_num); while (inst) { /* we need to redo it */ inst = (parse_pgm_inst_t *)pgm_read_word(ctx+inst_num); if (nb_token && match_inst(inst, buf, nb_token, NULL)) goto next2; token_p = (parse_pgm_token_hdr_t *)pgm_read_word(&inst->tokens[nb_token]); if (token_p) memcpy_P(&token_hdr, token_p, sizeof(token_hdr)); /* one choice for this token */ if (!token_p || !token_hdr.ops->complete_get_nb || !token_hdr.ops->complete_get_elt || (n = token_hdr.ops->complete_get_nb(token_p)) == 0) { if (local_state < *state) { local_state++; goto next2; } (*state)++; if (token_p && token_hdr.ops->get_help) { token_hdr.ops->get_help(token_p, tmpbuf, sizeof(tmpbuf)); help_str = (prog_char *) pgm_read_word(&inst->help_str); if (help_str) snprintf_P(dst, size, PSTR("[%s]: %S"), tmpbuf, help_str); else snprintf_P(dst, size, PSTR("[%s]: No help"), tmpbuf); } else { snprintf_P(dst, size, PSTR("[RETURN]")); } return 1; } /* several choices */ for (i=0 ; i<n ; i++) { if (token_hdr.ops->complete_get_elt(token_p, i, tmpbuf, sizeof(tmpbuf)) < 0) continue; strcat_P(tmpbuf, PSTR(" ")); /* we have at least room for one char */ debug_printf(" choice <%s>\n", tmpbuf); /* does the completion match the beginning of the word ? */ if (!strncmp(incomplete_token, tmpbuf, incomplete_token_len)) { if (local_state < *state) { local_state++; continue; } (*state)++; l=snprintf(dst, size, "%s", tmpbuf); if (l>=0 && token_hdr.ops->get_help) { token_hdr.ops->get_help(token_p, tmpbuf, sizeof(tmpbuf)); help_str = (prog_char *) pgm_read_word(&inst->help_str); if (help_str) snprintf_P(dst+l, size-l, PSTR("[%s]: %S"), tmpbuf, help_str); else snprintf_P(dst+l, size-l, PSTR("[%s]: No help"), tmpbuf); } return 1; } } next2: inst_num ++; inst = (parse_pgm_inst_t *)pgm_read_word(ctx+inst_num); } return 0; }