CLI: Create ast_cli_completion_add function.

Some completion generators are very inefficent due to the way CLI
requests matches one at a time.  ast_cli_completion_add can be called
multiple times during one invokation of a CLI generator to add all
results without having to reinitialize the search state for each match.

Change-Id: I73d26d270bbbe1e3e6390799cfc1b639e39cceec
This commit is contained in:
Corey Farrell
2017-11-09 00:42:10 -05:00
parent a02cbc2ef3
commit 9587a61f4c
2 changed files with 76 additions and 2 deletions

View File

@@ -305,6 +305,9 @@ int ast_cli_generatornummatches(const char *, const char *);
* Subsequent entries are all possible values, followed by a NULL. * Subsequent entries are all possible values, followed by a NULL.
* All strings and the array itself are malloc'ed and must be freed * All strings and the array itself are malloc'ed and must be freed
* by the caller. * by the caller.
*
* \warning This function cannot be called recursively so it will always
* fail if called from a CLI_GENERATE callback.
*/ */
char **ast_cli_completion_matches(const char *, const char *); char **ast_cli_completion_matches(const char *, const char *);
@@ -326,9 +329,29 @@ char **ast_cli_completion_matches(const char *, const char *);
* by the caller. * by the caller.
* *
* \note The vector is sorted and does not contain any duplicates. * \note The vector is sorted and does not contain any duplicates.
*
* \warning This function cannot be called recursively so it will always
* fail if called from a CLI_GENERATE callback.
*/ */
struct ast_vector_string *ast_cli_completion_vector(const char *text, const char *word); struct ast_vector_string *ast_cli_completion_vector(const char *text, const char *word);
/*!
* \brief Add a result to a request for completion options.
*
* \param value A completion option text.
*
* \retval 0 Success
* \retval -1 Failure
*
* This is an alternative to returning individual values from CLI_GENERATE. Instead
* of repeatedly being asked for the next match and having to start over, you can
* call this function repeatedly from your own stateful loop. When all matches have
* been added you can return NULL from the CLI_GENERATE function.
*
* \note This function always eventually results in calling ast_free on \a value.
*/
int ast_cli_completion_add(char *value);
/*! /*!
* \brief Command completion for the list of active channels. * \brief Command completion for the list of active channels.
* *

View File

@@ -2512,6 +2512,44 @@ char **ast_cli_completion_matches(const char *text, const char *word)
return match_list; return match_list;
} }
AST_THREADSTORAGE_RAW(completion_storage);
/*!
* \internal
* \brief Add a value to the vector.
*
* \param vec Vector to add \a value to. Must be from threadstorage.
* \param value The value to add.
*
* \retval 0 Success
* \retval -1 Failure
*/
static int cli_completion_vector_add(struct ast_vector_string *vec, char *value)
{
if (!value) {
return 0;
}
if (!vec || AST_VECTOR_ADD_SORTED(vec, value, strcasecmp)) {
if (vec) {
ast_threadstorage_set_ptr(&completion_storage, NULL);
AST_VECTOR_CALLBACK_VOID(vec, ast_free);
AST_VECTOR_FREE(vec);
}
ast_free(value);
return -1;
}
return 0;
}
int ast_cli_completion_add(char *value)
{
return cli_completion_vector_add(ast_threadstorage_get_ptr(&completion_storage), value);
}
struct ast_vector_string *ast_cli_completion_vector(const char *text, const char *word) struct ast_vector_string *ast_cli_completion_vector(const char *text, const char *word)
{ {
char *retstr, *prevstr; char *retstr, *prevstr;
@@ -2519,13 +2557,23 @@ struct ast_vector_string *ast_cli_completion_vector(const char *text, const char
size_t which = 0; size_t which = 0;
struct ast_vector_string *vec = ast_calloc(1, sizeof(*vec)); struct ast_vector_string *vec = ast_calloc(1, sizeof(*vec));
/* Recursion into this function is a coding error. */
ast_assert(!ast_threadstorage_get_ptr(&completion_storage));
if (!vec) { if (!vec) {
return NULL; return NULL;
} }
if (ast_threadstorage_set_ptr(&completion_storage, vec)) {
ast_log(LOG_ERROR, "Failed to initialize threadstorage for completion.\n");
ast_free(vec);
return NULL;
}
while ((retstr = ast_cli_generator(text, word, which)) != NULL) { while ((retstr = ast_cli_generator(text, word, which)) != NULL) {
if (AST_VECTOR_ADD_SORTED(vec, retstr, strcasecmp)) { if (cli_completion_vector_add(vec, retstr)) {
ast_free(retstr); ast_threadstorage_set_ptr(&completion_storage, NULL);
goto vector_cleanup; goto vector_cleanup;
} }
@@ -2533,6 +2581,8 @@ struct ast_vector_string *ast_cli_completion_vector(const char *text, const char
++which; ++which;
} }
ast_threadstorage_set_ptr(&completion_storage, NULL);
if (!AST_VECTOR_SIZE(vec)) { if (!AST_VECTOR_SIZE(vec)) {
AST_VECTOR_PTR_FREE(vec); AST_VECTOR_PTR_FREE(vec);
@@ -2571,6 +2621,7 @@ struct ast_vector_string *ast_cli_completion_vector(const char *text, const char
retstr = ast_strndup(AST_VECTOR_GET(vec, 0), max_equal); retstr = ast_strndup(AST_VECTOR_GET(vec, 0), max_equal);
if (!retstr || AST_VECTOR_INSERT_AT(vec, 0, retstr)) { if (!retstr || AST_VECTOR_INSERT_AT(vec, 0, retstr)) {
ast_free(retstr); ast_free(retstr);
goto vector_cleanup; goto vector_cleanup;
} }