* Subsequent entries are all possible values, followed by a NULL.
* All strings and the array itself are malloc'ed and must be freed
* 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 *);
* by the caller.
*
* \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);
+/*!
+ * \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.
*
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)
{
char *retstr, *prevstr;
size_t which = 0;
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) {
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) {
- if (AST_VECTOR_ADD_SORTED(vec, retstr, strcasecmp)) {
- ast_free(retstr);
+ if (cli_completion_vector_add(vec, retstr)) {
+ ast_threadstorage_set_ptr(&completion_storage, NULL);
goto vector_cleanup;
}
++which;
}
+ ast_threadstorage_set_ptr(&completion_storage, NULL);
+
if (!AST_VECTOR_SIZE(vec)) {
AST_VECTOR_PTR_FREE(vec);
retstr = ast_strndup(AST_VECTOR_GET(vec, 0), max_equal);
if (!retstr || AST_VECTOR_INSERT_AT(vec, 0, retstr)) {
ast_free(retstr);
+
goto vector_cleanup;
}