update the internal cli api following comments from kevin.
authorLuigi Rizzo <rizzo@icir.org>
Wed, 15 Nov 2006 14:11:28 +0000 (14:11 +0000)
committerLuigi Rizzo <rizzo@icir.org>
Wed, 15 Nov 2006 14:11:28 +0000 (14:11 +0000)
This change basically simplifies the interface of the
new-style handler removing almost all the tricks used in
the previous implementation to achieve backward compatibility
(which is still present and guaranteed.)

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@47652 65c4cc65-6c06-0410-ace0-fbb531ad65f3

include/asterisk/cli.h
main/cli.c

index 3ecc733..a718eb4 100644 (file)
@@ -38,6 +38,10 @@ void ast_cli(int fd, char *fmt, ...)
 #define RESULT_SHOWUSAGE       1
 #define RESULT_FAILURE         2
 
+#define CLI_SUCCESS    (char *)RESULT_SUCCESS
+#define CLI_SHOWUSAGE  (char *)RESULT_SHOWUSAGE
+#define CLI_FAILURE    (char *)RESULT_FAILURE
+
 #define AST_MAX_CMD_LEN        16
 
 #define AST_MAX_ARGS 64
@@ -67,67 +71,44 @@ void ast_cli(int fd, char *fmt, ...)
 
    In the "new-style" format, all the above functionalities are implemented
    by a single function, and the arguments tell which output is required.
+   The prototype is the following:
 
-   \note \b Note: ideally, the new-style handler would have a different prototype,
-   i.e. something like
-
-       int new_setdebug(const struct ast_cli *e, int function,
-           int fd, int argc, char *argv[],     // handler args
-           int n, int pos, const char *line, const char *word // -complete args)
-
-   but at this moment we want to help the transition from old-style to new-style
-   functions so we keep the same interface and override some of the traditional
-   arguments.
-
-   To help the transition, a new-style entry has the same interface as the old one,
-   but it is declared as follows:
-
-       int new_setdebug(int fd, int argc, char *argv[]);
+       char *new_setdebug(const struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 
        ...
        // this is how we create the entry to register 
        NEW_CLI(new_setdebug, "short description")
        ...
 
-   Called with the default arguments (argc > 0), the new_handler implements
-   the command as before.
-   A negative argc indicates one of the other functions, namely
-   generate the usage string, the full command, or implement the generator.
-   As a trick to extend the interface while being backward compatible,
-   argv[-1] points to a struct ast_cli_args, and, for the generator,
-   argv[0] is really a pointer to a struct ast_cli_args.
-   The return string is obtained by casting the result to char *
+   To help the transition, we make the pointer to the struct ast_cli_entry
+   available to old-style handlers via argv[-1].
 
    An example of new-style handler is the following
 
 \code
-static int test_new_cli(int fd, int argc, char *argv[])
+static char *test_new_cli(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-        struct ast_cli_entry *e = (struct ast_cli_entry *)argv[-1];
-        struct ast_cli_args *a;
        static char *choices = { "one", "two", "three", NULL };
 
-        switch(argc) {
-        case CLI_USAGE:
-                return (int)
+        switch (cmd) {
+        case CLI_INIT:
+               e->command = "do this well";
+                e->usage =
                        "Usage: do this well <arg>\n"
                        "       typically multiline with body indented\n";
-
-        case CLI_CMD_STRING:
-                return (int)"do this well";
+               return NULL;
 
         case CLI_GENERATE:
-                a = (struct ast_cli_args *)argv[0];
                 if (a->pos > e->args)
                         return NULL;
                return ast_cli_complete(a->word, choices, a->n);
 
         default:        
                 // we are guaranteed to be called with argc >= e->args;
-                if (argc > e->args + 1) // we accept one extra argument
-                        return RESULT_SHOWUSAGE;
-                ast_cli(fd, "done this well for %s\n", e->args[argc-1]);
-                return RESULT_SUCCESS;
+                if (a->argc > e->args + 1) // we accept one extra argument
+                        return CLI_SHOWUSAGE;
+                ast_cli(a->fd, "done this well for %s\n", e->args[argc-1]);
+                return CLI_SUCCESS;
         }
 }
 
@@ -139,12 +120,25 @@ static int test_new_cli(int fd, int argc, char *argv[])
        See \ref CLI_command_API
 */
 enum ast_cli_fn {
-       CLI_USAGE = -1,         /* return the usage string */
-       CLI_CMD_STRING = -2,    /* return the command string */
+       CLI_INIT = -2,          /* return the usage string */
        CLI_GENERATE = -3,      /* behave as 'generator', remap argv to struct ast_cli_args */
+       CLI_HANDLER = -4,       /* run the normal handler */
 };
 
+/* argument for new-style CLI handler */
+struct ast_cli_args {
+       int fd;
+       int argc;
+       char **argv;
+       const char *line;       /* the current input line */
+       const char *word;       /* the word we want to complete */
+       int pos;                /* position of the word to complete */
+       int n;                  /* the iteration count (n-th entry we generate) */
+};
+
+struct ast_cli_entry;
 typedef int (*old_cli_fn)(int fd, int argc, char *argv[]);
+typedef char *(*new_cli_fn)(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 
 /*! \brief descriptor for a cli entry 
        See \ref CLI_command_API
@@ -161,10 +155,10 @@ struct ast_cli_entry {
          You can overwrite argv or the strings it points to, but remember
          that this memory is deallocated after the handler returns.
         */
-       int (*handler)(int fd, int argc, char *argv[]);
+       old_cli_fn handler;
 
        const char *summary; /*!< Summary of the command (< 60 characters) */
-       const char *usage; /*!< Detailed usage information */
+       char *usage; /*!< Detailed usage information */
 
        /*! Generate the n-th (starting from 0) possible completion
          for a given 'word' following 'line' in position 'pos'.
@@ -188,21 +182,13 @@ struct ast_cli_entry {
        int args;               /*!< number of non-null entries in cmda */
        char *command;          /*!< command, non-null for new-style entries */
        int deprecated;
+       new_cli_fn new_handler;
        char *_deprecated_by;   /*!< copied from the "parent" _full_cmd, on deprecated commands */
        /*! For linking */
        AST_LIST_ENTRY(ast_cli_entry) list;
 };
 
-#define NEW_CLI(fn, txt)       { .handler = (old_cli_fn)fn, .summary = txt }
-
-/* argument for new-style CLI handler */
-struct ast_cli_args {
-       char fake[4];           /* a fake string, in the first position, for safety */
-       const char *line;       /* the current input line */
-       const char *word;       /* the word we want to complete */
-       int pos;                /* position of the word to complete */
-       int n;                  /* the iteration count (n-th entry we generate) */
-};
+#define NEW_CLI(fn, txt)       { .new_handler = fn, .summary = txt }
 
 /*!
  * Helper function to generate cli entries from a NULL-terminated array.
index 32bf888..3c0e808 100644 (file)
@@ -168,40 +168,38 @@ static int handle_reload_deprecated(int fd, int argc, char *argv[])
        return handle_reload(fd, argc+1, argv-1);       /* see comment in handle_load_deprecated() */
 }
 
-static int handle_verbose(int fd, int argc, char *argv[])
+static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       /* "core set verbose [atleast] <n>" */
        int oldval = option_verbose;
        int newlevel;
        int atleast = 0;
-       struct ast_cli_entry *e = (struct ast_cli_entry *)argv[-1];
        static char *choices[] = { "off", "atleast", NULL };
-       struct ast_cli_args *a;
-
-       switch (argc) {
-       case CLI_CMD_STRING:
-               return (int)"core set verbose";
-
-       case CLI_USAGE:
-               return (int)
+       int fd = a->fd;
+       int argc = a->argc;
+       char **argv = a->argv;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "core set verbose";
+               e->usage =
                        "Usage: core set verbose [atleast] <level>\n"
                        "       core set verbose off\n"
                        "       Sets level of verbose messages to be displayed.  0 or off means\n"
                        "       no messages should be displayed. Equivalent to -v[v[v...]]\n"
                        "       on startup\n";
+               return NULL;
 
        case CLI_GENERATE:
-               a = (struct ast_cli_args *)argv[0];
                if (a->pos > e->args)
-                       return (int)NULL;
-               return (int)ast_cli_complete(a->word, choices, a->n);
+                       return NULL;
+               return ast_cli_complete(a->word, choices, a->n);
        }
        /* all the above return, so we proceed with the handler.
         * we are guaranteed to be called with argc >= e->args;
         */
 
        if (argc < e->args + 1)
-               return RESULT_SHOWUSAGE;
+               return CLI_SHOWUSAGE;
 
        if (argc == e->args + 1 && !strcasecmp(argv[e->args], "off")) {
                newlevel = 0;
@@ -210,9 +208,9 @@ static int handle_verbose(int fd, int argc, char *argv[])
        if (!strcasecmp(argv[e->args], "atleast"))
                atleast = 1;
        if (argc != e->args + atleast + 1)
-               return RESULT_SHOWUSAGE;
+               return CLI_SHOWUSAGE;
        if (sscanf(argv[e->args + atleast], "%d", &newlevel) != 1)
-               return RESULT_SHOWUSAGE;
+               return CLI_SHOWUSAGE;
 
 done:
        if (!atleast || newlevel > option_verbose)
@@ -226,44 +224,43 @@ done:
                        ast_cli(fd, "Verbosity was %d and is now %d\n", oldval, option_verbose);
        }
 
-       return RESULT_SUCCESS;
+       return CLI_SUCCESS;
 }
 
-static int handle_set_debug(int fd, int argc, char *argv[])
+static char *handle_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       struct ast_cli_entry *e = (struct ast_cli_entry *)argv[-1];
        int oldval = option_debug;
        int newlevel;
        int atleast = 0;
        char *filename = '\0';
        static char *choices[] = { "off", "atleast", NULL };
-       struct ast_cli_args *a;
-
-       switch (argc) {
-       case CLI_CMD_STRING:
-               return (int)"core set debug";
-
-       case CLI_USAGE:
-               return (int)
+       int fd = a->fd;
+       int argc = a->argc;
+       char **argv = a->argv;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "core set debug";
+               e->usage =
                        "Usage: core set debug [atleast] <level> [filename]\n"
                        "       core set debug off\n"
                        "       Sets level of core debug messages to be displayed. 0 or 'off' means\n"
                        "       no messages should be displayed.  Equivalent to -d[d[d...]]\n"
                        "       on startup.  If filename is specified, debugging will be\n"
                        "       limited to just that file.\n";
+               return NULL;
 
        case CLI_GENERATE:
-               a = (struct ast_cli_args *)argv[0];
                if (a->pos > e->args)
-                       return (int)NULL;
-               return (int)ast_cli_complete(a->word, choices, a->n);
+                       return NULL;
+               return ast_cli_complete(a->word, choices, a->n);
        }
        /* all the above return, so we proceed with the handler.
         * we are guaranteed to be called with argc >= e->args;
         */
 
        if (argc < e->args + 1)
-               return RESULT_SHOWUSAGE;
+               return CLI_SHOWUSAGE;
 
        if (argc == e->args + 1 && !strcasecmp(argv[e->args], "off")) {
                newlevel = 0;
@@ -272,9 +269,9 @@ static int handle_set_debug(int fd, int argc, char *argv[])
        if (!strcasecmp(argv[e->args], "atleast"))
                atleast = 1;
        if (argc < e->args + atleast + 1 || argc > e->args + atleast + 2)
-               return RESULT_SHOWUSAGE;
+               return CLI_SHOWUSAGE;
        if (sscanf(argv[e->args + atleast], "%d", &newlevel) != 1)
-               return RESULT_SHOWUSAGE;
+               return CLI_SHOWUSAGE;
 
        if (argc == e->args + atleast + 1) {
                debug_filename[0] = '\0';
@@ -302,7 +299,7 @@ done:
                }
        }
 
-       return RESULT_SUCCESS;
+       return CLI_SUCCESS;
 }
 
 static int handle_logger_mute(int fd, int argc, char *argv[])
@@ -415,80 +412,72 @@ static void print_uptimestr(int fd, time_t timeval, const char *prefix, int prin
                ast_cli(fd, "%s: %s\n", prefix, timestr);
 }
 
-static int handle_showuptime(int fd, int argc, char *argv[])
+static char * handle_showuptime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       struct ast_cli_entry *e = (struct ast_cli_entry *)argv[-1];
        time_t curtime;
        int printsec;
-       struct ast_cli_args *a;
-
-       switch (argc) {
-        case CLI_CMD_STRING:
-               return (int)"core show uptime";
 
-       case CLI_USAGE:
-               return (int)
+       switch (cmd) {
+        case CLI_INIT:
+               e->command = "core show uptime";
+               e->usage =
                        "Usage: core show uptime [seconds]\n"
                        "       Shows Asterisk uptime information.\n"
                        "       The seconds word returns the uptime in seconds only.\n";
+               return NULL;
 
        case CLI_GENERATE:
-               a = (struct ast_cli_args *)argv[0];
-               return (int)((a->pos > e->args || a->n > 0) ? NULL : "seconds");
+               return (a->pos > e->args || a->n > 0) ? NULL : "seconds";
        }
        /* regular handler */
-       if (argc == e->args+1 && !strcasecmp(argv[e->args],"seconds"))
+       if (a->argc == e->args+1 && !strcasecmp(a->argv[e->args],"seconds"))
                printsec = 1;
-       else if (argc == e->args)
+       else if (a->argc == e->args)
                printsec = 0;
        else
-               return RESULT_SHOWUSAGE;
+               return CLI_SHOWUSAGE;
        curtime = time(NULL);
        if (ast_startuptime)
-               print_uptimestr(fd, curtime - ast_startuptime, "System uptime", printsec);
+               print_uptimestr(a->fd, curtime - ast_startuptime, "System uptime", printsec);
        if (ast_lastreloadtime)
-               print_uptimestr(fd, curtime - ast_lastreloadtime, "Last reload", printsec);
-       return RESULT_SUCCESS;
+               print_uptimestr(a->fd, curtime - ast_lastreloadtime, "Last reload", printsec);
+       return CLI_SUCCESS;
 }
 
-static int handle_modlist(int fd, int argc, char *argv[])
+static char *handle_modlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       struct ast_cli_entry *e = (struct ast_cli_entry *)argv[-1];
        char *like;
-       struct ast_cli_args *a;
 
-       switch(argc) {
-       case CLI_CMD_STRING:
-               return (int)"module show";
-
-       case CLI_USAGE:
-               return (int)
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "module show";
+               e->usage =
                        "Usage: module show [like keyword]\n"
                        "       Shows Asterisk modules currently in use, and usage statistics.\n";
+               return NULL;
 
        case CLI_GENERATE:
-               a = (struct ast_cli_args *)argv[0];
                if (a->pos == e->args)
-                       return (int)(a->n == 0 ? strdup("like") : NULL);
+                       return a->n == 0 ? strdup("like") : NULL;
                else if (a->pos == e->args+1 && strcasestr(a->line," like "))
-                       return (int)ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
+                       return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
                else
-                       return (int)NULL;
+                       return NULL;
        }
        /* all the above return, so we proceed with the handler.
         * we are guaranteed to have argc >= e->args
         */
-       if (argc == e->args)
+       if (a->argc == e->args)
                like = "";
-       else if (argc == e->args + 2 && !strcmp(argv[e->args],"like"))
-               like = argv[e->args + 1];
+       else if (a->argc == e->args + 2 && !strcmp(a->argv[e->args],"like"))
+               like = a->argv[e->args + 1];
        else
-               return RESULT_SHOWUSAGE;
+               return CLI_SHOWUSAGE;
                
        ast_mutex_lock(&climodentrylock);
-       climodentryfd = fd; /* global, protected by climodentrylock */
-       ast_cli(fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
-       ast_cli(fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like));
+       climodentryfd = a->fd; /* global, protected by climodentrylock */
+       ast_cli(a->fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
+       ast_cli(a->fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like));
        climodentryfd = -1;
        ast_mutex_unlock(&climodentrylock);
        return RESULT_SUCCESS;
@@ -1281,9 +1270,9 @@ static int __ast_cli_unregister(struct ast_cli_entry *e, struct ast_cli_entry *e
                AST_LIST_UNLOCK(&helpers);
                free(e->_full_cmd);
                e->_full_cmd = NULL;
-               if (e->command) {
+               if (e->new_handler) {
                        /* this is a new-style entry. Reset fields and free memory. */
-                       ((char **)e->cmda)[0] = NULL;
+                       bzero((char **)(e->cmda), sizeof(e->cmda));
                        free(e->command);
                        e->command = NULL;
                        e->usage = NULL;
@@ -1298,12 +1287,15 @@ static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed)
        char fulle[80] ="";
        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));
+       if (e->handler == NULL) {       /* new style entry, run the handler to init fields */
+               struct ast_cli_args a;  /* fake argument */
                char **dst = (char **)e->cmda;  /* need to cast as the entry is readonly */
+               char *s;
 
-               s = ast_skip_blanks(s);
+               bzero (&a, sizeof(a));
+               e->new_handler(e, CLI_INIT, &a);
+               /* XXX check that usage and command are filled up */
+               s = ast_skip_blanks(e->command);
                s = e->command = ast_strdup(s);
                for (i=0; !ast_strlen_zero(s) && i < AST_MAX_CMD_LEN-1; i++) {
                        *dst++ = s;     /* store string */
@@ -1314,7 +1306,6 @@ static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed)
                        s = ast_skip_blanks(s);
                }
                *dst++ = NULL;
-               e->usage = (char *)(e->handler(-1, CLI_USAGE, args+1));
        }
        for (i = 0; e->cmda[i]; i++)
                ;
@@ -1638,21 +1629,12 @@ static char *__ast_cli_generator(const char *text, const char *word, int state,
                         */
                        if (e->generator)
                                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.
-                                */
+                       else if (e->new_handler) {      /* new style command */
                                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);
+                               ret = e->new_handler(e, CLI_GENERATE, &a);
                        }
                        if (ret)
                                break;
@@ -1688,11 +1670,19 @@ int ast_cli_command(int fd, const char *s)
                        e->inuse++;
                AST_LIST_UNLOCK(&helpers);
                if (e) {
+                       int res;
                        /* 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->new_handler) {   /* new style */
+                               struct ast_cli_args a = {
+                                       .fd = fd, .argc = x, .argv = args+1 };
+                               res = (int)e->new_handler(e, CLI_HANDLER, &a);
+                       } else {                /* old style */
+                               res = e->handler(fd, x, args + 1);
+                       }
+                       switch (res) {
                        case RESULT_SHOWUSAGE:
                                if (e->usage)
                                        ast_cli(fd, "%s", e->usage);