Newer
Older
continue;
/* if we get here, both words exist but there is a mismatch */
if (match_type == 0) /* not the one we look for */
continue;
if (match_type == 1) /* not the one we look for */
continue;
if (cmds[y+1] != NULL || e->cmda[y+1] != NULL) /* not the one we look for */
continue;
/* we are in case match_type == -1 and mismatch on last word */
}
if (cand == NULL || y > matchlen) /* remember the candidate */
cand = e;
return e ? e : cand;
static char *find_best(char *argv[])
{
static char cmdline[80];
int x;
/* See how close we get, then print the candidate */
char *myargv[AST_MAX_CMD_LEN];
for (x=0;x<AST_MAX_CMD_LEN;x++)
myargv[x]=NULL;
AST_LIST_LOCK(&helpers);
for (x=0;argv[x];x++) {
myargv[x] = argv[x];
if (!find_cli(myargv, -1))
break;
}
AST_LIST_UNLOCK(&helpers);
ast_join(cmdline, sizeof(cmdline), myargv);
int ast_cli_unregister(struct ast_cli_entry *e)
{
Kevin P. Fleming
committed
if (e->inuse) {
ast_log(LOG_WARNING, "Can't remove command that is in use\n");
} else {
AST_LIST_LOCK(&helpers);
AST_LIST_REMOVE(&helpers, e, list);
AST_LIST_UNLOCK(&helpers);
int ast_cli_register(struct ast_cli_entry *e)
{
Kevin P. Fleming
committed
struct ast_cli_entry *cur;
char fulle[80] ="";
int lf, ret = -1;
ast_join(fulle, sizeof(fulle), e->cmda);
Kevin P. Fleming
committed
AST_LIST_LOCK(&helpers);
if (find_cli(e->cmda, 1)) {
Kevin P. Fleming
committed
AST_LIST_UNLOCK(&helpers);
ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", fulle);
e->_full_cmd = ast_strdup(fulle);
if (!e->_full_cmd)
goto done;
lf = strlen(fulle);
Kevin P. Fleming
committed
AST_LIST_TRAVERSE_SAFE_BEGIN(&helpers, cur, list) {
int len = strlen(cur->_full_cmd);
if (lf < len)
len = lf;
if (strncasecmp(fulle, cur->_full_cmd, len) < 0) {
Kevin P. Fleming
committed
AST_LIST_INSERT_BEFORE_CURRENT(&helpers, e, list);
Kevin P. Fleming
committed
AST_LIST_TRAVERSE_SAFE_END;
Kevin P. Fleming
committed
if (!cur)
AST_LIST_INSERT_TAIL(&helpers, e, list);
ret = 0; /* success */
Kevin P. Fleming
committed
AST_LIST_UNLOCK(&helpers);
Kevin P. Fleming
committed
/*
* register/unregister an array of entries.
*/
void ast_cli_register_multiple(struct ast_cli_entry *e, int len)
{
int i;
for (i = 0; i < len; i++)
Kevin P. Fleming
committed
ast_cli_register(e + i);
}
void ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
{
int i;
for (i = 0; i < len; i++)
Kevin P. Fleming
committed
ast_cli_unregister(e + i);
}
/*! \brief helper for help_workhorse and final part of
* handle_help. if locked = 0 it's just help_workhorse,
* otherwise assume the list is already locked and print
* an error message if not found.
*/
static int help1(int fd, char *match[], int locked)
char matchstr[80] = "";
struct ast_cli_entry *e;
int len = 0;
int found = 0;
struct cli_iterator i = { NULL, NULL};
if (match) {
ast_join(matchstr, sizeof(matchstr), match);
len = strlen(matchstr);
}
if (!locked)
AST_LIST_LOCK(&helpers);
while ( (e = cli_next(&i)) ) {
if (e->_full_cmd[0] == '_')
if (match && strncasecmp(matchstr, e->_full_cmd, len))
continue;
ast_cli(fd, "%25.25s %s\n", e->_full_cmd, e->summary);
found++;
AST_LIST_UNLOCK(&helpers);
if (!locked && !found && matchstr[0])
ast_cli(fd, "No such command '%s'.\n", matchstr);
static int help_workhorse(int fd, char *match[])
{
return help1(fd, match, 0 /* do not print errors */);
}
static int handle_help(int fd, int argc, char *argv[])
{
struct ast_cli_entry *e;
if (argc < 1)
AST_LIST_LOCK(&helpers);
e = find_cli(argv + 1, 1); /* try exact match first */
if (!e)
return help1(fd, argv + 1, 1 /* locked */);
if (e->usage)
ast_cli(fd, "%s", e->usage);
else {
ast_join(fullcmd, sizeof(fullcmd), argv+1);
ast_cli(fd, "No help text available for '%s'.\n", fullcmd);
AST_LIST_UNLOCK(&helpers);
Russell Bryant
committed
static char *parse_args(const char *s, int *argc, char *argv[], int max, int *trailingwhitespace)
int x = 0;
int quoted = 0;
int escaped = 0;
int whitespace = 1;
if (s == NULL) /* invalid, though! */
return NULL;
/* make a copy to store the parsed string */
if (!(dup = strdup(s)))
return NULL;
cur = dup;
/* scan the original string copying into cur when needed */
for (; *s ; s++) {
if (x >= max - 1) {
ast_log(LOG_WARNING, "Too many arguments, truncating at %s\n", s);
break;
}
if (*s == '"' && !escaped) {
if (quoted && whitespace) {
/* start a quoted string from previous whitespace: new argument */
argv[x++] = cur;
whitespace = 0;
}
} else if ((*s == ' ' || *s == '\t') && !(quoted || escaped)) {
/* If we are not already in whitespace, and not in a quoted string or
processing an escape sequence, and just entered whitespace, then
finalize the previous argument and remember that we are in whitespace
*/
if (!whitespace) {
} else if (*s == '\\' && !escaped) {
escaped = 1;
} else {
if (whitespace) {
/* we leave whitespace, and are not quoted. So it's a new argument */
argv[x++] = cur;
whitespace = 0;
*cur++ = '\0';
/* XXX put a NULL in the last argument, because some functions that take
* the array may want a null-terminated array.
* argc still reflects the number of non-NULL entries.
*/
argv[x] = NULL;
*argc = x;
/*! \brief Return the number of unique matches for the generator */
Russell Bryant
committed
int ast_cli_generatornummatches(const char *text, const char *word)
while ((buf = ast_cli_generator(text, word, i++))) {
if (!oldbuf || strcmp(buf,oldbuf))
matches++;
if (oldbuf)
free(oldbuf);
Russell Bryant
committed
char **ast_cli_completion_matches(const char *text, const char *word)
{
char **match_list = NULL, *retstr, *prevstr;
size_t match_list_len, max_equal, which, i;
int matches = 0;
/* leave entry 0 free for the longest common substring */
match_list_len = 1;
while ((retstr = ast_cli_generator(text, word, matches)) != NULL) {
if (matches + 1 >= match_list_len) {
match_list_len <<= 1;
Russell Bryant
committed
if (!(match_list = ast_realloc(match_list, match_list_len * sizeof(*match_list))))
return NULL;
}
match_list[++matches] = retstr;
}
if (!match_list)
return match_list; /* NULL */
/* Find the longest substring that is common to all results
* (it is a candidate for completion), and store a copy in entry 0.
*/
prevstr = match_list[1];
max_equal = strlen(prevstr);
for (which = 2; which <= matches; which++) {
for (i = 0; i < max_equal && toupper(prevstr[i]) == toupper(match_list[which][i]); i++)
Russell Bryant
committed
if (!(retstr = ast_malloc(max_equal + 1)))
return NULL;
strncpy(retstr, match_list[1], max_equal);
retstr[max_equal] = '\0';
match_list[0] = retstr;
/* ensure that the array is NULL terminated */
Russell Bryant
committed
if (matches + 1 >= match_list_len) {
if (!(match_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(*match_list))))
return NULL;
}
match_list[matches + 1] = NULL;
return match_list;
Russell Bryant
committed
static char *__ast_cli_generator(const char *text, const char *word, int state, int lock)
struct ast_cli_entry *e;
struct cli_iterator i = { NULL, NULL };
int x = 0, argindex, matchlen;
Joshua Colp
committed
int tws = 0;
char *dup = parse_args(text, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws);
if (!dup) /* error */
return NULL;
argindex = (!ast_strlen_zero(word) && x>0) ? x-1 : x;
/* rebuild the command, ignore tws */
ast_join(matchstr, sizeof(matchstr)-1, argv);
matchlen = strlen(matchstr);
Joshua Colp
committed
if (tws) {
strcat(matchstr, " "); /* XXX */
if (matchlen)
matchlen++;
}
if (lock)
AST_LIST_LOCK(&helpers);
while( !ret && (e = cli_next(&i)) ) {
int lc = strlen(e->_full_cmd);
if (e->_full_cmd[0] != '_' && lc > 0 && matchlen <= lc &&
!strncasecmp(matchstr, e->_full_cmd, matchlen)) {
/* Found initial part, return a copy of the next word... */
if (e->cmda[argindex] && ++matchnum > state)
ret = strdup(e->cmda[argindex]); /* we need a malloced string */
} else if (e->generator && !strncasecmp(matchstr, e->_full_cmd, lc) && matchstr[lc] < 33) {
/* We have a command in its entirity within us -- theoretically only one
command can have this occur */
ret = e->generator(matchstr, word, argindex, state);
if (lock)
AST_LIST_UNLOCK(&helpers);
free(dup);
return ret;
Russell Bryant
committed
char *ast_cli_generator(const char *text, const char *word, int state)
{
return __ast_cli_generator(text, word, state, 1);
}
Russell Bryant
committed
int ast_cli_command(int fd, const char *s)
{
char *argv[AST_MAX_ARGS];
struct ast_cli_entry *e;
int x;
char *dup;
Russell Bryant
committed
if (!(dup = parse_args(s, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws))) {
ast_log(LOG_ERROR, "Memory allocation failure\n");
return -1;
}
/* We need at least one entry, or ignore */
if (x > 0) {
Kevin P. Fleming
committed
AST_LIST_LOCK(&helpers);
e = find_cli(argv, 0);
if (e)
e->inuse++;
Kevin P. Fleming
committed
AST_LIST_UNLOCK(&helpers);
if (e) {
switch(e->handler(fd, x, argv)) {
case RESULT_SHOWUSAGE:
Russell Bryant
committed
if (e->usage)
ast_cli(fd, "%s", e->usage);
else
ast_cli(fd, "Invalid usage, but no usage information available.\n");
break;
}
} else
ast_cli(fd, "No such command '%s' (type 'help' for help)\n", find_best(argv));
if (e) {
Kevin P. Fleming
committed
AST_LIST_LOCK(&helpers);
e->inuse--; /* XXX here an atomic dec would suffice */
Kevin P. Fleming
committed
AST_LIST_UNLOCK(&helpers);
free(dup);