static void multikeys(void) { assert_int_equal(KEYS_WAIT, execute_keys(L"m")); assert_false(IS_KEYS_RET_CODE(execute_keys(L"ma"))); assert_false(IS_KEYS_RET_CODE(execute_keys(L"mb"))); assert_int_equal(KEYS_UNKNOWN, execute_keys(L"mba")); }
static void cmd_line_tst(void) { counter = 0; assert_false(IS_KEYS_RET_CODE(execute_keys(L"s"))); assert_false(IS_KEYS_RET_CODE(execute_keys(L"q"))); assert_true(counter == 9); }
static void test_noremap_functions(void) { assert_int_equal(0, add_user_keys(L"y", L"k", NORMAL_MODE, 0)); assert_false(IS_KEYS_RET_CODE(execute_keys(L"y"))); assert_true(IS_KEYS_RET_CODE(execute_keys_no_remap(L"y"))); assert_false(IS_KEYS_RET_CODE(execute_keys_timed_out(L"y"))); assert_true(IS_KEYS_RET_CODE(execute_keys_timed_out_no_remap(L"y"))); }
static void test_4dj(void) { assert_int_equal(KEYS_WAIT, execute_keys(L"4")); assert_int_equal(KEYS_WAIT, execute_keys(L"4d")); assert_false(IS_KEYS_RET_CODE(execute_keys(L"4dj"))); }
static void test_d3k(void) { assert_int_equal(KEYS_WAIT, execute_keys(L"d")); assert_int_equal(KEYS_WAIT, execute_keys(L"d3")); assert_false(IS_KEYS_RET_CODE(execute_keys(L"d3k"))); }
static void test_when_previous_known(void) { assert_int_equal(KEYS_WAIT_SHORT, execute_keys(L"q")); assert_int_equal(KEYS_WAIT_SHORT, execute_keys(L"qb")); assert_false(IS_KEYS_RET_CODE(execute_keys(L"qbc"))); }
static void test_max_count_is_handled_correctly(void) { char *const keys = format_str("%udd", INT_MAX); wchar_t *const keysw = to_wide(keys); assert_false(IS_KEYS_RET_CODE(execute_keys(keysw))); assert_int_equal(INT_MAX, last_command_count); free(keysw); free(keys); }
/* Handles the rest of the keys after first one has been determined (in curr). * These can be: <nothing> (empty line), selector, multikey argument. Returns * error code. */ static int execute_next_keys(key_chunk_t *curr, const wchar_t keys[], key_info_t *key_info, keys_info_t *keys_info, int has_duplicate, int no_remap) { const key_conf_t *const conf = &curr->conf; if(*keys == L'\0') { int wait_point = (conf->type == BUILTIN_WAIT_POINT); wait_point = wait_point || (conf->type == USER_CMD && conf->followed != FOLLOWED_BY_NONE); if(wait_point) { const int with_input = (mode_flags[vle_mode_get()] & MF_USES_INPUT); if(!keys_info->after_wait) { return (with_input || has_duplicate) ? KEYS_WAIT_SHORT : KEYS_WAIT; } } else if(conf->data.handler == NULL || conf->followed != FOLLOWED_BY_NONE) { return KEYS_UNKNOWN; } } else if(conf->type != USER_CMD) { int result; if(conf->followed == FOLLOWED_BY_MULTIKEY) { key_info->multi = keys[0]; return dispatch_key(*key_info, keys_info, curr, keys + 1); } keys_info->selector = 1; result = dispatch_keys(keys, keys_info, no_remap, key_info->count); keys_info->selector = 0; if(IS_KEYS_RET_CODE(result)) { return result; } /* We used this count in selector, so don't pass it to command. */ key_info->count = NO_COUNT_GIVEN; } return dispatch_key(*key_info, keys_info, curr, keys); }
static void prevent_stack_overflow(void) { assert_int_equal(0, add_user_keys(L"j", L"j", NORMAL_MODE, 0)); assert_false(IS_KEYS_RET_CODE(execute_keys(L"j"))); assert_int_equal(0, add_user_keys(L"q", L"q", NORMAL_MODE, 0)); assert_int_equal(KEYS_UNKNOWN, execute_keys(L"q")); set_def_handler(NORMAL_MODE, handler); assert_int_equal(0, add_user_keys(L"t", L"toto", NORMAL_MODE, 0)); assert_int_equal(0, execute_keys(L"t")); assert_int_equal(4, counter); set_def_handler(NORMAL_MODE, NULL); }
static void test_nops_count_not_passed(void) { assert_false(IS_KEYS_RET_CODE(execute_keys(L"10abcdd"))); assert_int_equal(NO_COUNT_GIVEN, last_command_count); }
static void test_huge_number_get_intmax(void) { assert_false(IS_KEYS_RET_CODE(execute_keys(L"999999999999dd"))); assert_int_equal(INT_MAX, last_command_count); }
static void test_normal_number_get_it(void) { assert_false(IS_KEYS_RET_CODE(execute_keys(L"123dd"))); assert_int_equal(123, last_command_count); }
static void test_no_number_get_not_def(void) { assert_false(IS_KEYS_RET_CODE(execute_keys(L"dd"))); assert_int_equal(NO_COUNT_GIVEN, last_command_count); }
/* Dispatches keys passed in using a tree of shortcuts registered in the root. * Returns error code. */ static int dispatch_keys_at_root(const wchar_t keys[], keys_info_t *keys_info, key_chunk_t *root, key_info_t key_info, int no_remap) { key_chunk_t *curr; const wchar_t *keys_start = keys; int has_duplicate; int result; /* The loop finds longest match of the input (keys) amoung registered * shortcuts. */ curr = root; while(*keys != L'\0') { key_chunk_t *p; int number_in_the_middle = 0; p = curr->child; while(p != NULL && p->key < *keys) { if(p->conf.type == BUILTIN_NIM_KEYS) { number_in_the_middle = 1; } p = p->next; } if(p == NULL || p->key != *keys) { if(curr == root) return KEYS_UNKNOWN; while(p != NULL) { if(p->conf.type == BUILTIN_NIM_KEYS) { number_in_the_middle = 1; } p = p->next; } if(curr->conf.followed != FOLLOWED_BY_NONE && (!number_in_the_middle || !is_at_count(keys))) { break; } if(number_in_the_middle) { int count; const wchar_t *new_keys = get_count(keys, &count); if(new_keys != keys) { key_info.count = combine_counts(key_info.count, count); keys = new_keys; continue; } } if(curr->conf.type == BUILTIN_WAIT_POINT) { return KEYS_UNKNOWN; } has_duplicate = root == &user_cmds_root[vle_mode_get()] && contains_chain(&builtin_cmds_root[vle_mode_get()], keys_start, keys); result = execute_next_keys(curr, curr->conf.type == USER_CMD ? keys : L"", &key_info, keys_info, has_duplicate, no_remap); if(curr->conf.type == USER_CMD) return result; if(IS_KEYS_RET_CODE(result)) { if(result == KEYS_WAIT_SHORT) return KEYS_UNKNOWN; return result; } inc_counter(keys_info, keys - keys_start); return execute_keys_general(keys, 0, keys_info->mapped, no_remap); } keys++; curr = p; } if(*keys == '\0' && curr->conf.type != BUILTIN_WAIT_POINT && curr->children_count > 0 && curr->conf.data.handler != NULL && !keys_info->after_wait) { return KEYS_WAIT_SHORT; } has_duplicate = root == &user_cmds_root[vle_mode_get()] && contains_chain(&builtin_cmds_root[vle_mode_get()], keys_start, keys); result = execute_next_keys(curr, keys, &key_info, keys_info, has_duplicate, no_remap); if(!IS_KEYS_RET_CODE(result)) { inc_counter(keys_info, keys - keys_start); } else if(*keys == '\0' && result == KEYS_UNKNOWN && curr->children_count > 0) { return keys_info->after_wait ? KEYS_UNKNOWN : KEYS_WAIT_SHORT; } return result; }
/* Dispatches keys passed in as a selector followed by arbitrary other keys. * Returns error code. */ static int dispatch_selector(const wchar_t keys[], keys_info_t *keys_info, key_info_t master_key_info, key_chunk_t *master_curr, int no_remap) { const wchar_t *keys_start = keys; key_chunk_t *curr = &selectors_root[vle_mode_get()]; key_info_t key_info; int result; if(fill_key_info(&keys, &key_info, master_key_info.count, &result) != 0) { return result; } /* The loop finds longest match of the input (keys) among registered * shortcuts. */ while(*keys != L'\0') { key_chunk_t *p; for(p = curr->child; p != NULL && p->key < *keys; p = p->next) { /* Advance. */ } if(p == NULL || p->key != *keys) { break; } ++keys; curr = p; } /* Handle ambiguous selector. */ if(*keys == '\0' && curr->type != BUILTIN_WAIT_POINT && curr->children_count > 0 && curr->conf.data.handler != NULL && !keys_info->after_wait) { return KEYS_WAIT_SHORT; } /* Execute the selector. */ if(curr->conf.followed == FOLLOWED_BY_MULTIKEY && keys[0] != L'\0') { const wchar_t mk[] = { keys[0], L'\0' }; result = execute_next_keys(curr, mk, &key_info, keys_info, 0, no_remap); ++keys; } else { result = keys[0] == L'\0' ? execute_next_keys(curr, L"", &key_info, keys_info, 0, no_remap) : dispatch_key(key_info, keys_info, curr, L""); } if(IS_KEYS_RET_CODE(result)) { return result; } /* We used this count in selector, so don't pass it to command. */ master_key_info.count = NO_COUNT_GIVEN; /* Execute command that requested the selector. */ result = execute_mapping_handler(&master_curr->conf, master_key_info, keys_info); if(IS_KEYS_RET_CODE(result)) { return result; } inc_counter(keys_info, keys - keys_start); /* execute_keys_general() treats empty input as an error. */ if(keys[0] == L'\0') { return 0; } /* Process the rest of the line. */ return execute_keys_general(keys, keys_info->after_wait, 0, no_remap); }