Merge branch 'master' into brpoplpush

This commit is contained in:
Pieter Noordhuis 2010-12-06 12:31:56 +01:00
commit bc8ffafeb9
7 changed files with 1010 additions and 18 deletions

View File

@ -16,4 +16,7 @@ clean:
$(TARGETS):
cd src && $(MAKE) $@
src/help.h:
@./utils/generate-command-help.rb > $@
dummy:

View File

@ -2,9 +2,18 @@
#include <stdlib.h>
#include "linenoise.h"
void completion(const char *buf, linenoiseCompletions *lc) {
if (buf[0] == 'h') {
linenoiseAddCompletion(lc,"hello");
linenoiseAddCompletion(lc,"hello there");
}
}
int main(void) {
char *line;
linenoiseSetCompletionCallback(completion);
linenoiseHistoryLoad("history.txt"); /* Load the history at startup */
while((line = linenoise("hello> ")) != NULL) {
if (line[0] != '\0') {

View File

@ -9,6 +9,8 @@
* the 2010 UNIX computers around.
*
* Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -66,6 +68,17 @@
* CUF (CUrsor Forward)
* Sequence: ESC [ n C
* Effect: moves cursor forward of n chars
*
* The following are used to clear the screen: ESC [ H ESC [ 2 J
* This is actually composed of two sequences:
*
* cursorhome
* Sequence: ESC [ H
* Effect: moves the cursor to upper left corner
*
* ED2 (Clear entire screen)
* Sequence: ESC [ 2 J
* Effect: clear the whole screen
*
*/
@ -79,10 +92,12 @@
#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "linenoise.h"
#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
#define LINENOISE_MAX_LINE 4096
static char *unsupported_term[] = {"dumb","cons25",NULL};
static linenoiseCompletionCallback *completionCallback = NULL;
static struct termios orig_termios; /* in order to restore at exit */
static int rawmode = 0; /* for atexit() function to check if restore is needed*/
@ -195,6 +210,78 @@ static void refreshLine(int fd, const char *prompt, char *buf, size_t len, size_
if (write(fd,seq,strlen(seq)) == -1) return;
}
static void beep() {
fprintf(stderr, "\x7");
fflush(stderr);
}
static void freeCompletions(linenoiseCompletions *lc) {
size_t i;
for (i = 0; i < lc->len; i++)
free(lc->cvec[i]);
if (lc->cvec != NULL)
free(lc->cvec);
}
static int completeLine(int fd, const char *prompt, char *buf, size_t buflen, size_t *len, size_t *pos, size_t cols) {
linenoiseCompletions lc = { 0, NULL };
int nread, nwritten;
char c = 0;
completionCallback(buf,&lc);
if (lc.len == 0) {
beep();
} else {
size_t stop = 0, i = 0;
size_t clen;
while(!stop) {
/* Show completion or original buffer */
if (i < lc.len) {
clen = strlen(lc.cvec[i]);
refreshLine(fd,prompt,lc.cvec[i],clen,clen,cols);
} else {
refreshLine(fd,prompt,buf,*len,*pos,cols);
}
nread = read(fd,&c,1);
if (nread <= 0) {
freeCompletions(&lc);
return -1;
}
switch(c) {
case 9: /* tab */
i = (i+1) % (lc.len+1);
if (i == lc.len) beep();
break;
case 27: /* escape */
/* Re-show original buffer */
if (i < lc.len) {
refreshLine(fd,prompt,buf,*len,*pos,cols);
}
stop = 1;
break;
default:
/* Update buffer and return */
if (i < lc.len) {
nwritten = snprintf(buf,buflen,"%s",lc.cvec[i]);
*len = *pos = nwritten;
}
stop = 1;
break;
}
}
}
freeCompletions(&lc);
return c; /* Return last read character */
}
void linenoiseClearScreen(void) {
write(STDIN_FILENO,"\x1b[H\x1b[2J",7);
}
static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt) {
size_t plen = strlen(prompt);
size_t pos = 0;
@ -217,6 +304,18 @@ static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt)
nread = read(fd,&c,1);
if (nread <= 0) return len;
/* Only autocomplete when the callback is set. It returns < 0 when
* there was an error reading from fd. Otherwise it will return the
* character that should be handled next. */
if (c == 9 && completionCallback != NULL) {
c = completeLine(fd,prompt,buf,buflen,&len,&pos,cols);
/* Return on errors */
if (c < 0) return len;
/* Read next character when 0 */
if (c == 0) continue;
}
switch(c) {
case 13: /* enter */
case 4: /* ctrl-d */
@ -350,6 +449,9 @@ up_down_arrow:
pos = len;
refreshLine(fd,prompt,buf,len,pos,cols);
break;
case 12: /* ctrl+l, clear screen */
linenoiseClearScreen();
refreshLine(fd,prompt,buf,len,pos,cols);
}
}
return len;
@ -402,6 +504,19 @@ char *linenoise(const char *prompt) {
}
}
/* Register a callback function to be called for tab-completion. */
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
completionCallback = fn;
}
void linenoiseAddCompletion(linenoiseCompletions *lc, char *str) {
size_t len = strlen(str);
char *copy = malloc(len+1);
memcpy(copy,str,len+1);
lc->cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
lc->cvec[lc->len++] = copy;
}
/* Using a circular buffer is smarter, but a bit more complex to handle. */
int linenoiseHistoryAdd(const char *line) {
char *linecopy;

View File

@ -4,6 +4,8 @@
* See linenoise.c for more information.
*
* Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -34,10 +36,20 @@
#ifndef __LINENOISE_H
#define __LINENOISE_H
typedef struct linenoiseCompletions {
size_t len;
char **cvec;
} linenoiseCompletions;
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
void linenoiseAddCompletion(linenoiseCompletions *, char *);
char *linenoise(const char *prompt);
int linenoiseHistoryAdd(const char *line);
int linenoiseHistorySetMaxLen(int len);
int linenoiseHistorySave(char *filename);
int linenoiseHistoryLoad(char *filename);
void linenoiseClearScreen(void);
#endif /* __LINENOISE_H */

608
src/help.h Normal file
View File

@ -0,0 +1,608 @@
/* Automatically generated by utils/generate-command-help.rb, do not edit. */
#ifndef __REDIS_HELP_H
#define __REDIS_HELP_H
static char *commandGroups[] = {
"generic",
"string",
"list",
"set",
"sorted_set",
"hash",
"pubsub",
"transactions",
"connection",
"server"
};
struct commandHelp {
char *name;
char *params;
char *summary;
int group;
char *since;
} commandHelp[] = {
{ "APPEND",
"key value",
"Append a value to a key",
1,
"1.3.3" },
{ "AUTH",
"password",
"Authenticate to the server",
8,
"0.08" },
{ "BGREWRITEAOF",
"-",
"Asynchronously rewrite the append-only file",
9,
"1.07" },
{ "BGSAVE",
"-",
"Asynchronously save the dataset to disk",
9,
"0.07" },
{ "BLPOP",
"key [key ...] timeout",
"Remove and get the first element in a list, or block until one is available",
2,
"1.3.1" },
{ "BRPOP",
"key [key ...] timeout",
"Remove and get the last element in a list, or block until one is available",
2,
"1.3.1" },
{ "CONFIG GET",
"parameter",
"Get the value of a configuration parameter",
9,
"2.0" },
{ "CONFIG SET",
"parameter value",
"Set a configuration parameter to the given value",
9,
"2.0" },
{ "DBSIZE",
"-",
"Return the number of keys in the selected database",
9,
"0.07" },
{ "DEBUG OBJECT",
"key",
"Get debugging information about a key",
9,
"0.101" },
{ "DEBUG SEGFAULT",
"-",
"Make the server crash",
9,
"0.101" },
{ "DECR",
"key decrement",
"Decrement the integer value of a key by one",
1,
"0.07" },
{ "DECRBY",
"key decrement",
"Decrement the integer value of a key by the given number",
1,
"0.07" },
{ "DEL",
"key [key ...]",
"Delete a key",
0,
"0.07" },
{ "DISCARD",
"-",
"Discard all commands issued after MULTI",
7,
"1.3.3" },
{ "ECHO",
"message",
"Echo the given string",
8,
"0.07" },
{ "EXEC",
"-",
"Execute all commands issued after MULTI",
7,
"1.1.95" },
{ "EXISTS",
"key",
"Determine if a key exists",
9,
"0.07" },
{ "EXPIRE",
"key seconds",
"Set a key's time to live in seconds",
0,
"0.09" },
{ "EXPIREAT",
"key timestamp",
"Set the expiration for a key as a UNIX timestamp",
0,
"1.1" },
{ "FLUSHALL",
"-",
"Remove all keys from all databases",
9,
"0.07" },
{ "FLUSHDB",
"-",
"Remove all keys from the current database",
9,
"0.07" },
{ "GET",
"key",
"Get the value of a key",
1,
"0.07" },
{ "GETSET",
"key value",
"Set the string value of a key and return its old value",
1,
"0.091" },
{ "HDEL",
"key field",
"Delete a hash field",
5,
"1.3.10" },
{ "HEXISTS",
"key field",
"Determine if a hash field exists",
5,
"1.3.10" },
{ "HGET",
"key field",
"Get the value of a hash field",
5,
"1.3.10" },
{ "HGETALL",
"key",
"Get all the fields and values in a hash",
5,
"1.3.10" },
{ "HINCRBY",
"key field increment",
"Increment the integer value of a hash field by the given number",
5,
"1.3.10" },
{ "HKEYS",
"key",
"Get all the fields in a hash",
5,
"1.3.10" },
{ "HLEN",
"key",
"Get the number of fields in a hash",
5,
"1.3.10" },
{ "HMGET",
"key field [field ...]",
"Get the values of all the given hash fields",
5,
"1.3.10" },
{ "HMSET",
"key field value [field value ...]",
"Set multiple hash fields to multiple values",
5,
"1.3.8" },
{ "HSET",
"key field value",
"Set the string value of a hash field",
5,
"1.3.10" },
{ "HSETNX",
"key field value",
"Set the value of a hash field, only if the field does not exist",
5,
"1.3.8" },
{ "HVALS",
"key",
"Get all the values in a hash",
5,
"1.3.10" },
{ "INCR",
"key",
"Increment the integer value of a key by one",
1,
"0.07" },
{ "INCRBY",
"key increment",
"Increment the integer value of a key by the given number",
1,
"0.07" },
{ "INFO",
"-",
"Get information and statistics about the server",
9,
"0.07" },
{ "KEYS",
"pattern",
"Find all keys matching the given pattern",
0,
"0.07" },
{ "LASTSAVE",
"-",
"Get the UNIX time stamp of the last successful save to disk",
9,
"0.07" },
{ "LINDEX",
"key index",
"Get an element from a list by its index",
2,
"0.07" },
{ "LINSERT",
"key BEFORE|AFTER pivot value",
"Insert an element before or after another element in a list",
2,
"2.1.1" },
{ "LLEN",
"key",
"Get the length of a list",
2,
"0.07" },
{ "LPOP",
"key",
"Remove and get the first element in a list",
2,
"0.07" },
{ "LPUSH",
"key value",
"Prepend a value to a list",
2,
"0.07" },
{ "LPUSHX",
"key value",
"Prepend a value to a list, only if the list exists",
2,
"2.1.1" },
{ "LRANGE",
"key start stop",
"Get a range of elements from a list",
2,
"0.07" },
{ "LREM",
"key count value",
"Remove elements from a list",
2,
"0.07" },
{ "LSET",
"key index value",
"Set the value of an element in a list by its index",
2,
"0.07" },
{ "LTRIM",
"key start stop",
"Trim a list to the specified range",
2,
"0.07" },
{ "MGET",
"key [key ...]",
"Get the values of all the given keys",
1,
"0.07" },
{ "MONITOR",
"-",
"Listen for all requests received by the server in real time",
9,
"0.07" },
{ "MOVE",
"key db",
"Move a key to another database",
0,
"0.07" },
{ "MSET",
"key value [key value ...]",
"Set multiple keys to multiple values",
1,
"1.001" },
{ "MSETNX",
"key value [key value ...]",
"Set multiple keys to multiple values, only if none of the keys exist",
1,
"1.001" },
{ "MULTI",
"-",
"Mark the start of a transaction block",
7,
"1.1.95" },
{ "PERSIST",
"key",
"Remove the expiration from a key",
0,
"2.1.2" },
{ "PING",
"-",
"Ping the server",
8,
"0.07" },
{ "PSUBSCRIBE",
"pattern",
"Listen for messages published to channels matching the given patterns",
6,
"1.3.8" },
{ "PUBLISH",
"channel message",
"Post a message to a channel",
6,
"1.3.8" },
{ "PUNSUBSCRIBE",
"[pattern [pattern ...]]",
"Stop listening for messages posted to channels matching the given patterns",
6,
"1.3.8" },
{ "QUIT",
"-",
"Close the connection",
8,
"0.07" },
{ "RANDOMKEY",
"-",
"Return a random key from the keyspace",
0,
"0.07" },
{ "RENAME",
"old new",
"Rename a key",
0,
"0.07" },
{ "RENAMENX",
"old new",
"Rename a key, only if the new key does not exist",
0,
"0.07" },
{ "RPOP",
"key",
"Remove and get the last element in a list",
2,
"0.07" },
{ "RPOPLPUSH",
"source destination",
"Remove the last element in a list, append it to another list and return it",
2,
"1.1" },
{ "RPUSH",
"key value",
"Append a value to a list",
2,
"0.07" },
{ "RPUSHX",
"key value",
"Append a value to a list, only if the list exists",
2,
"2.1.1" },
{ "SADD",
"key member",
"Add a member to a set",
3,
"0.07" },
{ "SAVE",
"-",
"Synchronously save the dataset to disk",
9,
"0.07" },
{ "SCARD",
"key",
"Get the number of members in a set",
3,
"0.07" },
{ "SDIFF",
"key [key ...]",
"Subtract multiple sets",
3,
"0.100" },
{ "SDIFFSTORE",
"destination key [key ...]",
"Subtract multiple sets and store the resulting set in a key",
3,
"0.100" },
{ "SELECT",
"index",
"Change the selected database for the current connection",
8,
"0.07" },
{ "SET",
"key value",
"Set the string value of a key",
1,
"0.07" },
{ "SETEX",
"key timestamp value",
"Set the value and expiration of a key",
1,
"1.3.10" },
{ "SETNX",
"key value",
"Set the value of a key, only if the key does not exist",
1,
"0.07" },
{ "SHUTDOWN",
"-",
"Synchronously save the dataset to disk and then shut down the server",
9,
"0.07" },
{ "SINTER",
"key [key ...]",
"Intersect multiple sets",
3,
"0.07" },
{ "SINTERSTORE",
"destination key [key ...]",
"Intersect multiple sets and store the resulting set in a key",
3,
"0.07" },
{ "SISMEMBER",
"key member",
"Determine if a given value is a member of a set",
3,
"0.07" },
{ "SLAVEOF",
"host port",
"Make the server a slave of another instance, or promote it as master",
9,
"0.100" },
{ "SMEMBERS",
"key",
"Get all the members in a set",
3,
"0.07" },
{ "SMOVE",
"source destination member",
"Move a member from one set to another",
3,
"0.091" },
{ "SORT",
"key [BY pattern] [LIMIT start count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]",
"Sort the elements in a list, set or sorted set",
0,
"0.07" },
{ "SPOP",
"key",
"Remove and return a random member from a set",
3,
"0.101" },
{ "SRANDMEMBER",
"key",
"Get a random member from a set",
3,
"1.001" },
{ "SREM",
"key member",
"Remove a member from a set",
3,
"0.07" },
{ "STRLEN",
"key",
"Get the length of the value stored in a key",
1,
"2.1.2" },
{ "SUBSCRIBE",
"channel",
"Listen for messages published to the given channels",
6,
"1.3.8" },
{ "SUBSTR",
"key start stop",
"Get a substring of the string stored at a key",
1,
"1.3.4" },
{ "SUNION",
"key [key ...]",
"Add multiple sets",
3,
"0.091" },
{ "SUNIONSTORE",
"destination key [key ...]",
"Add multiple sets and store the resulting set in a key",
3,
"0.091" },
{ "SYNC",
"-",
"Internal command used for replication",
9,
"0.07" },
{ "TTL",
"key",
"Get the time to live for a key",
0,
"0.100" },
{ "TYPE",
"key",
"Determine the type stored at key",
0,
"0.07" },
{ "UNSUBSCRIBE",
"[channel [channel ...]]",
"Stop listening for messages posted to the given channels",
6,
"1.3.8" },
{ "UNWATCH",
"-",
"Forget about all watched keys",
7,
"2.1.0" },
{ "WATCH",
"key [key ...]",
"Watch the given keys to determine execution of the MULTI/EXEC block",
7,
"2.1.0" },
{ "ZADD",
"key score member",
"Add a member to a sorted set, or update its score if it already exists",
4,
"1.1" },
{ "ZCARD",
"key",
"Get the number of members in a sorted set",
4,
"1.1" },
{ "ZCOUNT",
"key min max",
"Count the members in a sorted set with scores within the given values",
4,
"1.3.3" },
{ "ZINCRBY",
"key increment member",
"Increment the score of a member in a sorted set",
4,
"1.1" },
{ "ZINTERSTORE",
"destination key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]",
"Intersect multiple sorted sets and store the resulting sorted set in a new key",
4,
"1.3.10" },
{ "ZRANGE",
"key start stop",
"Return a range of members in a sorted set, by index",
4,
"1.1" },
{ "ZRANGEBYSCORE",
"key min max",
"Return a range of members in a sorted set, by score",
4,
"1.050" },
{ "ZRANK",
"key member",
"Determine the index of a member in a sorted set",
4,
"1.3.4" },
{ "ZREM",
"key member",
"Remove a member from a sorted set",
4,
"1.1" },
{ "ZREMRANGEBYRANK",
"key start stop",
"Remove all members in a sorted set within the given indexes",
4,
"1.3.4" },
{ "ZREMRANGEBYSCORE",
"key min max",
"Remove all members in a sorted set within the given scores",
4,
"1.1" },
{ "ZREVRANGE",
"key start stop",
"Return a range of members in a sorted set, by index, with scores ordered from high to low",
4,
"1.1" },
{ "ZREVRANK",
"key member",
"Determine the index of a member in a sorted set, with scores ordered from high to low",
4,
"1.3.4" },
{ "ZSCORE",
"key member",
"Get the score associated with the given member in a sorted set",
4,
"1.1" },
{ "ZUNIONSTORE",
"destination key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]",
"Add multiple sorted sets and store the resulting sorted set in a new key",
4,
"1.3.10" }
};
#endif

View File

@ -39,11 +39,13 @@
#include <errno.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <assert.h>
#include "hiredis.h"
#include "sds.h"
#include "zmalloc.h"
#include "linenoise.h"
#include "help.h"
#define REDIS_NOTUSED(V) ((void) V)
@ -83,6 +85,149 @@ static long long mstime(void) {
return mst;
}
/*------------------------------------------------------------------------------
* Help functions
*--------------------------------------------------------------------------- */
#define CLI_HELP_COMMAND 1
#define CLI_HELP_GROUP 2
typedef struct {
int type;
int argc;
sds *argv;
sds full;
/* Only used for help on commands */
struct commandHelp *org;
} helpEntry;
static helpEntry *helpEntries;
static int helpEntriesLen;
static void cliInitHelp() {
int commandslen = sizeof(commandHelp)/sizeof(struct commandHelp);
int groupslen = sizeof(commandGroups)/sizeof(char*);
int i, len, pos = 0;
helpEntry tmp;
helpEntriesLen = len = commandslen+groupslen;
helpEntries = malloc(sizeof(helpEntry)*len);
for (i = 0; i < groupslen; i++) {
tmp.argc = 1;
tmp.argv = malloc(sizeof(sds));
tmp.argv[0] = sdscatprintf(sdsempty(),"@%s",commandGroups[i]);
tmp.full = tmp.argv[0];
tmp.type = CLI_HELP_GROUP;
tmp.org = NULL;
helpEntries[pos++] = tmp;
}
for (i = 0; i < commandslen; i++) {
tmp.argv = sdssplitargs(commandHelp[i].name,&tmp.argc);
tmp.full = sdsnew(commandHelp[i].name);
tmp.type = CLI_HELP_COMMAND;
tmp.org = &commandHelp[i];
helpEntries[pos++] = tmp;
}
}
/* 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);
printf(" \x1b[33msummary:\x1b[0m %s\r\n", help->summary);
printf(" \x1b[33msince:\x1b[0m %s\r\n", help->since);
if (group) {
printf(" \x1b[33mgroup:\x1b[0m %s\r\n", commandGroups[help->group]);
}
}
/* Print generic help. */
static void cliOutputGenericHelp() {
printf(
"redis-cli %s\r\n"
"Type: \"help @<group>\" to get a list of commands in <group>\r\n"
" \"help <command>\" for help on <command>\r\n"
" \"help <tab>\" to get a list of possible help topics\r\n"
" \"quit\" to exit\r\n",
REDIS_VERSION
);
}
/* Output all command help, filtering by group or command name. */
static void cliOutputHelp(int argc, char **argv) {
int i, j, len;
int group = -1;
helpEntry *entry;
struct commandHelp *help;
if (argc == 0) {
cliOutputGenericHelp();
return;
} else if (argc > 0 && argv[0][0] == '@') {
len = sizeof(commandGroups)/sizeof(char*);
for (i = 0; i < len; i++) {
if (strcasecmp(argv[0]+1,commandGroups[i]) == 0) {
group = i;
break;
}
}
}
assert(argc > 0);
for (i = 0; i < helpEntriesLen; i++) {
entry = &helpEntries[i];
if (entry->type != CLI_HELP_COMMAND) continue;
help = entry->org;
if (group == -1) {
/* Compare all arguments */
if (argc == entry->argc) {
for (j = 0; j < argc; j++) {
if (strcasecmp(argv[j],entry->argv[j]) != 0) break;
}
if (j == argc) {
cliOutputCommandHelp(help,1);
}
}
} else {
if (group == help->group) {
cliOutputCommandHelp(help,0);
}
}
}
printf("\r\n");
}
static void completionCallback(const char *buf, linenoiseCompletions *lc) {
size_t startpos = 0;
int mask;
int i;
size_t matchlen;
sds tmp;
if (strncasecmp(buf,"help ",5) == 0) {
startpos = 5;
while (isspace(buf[startpos])) startpos++;
mask = CLI_HELP_COMMAND | CLI_HELP_GROUP;
} else {
mask = CLI_HELP_COMMAND;
}
for (i = 0; i < helpEntriesLen; i++) {
if (!(helpEntries[i].type & mask)) continue;
matchlen = strlen(buf+startpos);
if (strncasecmp(buf+startpos,helpEntries[i].full,matchlen) == 0) {
tmp = sdsnewlen(buf,startpos);
tmp = sdscat(tmp,helpEntries[i].full);
linenoiseAddCompletion(lc,tmp);
sdsfree(tmp);
}
}
}
/*------------------------------------------------------------------------------
* Networking / parsing
*--------------------------------------------------------------------------- */
@ -252,22 +397,6 @@ static int cliReadReply() {
return REDIS_OK;
}
static void showInteractiveHelp(void) {
printf(
"\n"
"Welcome to redis-cli " REDIS_VERSION "!\n"
"Just type any valid Redis command to see a pretty printed output.\n"
"\n"
"It is possible to quote strings, like in:\n"
" set \"my key\" \"some string \\xff\\n\"\n"
"\n"
"You can find a list of valid Redis commands at\n"
" http://code.google.com/p/redis/wiki/CommandReference\n"
"\n"
"Note: redis-cli supports line editing, use up/down arrows for history."
"\n\n");
}
static int cliSendCommand(int argc, char **argv, int repeat) {
char *command = argv[0];
size_t *argvlen;
@ -279,8 +408,8 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
}
config.raw_output = !strcasecmp(command,"info");
if (!strcasecmp(command,"help")) {
showInteractiveHelp();
if (!strcasecmp(command,"help") || !strcasecmp(command,"?")) {
cliOutputHelp(--argc, ++argv);
return REDIS_OK;
}
if (!strcasecmp(command,"shutdown")) config.shutdown = 1;
@ -412,6 +541,8 @@ static void repl() {
sds *argv;
config.interactive = 1;
linenoiseSetCompletionCallback(completionCallback);
while((line = linenoise(context ? "redis> " : "not connected> ")) != NULL) {
if (line[0] != '\0') {
argv = sdssplitargs(line,&argc);
@ -430,6 +561,8 @@ static void repl() {
config.hostip = sdsnew(argv[1]);
config.hostport = atoi(argv[2]);
cliConnect(1);
} else if (argc == 1 && !strcasecmp(argv[0],"clear")) {
linenoiseClearScreen();
} else {
long long start_time = mstime(), elapsed;
@ -489,6 +622,7 @@ int main(int argc, char **argv) {
config.historyfile = NULL;
config.tty = isatty(fileno(stdout)) || (getenv("FAKETTY") != NULL);
config.mb_sep = '\n';
cliInitHelp();
if (getenv("HOME") != NULL) {
config.historyfile = malloc(256);

111
utils/generate-command-help.rb Executable file
View File

@ -0,0 +1,111 @@
#!/usr/bin/env ruby
GROUPS = [
"generic",
"string",
"list",
"set",
"sorted_set",
"hash",
"pubsub",
"transactions",
"connection",
"server"
].freeze
GROUPS_BY_NAME = Hash[*
GROUPS.each_with_index.map do |n,i|
[n,i]
end.flatten
].freeze
def argument arg
name = arg["name"].is_a?(Array) ? arg["name"].join(" ") : arg["name"]
name = arg["enum"].join "|" if "enum" == arg["type"]
name = arg["command"] + " " + name if arg["command"]
if arg["multiple"]
name = "#{name} [#{name} ...]"
end
if arg["optional"]
name = "[#{name}]"
end
name
end
def arguments command
return "-" unless command["arguments"]
command["arguments"].map do |arg|
argument arg
end.join " "
end
def commands
return @commands if @commands
require "net/http"
require "net/https"
require "json"
require "uri"
url = URI.parse "https://github.com/antirez/redis-doc/raw/master/commands.json"
client = Net::HTTP.new url.host, url.port
client.use_ssl = true
response = client.get url.path
if response.is_a?(Net::HTTPSuccess)
@commands = JSON.parse(response.body)
else
response.error!
end
end
def generate_groups
GROUPS.map do |n|
"\"#{n}\""
end.join(",\n ");
end
def generate_commands
commands.to_a.sort do |x,y|
x[0] <=> y[0]
end.map do |key, command|
group = GROUPS_BY_NAME[command["group"]]
if group.nil?
STDERR.puts "Please update groups array in #{__FILE__}"
raise "Unknown group #{command["group"]}"
end
ret = <<-SPEC
{ "#{key}",
"#{arguments(command)}",
"#{command["summary"]}",
#{group},
"#{command["since"]}" }
SPEC
ret.strip
end.join(",\n ")
end
# Write to stdout
puts <<-HELP_H
/* Automatically generated by #{__FILE__}, do not edit. */
#ifndef __REDIS_HELP_H
#define __REDIS_HELP_H
static char *commandGroups[] = {
#{generate_groups}
};
struct commandHelp {
char *name;
char *params;
char *summary;
int group;
char *since;
} commandHelp[] = {
#{generate_commands}
};
#endif
HELP_H