/* Reads count of the command. Sets *count to NO_COUNT_GIVEN if there is no * count in the current position. Returns pointer to a character right next to * the count. */ static const wchar_t * get_count(const wchar_t keys[], int *count) { if(is_at_count(keys)) { wchar_t *ptr; *count = wcstol(keys, &ptr, 10); /* Handle overflow correctly. */ if(*count <= 0) { *count = INT_MAX; } keys = ptr; } else { *count = NO_COUNT_GIVEN; } return keys; }
/* 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; }
/* Looks up possible continuations of keys for the given root and calls cb on * them. */ static void keys_suggest(const key_chunk_t *root, const wchar_t keys[], const wchar_t prefix[], vle_keys_list_cb cb, int custom_only, int fold_subkeys) { const key_chunk_t *curr = root; while(*keys != L'\0') { const key_chunk_t *p; int number_in_the_middle = 0; /* Look up current key among children of current node (might be root), while * inspecting NIM as well. */ for(p = curr->child; p != NULL && p->key < *keys; p = p->next) { if(p->type == BUILTIN_NIM_KEYS) { number_in_the_middle = 1; } } /* Go to the next character if a match found. */ if(p != NULL && p->key == *keys) { ++keys; curr = p; continue; } /* No match for the first character is fatal for the lookup. */ if(curr == root) { return; } /* Need to inspect all children for NIM. */ for(; p != NULL && !number_in_the_middle; p = p->next) { if(p->type == BUILTIN_NIM_KEYS) { number_in_the_middle = 1; } } /* Give up if this isn't one of cases where next character is not presented * in the tree by design. */ if(curr->conf.followed != FOLLOWED_BY_NONE && (!number_in_the_middle || !is_at_count(keys))) { break; } /* Skip over number in the middle, if any. */ if(number_in_the_middle) { int count; const wchar_t *new_keys = get_count(keys, &count); if(new_keys != keys) { keys = new_keys; continue; } } break; } if(!custom_only && *keys == L'\0') { suggest_children(curr, prefix, cb, fold_subkeys); } if(curr->type == BUILTIN_WAIT_POINT) { if(curr->conf.followed == FOLLOWED_BY_SELECTOR) { /* Suggest selectors. */ keys_suggest(&selectors_root[vle_mode_get()], keys, L"sel: ", cb, custom_only, fold_subkeys); } else if(curr->conf.followed == FOLLOWED_BY_MULTIKEY) { /* Invoke optional external function to provide suggestions. */ if(curr->conf.suggest != NULL) { curr->conf.suggest(cb); } } } }