From 8eb43bf72c2109092e498870869109419e13e895 Mon Sep 17 00:00:00 2001 From: antirez Date: Sat, 7 May 2016 13:03:25 +0200 Subject: [PATCH] redis-cli: integrate help.h with COMMAND output. Use the COMMAND output to fill with partial information the built-in help. This makes redis-cli able to at least complete commands that are exported by the Redis server it is connected to, but were not available in the help.h file when the redis-cli binary was compiled. --- src/redis-cli.c | 80 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 10 deletions(-) diff --git a/src/redis-cli.c b/src/redis-cli.c index 1e47c340..cf939c8c 100644 --- a/src/redis-cli.c +++ b/src/redis-cli.c @@ -130,6 +130,7 @@ static void usage(void); static void slaveMode(void); char *redisGitSHA1(void); char *redisGitDirty(void); +static int cliConnect(int force); /*------------------------------------------------------------------------------ * Utility functions @@ -238,11 +239,11 @@ static void cliInitHelp(void) { helpEntry tmp; helpEntriesLen = len = commandslen+groupslen; - helpEntries = malloc(sizeof(helpEntry)*len); + helpEntries = zmalloc(sizeof(helpEntry)*len); for (i = 0; i < groupslen; i++) { tmp.argc = 1; - tmp.argv = malloc(sizeof(sds)); + tmp.argv = zmalloc(sizeof(sds)); tmp.argv[0] = sdscatprintf(sdsempty(),"@%s",commandGroups[i]); tmp.full = tmp.argv[0]; tmp.type = CLI_HELP_GROUP; @@ -259,6 +260,64 @@ static void cliInitHelp(void) { } } +/* cliInitHelp() setups the helpEntries array with the command and group + * names from the help.h file. However the Redis instance we are connecting + * to may support more commands, so this function integrates the previous + * entries with additional entries obtained using the COMMAND command + * available in recent versions of Redis. */ +static void cliIntegrateHelp(void) { + if (cliConnect(0) == REDIS_ERR) return; + + redisReply *reply = redisCommand(context, "COMMAND"); + if(reply == NULL || reply->type != REDIS_REPLY_ARRAY) return; + + /* Scan the array reported by COMMAND and fill only the entries that + * don't already match what we have. */ + for (size_t j = 0; j < reply->elements; j++) { + redisReply *entry = reply->element[j]; + char *cmdname = entry->element[0]->str; + int i; + + for (i = 0; i < helpEntriesLen; i++) { + helpEntry *he = helpEntries+i; + if (!strcasecmp(he->argv[0],cmdname)) + break; + } + if (i != helpEntriesLen) continue; + printf("%s\n", cmdname); + + helpEntriesLen++; + helpEntries = zrealloc(helpEntries,sizeof(helpEntry)*helpEntriesLen); + helpEntry *new = helpEntries+(helpEntriesLen-1); + + new->argc = 1; + new->argv = zmalloc(sizeof(sds)); + new->argv[0] = sdsnew(cmdname); + new->full = new->argv[0]; + new->type = CLI_HELP_COMMAND; + sdstoupper(new->argv[0]); + + struct commandHelp *ch = zmalloc(sizeof(*ch)); + ch->name = new->argv[0]; + ch->params = sdsempty(); + int args = llabs(entry->element[1]->integer); + if (entry->element[3]->integer == 1) { + ch->params = sdscat(ch->params,"key "); + args--; + } + while(args--) ch->params = sdscat(ch->params,"arg "); + if (entry->element[1]->integer < 0) + ch->params = sdscat(ch->params,"...options..."); + ch->summary = "Help not available"; + ch->group = 0; + ch->since = "not known"; + new->org = ch; + } + freeReplyObject(reply); + + printf("%s\n", helpEntries[80].full); +} + /* Output command help to stdout. */ static void cliOutputCommandHelp(struct commandHelp *help, int group) { printf("\r\n \x1b[1m%s\x1b[0m \x1b[90m%s\x1b[0m\r\n", help->name, help->params); @@ -828,7 +887,7 @@ static int cliSendCommand(int argc, char **argv, int repeat) { } /* Setup argument length */ - argvlen = malloc(argc*sizeof(size_t)); + argvlen = zmalloc(argc*sizeof(size_t)); for (j = 0; j < argc; j++) argvlen[j] = sdslen(argv[j]); @@ -851,12 +910,12 @@ static int cliSendCommand(int argc, char **argv, int repeat) { printf("Entering slave output mode... (press Ctrl-C to quit)\n"); slaveMode(); config.slave_mode = 0; - free(argvlen); + zfree(argvlen); return REDIS_ERR; /* Error = slaveMode lost connection to master */ } if (cliReadReply(output_raw) != REDIS_OK) { - free(argvlen); + zfree(argvlen); return REDIS_ERR; } else { /* Store database number when SELECT was successfully executed. */ @@ -871,7 +930,7 @@ static int cliSendCommand(int argc, char **argv, int repeat) { fflush(stdout); /* Make it grep friendly */ } - free(argvlen); + zfree(argvlen); return REDIS_OK; } @@ -1235,7 +1294,7 @@ static void repl(void) { if (argv == NULL) { printf("Invalid argument(s)\n"); - free(line); + linenoiseFree(line); continue; } else if (argc > 0) { if (strcasecmp(argv[0],"quit") == 0 || @@ -1294,7 +1353,7 @@ static void repl(void) { sdsfreesplitres(argv,argc); } /* linenoise() returns malloc-ed lines like readline() */ - free(line); + linenoiseFree(line); } exit(0); } @@ -2159,7 +2218,7 @@ static char *getInfoField(char *info, char *field) { n1 = strchr(p,'\r'); n2 = strchr(p,','); if (n2 && n2 < n1) n1 = n2; - result = malloc(sizeof(char)*(n1-p)+1); + result = zmalloc(sizeof(char)*(n1-p)+1); memcpy(result,p,(n1-p)); result[n1-p] = '\0'; return result; @@ -2173,7 +2232,7 @@ static long getLongInfoField(char *info, char *field) { if (!value) return LONG_MIN; l = strtol(value,NULL,10); - free(value); + zfree(value); return l; } @@ -2536,6 +2595,7 @@ int main(int argc, char **argv) { config.output = OUTPUT_STANDARD; config.mb_delim = sdsnew("\n"); cliInitHelp(); + cliIntegrateHelp(); firstarg = parseOptions(argc,argv); argc -= firstarg;