Skip to content
Snippets Groups Projects
cli.c 48.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	struct ast_channel *c = NULL;
    	int numchans = 0;
    	struct ast_var_t *current;
    	struct varshead *headp;
    	regex_t regexbuf;
    	int havepattern = 0;
    
    	if (argc < 3 || argc > 4)
    		return RESULT_SHOWUSAGE;
    	
    	if (argc == 4) {
    		if (regcomp(&regexbuf, argv[3], REG_EXTENDED | REG_NOSUB))
    			return RESULT_SHOWUSAGE;
    		havepattern = 1;
    	}
    
    	ast_cli(fd, FORMAT_STRING, "Channel", "Group", "Category");
    	while ( (c = ast_channel_walk_locked(c)) != NULL) {
    		headp=&c->varshead;
    		AST_LIST_TRAVERSE(headp,current,entries) {
    			if (!strncmp(ast_var_name(current), GROUP_CATEGORY_PREFIX "_", strlen(GROUP_CATEGORY_PREFIX) + 1)) {
    				if (!havepattern || !regexec(&regexbuf, ast_var_value(current), 0, NULL, 0)) {
    					ast_cli(fd, FORMAT_STRING, c->name, ast_var_value(current),
    						(ast_var_name(current) + strlen(GROUP_CATEGORY_PREFIX) + 1));
    					numchans++;
    				}
    			} else if (!strcmp(ast_var_name(current), GROUP_CATEGORY_PREFIX)) {
    				if (!havepattern || !regexec(&regexbuf, ast_var_value(current), 0, NULL, 0)) {
    					ast_cli(fd, FORMAT_STRING, c->name, ast_var_value(current), "(default)");
    					numchans++;
    				}
    			}
    		}
    		numchans++;
    
    	}
    
    	if (havepattern)
    		regfree(&regexbuf);
    
    	ast_cli(fd, "%d active channel%s\n", numchans, (numchans != 1) ? "s" : "");
    	return RESULT_SUCCESS;
    #undef FORMAT_STRING
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int handle_help(int fd, int argc, char *argv[]);
    
    
    static char * complete_help(const char *text, const char *word, int pos, int state)
    
    {
    	/* skip first 4 or 5 chars, "help "*/
    	int l = strlen(text);
    
    	if (l > 5)
    		l = 5;
    	text += l;
    	/* XXX watch out, should stop to the non-generator parts */
    
    	return __ast_cli_generator(text, word, state, 0);
    
    /* XXX Nothing in this array can currently be deprecated...
       You have to change the way find_cli works in order to remove this array
       I recommend doing this eventually...
     */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static struct ast_cli_entry builtins[] = {
    
    James Golovich's avatar
    James Golovich committed
    	/* Keep alphabetized, with longer matches first (example: abcd before abc) */
    
    	{ { "_command", "complete", NULL },
    	handle_commandcomplete, "Command complete",
    	commandcomplete_help },
    
    	{ { "_command", "nummatches", NULL },
    	handle_commandnummatches, "Returns number of command matches",
    	commandnummatches_help },
    
    	{ { "_command", "matchesarray", NULL },
    	handle_commandmatchesarray, "Returns command matches array",
    	commandmatchesarray_help },
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	{ { NULL }, NULL, NULL, NULL }
    };
    
    
    static struct ast_cli_entry cli_debug_channel_deprecated = {
    	{ "debug", "channel", NULL },
    	handle_debugchan_deprecated, NULL,
    	NULL, complete_ch_3 };
    
    static struct ast_cli_entry cli_module_load_deprecated = {
    	{ "load", NULL },
    	handle_load_deprecated, NULL,
    	NULL, complete_fn };
    
    static struct ast_cli_entry cli_module_reload_deprecated = {
    	{ "reload", NULL },
    	handle_reload_deprecated, NULL,
    	NULL, complete_mod_2 };
    
    static struct ast_cli_entry cli_module_unload_deprecated = {
    	{ "unload", NULL },
    	handle_unload_deprecated, NULL,
    	NULL, complete_mod_2 };
    
    
    static struct ast_cli_entry cli_cli[] = {
    
    	/* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */
    	{ { "no", "debug", "channel", NULL },
    	handle_nodebugchan_deprecated, NULL,
    	NULL, complete_ch_4 },
    
    
    	{ { "core", "show", "channels", NULL },
    
    	handle_chanlist, "Display information on channels",
    
    	chanlist_help, complete_show_channels },
    
    	{ { "core", "show", "channel", NULL },
    
    	handle_showchan, "Display information on a specific channel",
    
    	showchan_help, complete_ch_4 },
    
    	{ { "core", "set", "debug", "channel", NULL },
    	handle_core_set_debug_channel, "Enable/disable debugging on a channel",
    	debugchan_help, complete_ch_5, &cli_debug_channel_deprecated },
    
    	NEW_CLI(handle_set_debug, "Set level of debug chattiness"),
    
    	NEW_CLI(handle_verbose, "Set level of verboseness"),
    
    	{ { "group", "show", "channels", NULL },
    
    	group_show_channels, "Display active channels with group(s)",
    
    	group_show_channels_help },
    
    
    	{ { "help", NULL },
    	handle_help, "Display help list, or specific help on a command",
    	help_help, complete_help },
    
    	{ { "logger", "mute", NULL },
    	handle_logger_mute, "Toggle logging output to a console",
    	logger_mute_help },
    
    
    	NEW_CLI(handle_modlist, "List modules and info"),
    
    	{ { "module", "load", NULL },
    
    	handle_load, "Load a module by name",
    
    	load_help, complete_fn, &cli_module_load_deprecated },
    
    	{ { "module", "reload", NULL },
    
    	handle_reload, "Reload configuration",
    
    	reload_help, complete_mod_3, &cli_module_reload_deprecated },
    
    	{ { "module", "unload", NULL },
    
    	handle_unload, "Unload a module by name",
    
    	unload_help, complete_mod_3_nr, &cli_module_unload_deprecated },
    
    	NEW_CLI(handle_showuptime, "Show uptime information"),
    
    
    	{ { "soft", "hangup", NULL },
    	handle_softhangup, "Request a hangup on a given channel",
    	softhangup_help, complete_ch_3 },
    };
    
    
    /*! \brief initialize the _full_cmd string in * each of the builtins. */
    void ast_builtins_init(void)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	struct ast_cli_entry *e;
    
    	for (e = builtins; e->cmda[0] != NULL; e++) {
    		char buf[80];
    		ast_join(buf, sizeof(buf), e->cmda);
    		e->_full_cmd = strdup(buf);
    		if (!e->_full_cmd)
    			ast_log(LOG_WARNING, "-- cannot allocate <%s>\n", buf);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    
    	ast_cli_register_multiple(cli_cli, sizeof(cli_cli) / sizeof(struct ast_cli_entry));
    
    /*
     * We have two sets of commands: builtins are stored in a
     * NULL-terminated array of ast_cli_entry, whereas external
     * commands are in a list.
     * When navigating, we need to keep two pointers and get
     * the next one in lexicographic order. For the purpose,
     * we use a structure.
     */
    
    struct cli_iterator {
    	struct ast_cli_entry *builtins;
    	struct ast_cli_entry *helpers;
    };
    
    static struct ast_cli_entry *cli_next(struct cli_iterator *i)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	struct ast_cli_entry *e;
    
    	if (i->builtins == NULL && i->helpers == NULL) {
    		/* initialize */
    		i->builtins = builtins;
    		i->helpers = AST_LIST_FIRST(&helpers);
    	}
    	e = i->builtins; /* temporary */
    	if (!e->cmda[0] || (i->helpers &&
    		    strcmp(i->helpers->_full_cmd, e->_full_cmd) < 0)) {
    		/* Use helpers */
    		e = i->helpers;
    		if (e)
    			i->helpers = AST_LIST_NEXT(e, list);
    	} else { /* use builtin. e is already set  */
    		(i->builtins)++;	/* move to next */
    	}
    	return e;
    
    /*!
     * \brief locate a cli command in the 'helpers' list (which must be locked).
     * exact has 3 values:
     *      0       returns if the search key is equal or longer than the entry.
     *      -1      true if the mismatch is on the last word XXX not true!
     *      1       true only on complete, exact match.
     */
    static struct ast_cli_entry *find_cli(char *const cmds[], int match_type)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	int matchlen = -1;	/* length of longest match so far */
    	struct ast_cli_entry *cand = NULL, *e=NULL;
    	struct cli_iterator i = { NULL, NULL};
    
    	while( (e = cli_next(&i)) ) {
    		int y;
    		for (y = 0 ; cmds[y] && e->cmda[y]; y++) {
    			if (strcasecmp(e->cmda[y], cmds[y]))
    				break;
    		}
    		if (e->cmda[y] == NULL) {	/* no more words in candidate */
    			if (cmds[y] == NULL)	/* this is an exact match, cannot do better */
    				break;
    			/* here the search key is longer than the candidate */
    			if (match_type != 0)	/* but we look for almost exact match... */
    				continue;	/* so we skip this one. */
    			/* otherwise we like it (case 0) */
    		} else {			/* still words in candidate */
    			if (cmds[y] == NULL)	/* search key is shorter, not good */
    				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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char *find_best(char *argv[])
    {
    	static char cmdline[80];
    	int x;
    
    	/* See how close we get, then print the candidate */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *myargv[AST_MAX_CMD_LEN];
    	for (x=0;x<AST_MAX_CMD_LEN;x++)
    		myargv[x]=NULL;
    
    	AST_LIST_LOCK(&helpers);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return cmdline;
    }
    
    
    static int __ast_cli_unregister(struct ast_cli_entry *e, struct ast_cli_entry *ed)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	if (e->deprecate_cmd) {
    		__ast_cli_unregister(e->deprecate_cmd, e);
    	}
    
    	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);
    
    		free(e->_full_cmd);
    
    		e->_full_cmd = NULL;
    		if (e->command) {
    			/* this is a new-style entry. Reset fields and free memory. */
    			((char **)e->cmda)[0] = NULL;
    			free(e->command);
    			e->command = NULL;
    			e->usage = NULL;
    		}
    
    static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	int i, lf, ret = -1;
    
    	if (e->cmda[0] == NULL) {	/* new style entry, run the handler to init fields */
    		char *args[2] = { (char *)e, NULL };
    		char *s = (char *)(e->handler(-1, CLI_CMD_STRING, args+1));
    		char **dst = (char **)e->cmda;	/* need to cast as the entry is readonly */
    
    		s = ast_skip_blanks(s);
    		s = e->command = ast_strdup(s);
    		for (i=0; !ast_strlen_zero(s) && i < AST_MAX_CMD_LEN-1; i++) {
    			*dst++ = s;	/* store string */
    			s = ast_skip_nonblanks(s);
    			if (*s == '\0')	/* we are done */
    				break;
    			*s++ = '\0';
    			s = ast_skip_blanks(s);
    		}
    		*dst++ = NULL;
    		e->usage = (char *)(e->handler(-1, CLI_USAGE, args+1));
    	}
    	for (i = 0; e->cmda[i]; i++)
    		;
    	e->args = i;
    
    	ast_join(fulle, sizeof(fulle), e->cmda);
    
    	if (find_cli(e->cmda, 1)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", fulle);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	e->_full_cmd = ast_strdup(fulle);
    	if (!e->_full_cmd)
    		goto done;
    
    
    	if (ed) {
    		e->deprecated = 1;
    		e->summary = ed->summary;
    		e->usage = ed->usage;
    		/* XXX If command A deprecates command B, and command B deprecates command C...
    		   Do we want to show command A or command B when telling the user to use new syntax?
    		   This currently would show command A.
    		   To show command B, you just need to always use ed->_full_cmd.
    		 */
    		e->_deprecated_by = S_OR(ed->_deprecated_by, ed->_full_cmd);
    
    	} else {
    		e->deprecated = 0;
    
    	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) {
    
    			AST_LIST_INSERT_BEFORE_CURRENT(&helpers, e, list); 
    
    	if (!cur)
    		AST_LIST_INSERT_TAIL(&helpers, e, list); 
    
    	ret = 0;	/* success */
    
    
    	if (e->deprecate_cmd) {
    		/* This command deprecates another command.  Register that one also. */
    		__ast_cli_register(e->deprecate_cmd, e);
    	}
    
    /* wrapper function, so we can unregister deprecated commands recursively */
    int ast_cli_unregister(struct ast_cli_entry *e)
    {
    	return __ast_cli_unregister(e, NULL);
    }
    
    /* wrapper function, so we can register deprecated commands recursively */
    int ast_cli_register(struct ast_cli_entry *e)
    {
    	return __ast_cli_register(e, NULL);
    }
    
    
    /*
     * register/unregister an array of entries.
     */
    void ast_cli_register_multiple(struct ast_cli_entry *e, int len)
    {
    	int i;
    
    
    		ast_cli_register(e + i);
    }
    
    void ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
    {
    	int 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)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	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)) ) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Hide commands that start with '_' */
    
    		if (e->_full_cmd[0] == '_')
    
    Mark Spencer's avatar
    Mark Spencer committed
    			continue;
    
    		/* Hide commands that are marked as deprecated. */
    		if (e->deprecated)
    			continue;
    
    		if (match && strncasecmp(matchstr, e->_full_cmd, len))
    			continue;
    
    		ast_cli(fd, "%25.25s  %s\n", e->_full_cmd, S_OR(e->summary, "<no description available>"));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	AST_LIST_UNLOCK(&helpers);
    	if (!locked && !found && matchstr[0])
    		ast_cli(fd, "No such command '%s'.\n", matchstr);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    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[])
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char fullcmd[80];
    
    	struct ast_cli_entry *e;
    
    	if (argc < 1)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return RESULT_SHOWUSAGE;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return help_workhorse(fd, NULL);
    
    
    	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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	AST_LIST_UNLOCK(&helpers);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return RESULT_SUCCESS;
    }
    
    
    static char *parse_args(const char *s, int *argc, char *argv[], int max, int *trailingwhitespace)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	char *dup, *cur;
    
    	int x = 0;
    	int quoted = 0;
    	int escaped = 0;
    	int whitespace = 1;
    
    
    	*trailingwhitespace = 0;
    
    	if (s == NULL)	/* invalid, though! */
    		return NULL;
    	/* make a copy to store the parsed string */
    
    		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) {
    
    			quoted = !quoted;
    
    			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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    	/* Null terminate */
    
    	*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;
    
    	*trailingwhitespace = whitespace;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return dup;
    }
    
    
    /*! \brief Return the number of unique matches for the generator */
    
    int ast_cli_generatornummatches(const char *text, const char *word)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int matches = 0, i = 0;
    
    	char *buf = NULL, *oldbuf = NULL;
    
    	while ((buf = ast_cli_generator(text, word, i++))) {
    
    		if (!oldbuf || strcmp(buf,oldbuf))
    			matches++;
    		if (oldbuf)
    			free(oldbuf);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		oldbuf = buf;
    	}
    
    	if (oldbuf)
    		free(oldbuf);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return matches;
    }
    
    
    char **ast_cli_completion_matches(const char *text, const char *word)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	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 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	match_list_len = 1;
    	while ((retstr = ast_cli_generator(text, word, matches)) != NULL) {
    		if (matches + 1 >= match_list_len) {
    			match_list_len <<= 1;
    
    			if (!(match_list = ast_realloc(match_list, match_list_len * sizeof(*match_list))))
    				return NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		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.
    	 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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++)
    
    Mark Spencer's avatar
    Mark Spencer committed
    			continue;
    		max_equal = i;
    	}
    
    
    	if (!(retstr = ast_malloc(max_equal + 1)))
    		return NULL;
    	
    
    	ast_copy_string(retstr, match_list[1], max_equal + 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	match_list[0] = retstr;
    
    
    	/* ensure that the array is NULL terminated */
    
    	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;
    
    static char *__ast_cli_generator(const char *text, const char *word, int state, int lock)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	char *argv[AST_MAX_ARGS];
    
    	struct ast_cli_entry *e;
    	struct cli_iterator i = { NULL, NULL };
    	int x = 0, argindex, matchlen;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int matchnum=0;
    
    	char matchstr[80] = "";
    
    	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);
    
    	if (tws) {
    		strcat(matchstr, " "); /* XXX */
    		if (matchlen)
    			matchlen++;
    	}
    
    	if (lock)
    		AST_LIST_LOCK(&helpers);
    
    	while ( (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 */
    
    				break;
    			}
    		} else if (!strncasecmp(matchstr, e->_full_cmd, lc) && matchstr[lc] < 33) {
    
    			/* This entry is a prefix of the command string entered
    			 * (only one entry in the list should have this property).
    			 * Run the generator if one is available. In any case we are done.
    			 */
    
    				ret = e->generator(matchstr, word, argindex, state - matchnum);
    
    			else if (e->command) {	/* new style command */
    				/* prepare fake arguments for the generator.
    				 * argv[-1] is the cli entry we use,
    				 * argv[0] is a pointer to the generator arguments,
    				 *   with a fake string '-' at the beginning so we can
    				 *   dereference it as a string with no trouble,
    				 *   and then the usual NULL terminator.
    				 */
    				struct ast_cli_args a = {
    					.fake = "-",
    					.line = matchstr, .word = word,
    					.pos = argindex,
    
    					.n = state - matchnum };
    
    				char *args[] = { (char *)e, (char *)&a, NULL };
    				ret = (char *)e->handler(-1, CLI_GENERATE, args + 1);
    			}
    			if (ret)
    				break;
    
    	if (lock)
    		AST_LIST_UNLOCK(&helpers);
    	free(dup);
    	return ret;
    
    char *ast_cli_generator(const char *text, const char *word, int state)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	return __ast_cli_generator(text, word, state, 1);
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	char *args[AST_MAX_ARGS + 1];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_cli_entry *e;
    	int x;
    	char *dup;
    
    	if (!(dup = parse_args(s, &x, args + 1, AST_MAX_ARGS, &tws)))
    
    		return -1;
    
    	/* We need at least one entry, or ignore */
    	if (x > 0) {
    
    		e = find_cli(args + 1, 0);
    
    			/* within calling the handler, argv[-1] contains a pointer
    			 * to the cli entry, and the array is null-terminated
    			 */
    			args[0] = (char *)e;
    			switch(e->handler(fd, x, args + 1)) {
    
    				if (e->usage)
    					ast_cli(fd, "%s", e->usage);
    				else
    					ast_cli(fd, "Invalid usage, but no usage information available.\n");
    
    			default:
    				AST_LIST_LOCK(&helpers);
    				if (e->deprecated == 1) {
    					ast_cli(fd, "The '%s' command is deprecated and will be removed in a future release. Please use '%s' instead.\n", e->_full_cmd, e->_deprecated_by);
    					e->deprecated = 2;
    				}
    				AST_LIST_UNLOCK(&helpers);
    				break;
    
    			ast_cli(fd, "No such command '%s' (type 'help' for help)\n", find_best(args + 1));
    
    		if (e)
    			ast_atomic_fetchadd_int(&e->inuse, -1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }