]> git.saurik.com Git - redis.git/commitdiff
Merge remote branch 'visionmedia/cli-help' into cli-help
authorPieter Noordhuis <pcnoordhuis@gmail.com>
Fri, 26 Nov 2010 19:46:42 +0000 (20:46 +0100)
committerPieter Noordhuis <pcnoordhuis@gmail.com>
Fri, 26 Nov 2010 19:46:42 +0000 (20:46 +0100)
1  2 
Makefile
src/redis-cli.c

diff --cc Makefile
index 185aab8d2038f404c6f5f8143859ca2e03bed2a2,d4d2c149a522331beff539ede619488367a0211d..b72faa1b18c88b8a491deca9d9ebe5e3c1a95f64
+++ b/Makefile
@@@ -8,12 -8,10 +8,15 @@@ all
  install: dummy
        cd src && $(MAKE) $@
  
 -$(TARGETS) clean:
 +clean:
 +      cd src && $(MAKE) $@
 +      cd deps/hiredis && $(MAKE) $@
 +      cd deps/linenoise && $(MAKE) $@
 +
 +$(TARGETS):
        cd src && $(MAKE) $@
  
+ src/help.h:
+       @./utils/generate-command-help.rb > $@
  dummy:
diff --cc src/redis-cli.c
index eef5ad1e07f7776618842d98af0a6a96944cb819,6ae77545e6903635e90f0b753afffb778a0180d6..5f01a936e0d5851fbdd2a64602f0ede0d5789cd6
  #include <ctype.h>
  #include <errno.h>
  #include <sys/stat.h>
 +#include <sys/time.h>
  
 -#include "anet.h"
 +#include "hiredis.h"
  #include "sds.h"
 -#include "adlist.h"
  #include "zmalloc.h"
  #include "linenoise.h"
+ #include "help.h"
  
  #define REDIS_NOTUSED(V) ((void) V)
  
@@@ -66,217 -65,199 +67,201 @@@ static struct config 
      char *historyfile;
  } config;
  
 -static int cliReadReply(int fd);
  static void usage();
 +char *redisGitSHA1(void);
  
 -/* Connect to the client. If force is not zero the connection is performed
 - * even if there is already a connected socket. */
 -static int cliConnect(int force) {
 -    char err[ANET_ERR_LEN];
 -    static int fd = ANET_ERR;
 -
 -    if (fd == ANET_ERR || force) {
 -        if (force) close(fd);
 -        fd = anetTcpConnect(err,config.hostip,config.hostport);
 -        if (fd == ANET_ERR) {
 -            fprintf(stderr, "Could not connect to Redis at %s:%d: %s", config.hostip, config.hostport, err);
 -            return -1;
 -        }
 -        anetTcpNoDelay(NULL,fd);
 -    }
 -    return fd;
 -}
 +/*------------------------------------------------------------------------------
 + * Utility functions
 + *--------------------------------------------------------------------------- */
  
 -static sds cliReadLine(int fd) {
 -    sds line = sdsempty();
 +static long long mstime(void) {
 +    struct timeval tv;
 +    long long mst;
  
 -    while(1) {
 -        char c;
 -        ssize_t ret;
 -
 -        ret = read(fd,&c,1);
 -        if (ret <= 0) {
 -            sdsfree(line);
 -            return NULL;
 -        } else if ((ret == 0) || (c == '\n')) {
 -            break;
 -        } else {
 -            line = sdscatlen(line,&c,1);
 -        }
 -    }
 -    return sdstrim(line,"\r\n");
 +    gettimeofday(&tv, NULL);
 +    mst = ((long)tv.tv_sec)*1000;
 +    mst += tv.tv_usec/1000;
 +    return mst;
  }
  
 -static int cliReadSingleLineReply(int fd, int quiet) {
 -    sds reply = cliReadLine(fd);
 +/*------------------------------------------------------------------------------
 + * Networking / parsing
 + *--------------------------------------------------------------------------- */
  
 -    if (reply == NULL) return 1;
 -    if (!quiet)
 -        printf("%s", reply);
 -    sdsfree(reply);
 -    return 0;
 -}
 +/* Send AUTH command to the server */
 +static int cliAuth() {
 +    redisReply *reply;
 +    if (config.auth == NULL) return REDIS_OK;
  
 -static void printStringRepr(char *s, int len) {
 -    printf("\"");
 -    while(len--) {
 -        switch(*s) {
 -        case '\\':
 -        case '"':
 -            printf("\\%c",*s);
 -            break;
 -        case '\n': printf("\\n"); break;
 -        case '\r': printf("\\r"); break;
 -        case '\t': printf("\\t"); break;
 -        case '\a': printf("\\a"); break;
 -        case '\b': printf("\\b"); break;
 -        default:
 -            if (isprint(*s))
 -                printf("%c",*s);
 -            else
 -                printf("\\x%02x",(unsigned char)*s);
 -            break;
 -        }
 -        s++;
 +    reply = redisCommand(context,"AUTH %s",config.auth);
 +    if (reply != NULL) {
 +        freeReplyObject(reply);
 +        return REDIS_OK;
      }
 -    printf("\"");
 +    return REDIS_ERR;
  }
  
 -static int cliReadBulkReply(int fd) {
 -    sds replylen = cliReadLine(fd);
 -    char *reply, crlf[2];
 -    int bulklen;
 -
 -    if (replylen == NULL) return 1;
 -    bulklen = atoi(replylen);
 -    if (bulklen == -1) {
 -        sdsfree(replylen);
 -        printf("(nil)\n");
 -        return 0;
 -    }
 -    reply = zmalloc(bulklen);
 -    anetRead(fd,reply,bulklen);
 -    anetRead(fd,crlf,2);
 -    if (config.raw_output || !config.tty) {
 -        if (bulklen && fwrite(reply,bulklen,1,stdout) == 0) {
 -            zfree(reply);
 -            return 1;
 -        }
 -    } else {
 -        /* If you are producing output for the standard output we want
 -         * a more interesting output with quoted characters and so forth */
 -        printStringRepr(reply,bulklen);
 +/* Send SELECT dbnum to the server */
 +static int cliSelect() {
 +    redisReply *reply;
 +    char dbnum[16];
 +    if (config.dbnum == 0) return REDIS_OK;
 +
 +    snprintf(dbnum,sizeof(dbnum),"%d",config.dbnum);
 +    reply = redisCommand(context,"SELECT %s",dbnum);
 +    if (reply != NULL) {
 +        freeReplyObject(reply);
 +        return REDIS_OK;
      }
 -    zfree(reply);
 -    return 0;
 +    return REDIS_ERR;
  }
  
 -static int cliReadMultiBulkReply(int fd) {
 -    sds replylen = cliReadLine(fd);
 -    int elements, c = 1;
 -    int retval = 0;
 +/* Connect to the client. If force is not zero the connection is performed
 + * even if there is already a connected socket. */
 +static int cliConnect(int force) {
 +    if (context == NULL || force) {
 +        if (context != NULL)
 +            redisFree(context);
  
 -    if (replylen == NULL) return 1;
 -    elements = atoi(replylen);
 -    if (elements == -1) {
 -        sdsfree(replylen);
 -        printf("(nil)\n");
 -        return 0;
 -    }
 -    if (elements == 0) {
 -        printf("(empty list or set)\n");
 -    }
 -    while(elements--) {
 -        if (config.tty) printf("%d. ", c);
 -        if (cliReadReply(fd)) retval = 1;
 -        if (elements) printf("%c",config.mb_sep);
 -        c++;
 +        if (config.hostsocket == NULL) {
 +            context = redisConnect(config.hostip,config.hostport);
 +        } else {
 +            context = redisConnectUnix(config.hostsocket);
 +        }
 +
 +        if (context->err) {
 +            fprintf(stderr,"Could not connect to Redis at ");
 +            if (config.hostsocket == NULL)
 +                fprintf(stderr,"%s:%d: %s\n",config.hostip,config.hostport,context->errstr);
 +            else
 +                fprintf(stderr,"%s: %s\n",config.hostsocket,context->errstr);
 +            redisFree(context);
 +            context = NULL;
 +            return REDIS_ERR;
 +        }
 +
 +        /* Do AUTH and select the right DB. */
 +        if (cliAuth() != REDIS_OK)
 +            return REDIS_ERR;
 +        if (cliSelect() != REDIS_OK)
 +            return REDIS_ERR;
      }
 -    return retval;
 +    return REDIS_OK;
  }
  
 -static int cliReadReply(int fd) {
 -    char type;
 -    int nread;
 +static void cliPrintContextErrorAndExit() {
 +    if (context == NULL) return;
 +    fprintf(stderr,"Error: %s\n",context->errstr);
 +    exit(1);
 +}
  
 -    if ((nread = anetRead(fd,&type,1)) <= 0) {
 -        if (config.shutdown) return 0;
 -        if (config.interactive &&
 -            (nread == 0 || (nread == -1 && errno == ECONNRESET)))
 -        {
 -            return ECONNRESET;
 +static sds cliFormatReply(redisReply *r, char *prefix) {
 +    sds out = sdsempty();
 +    switch (r->type) {
 +    case REDIS_REPLY_ERROR:
 +        if (config.tty) out = sdscat(out,"(error) ");
 +        out = sdscatprintf(out,"%s\n", r->str);
 +    break;
 +    case REDIS_REPLY_STATUS:
 +        out = sdscat(out,r->str);
 +        out = sdscat(out,"\n");
 +    break;
 +    case REDIS_REPLY_INTEGER:
 +        if (config.tty) out = sdscat(out,"(integer) ");
 +        out = sdscatprintf(out,"%lld\n",r->integer);
 +    break;
 +    case REDIS_REPLY_STRING:
 +        if (config.raw_output || !config.tty) {
 +            out = sdscatlen(out,r->str,r->len);
          } else {
 -            printf("I/O error while reading from socket: %s",strerror(errno));
 -            exit(1);
 +            /* If you are producing output for the standard output we want
 +             * a more interesting output with quoted characters and so forth */
 +            out = sdscatrepr(out,r->str,r->len);
 +            out = sdscat(out,"\n");
          }
 -    }
 -    switch(type) {
 -    case '-':
 -        if (config.tty) printf("(error) ");
 -        cliReadSingleLineReply(fd,0);
 -        return 1;
 -    case '+':
 -        return cliReadSingleLineReply(fd,0);
 -    case ':':
 -        if (config.tty) printf("(integer) ");
 -        return cliReadSingleLineReply(fd,0);
 -    case '$':
 -        return cliReadBulkReply(fd);
 -    case '*':
 -        return cliReadMultiBulkReply(fd);
 +    break;
 +    case REDIS_REPLY_NIL:
 +        out = sdscat(out,"(nil)\n");
 +    break;
 +    case REDIS_REPLY_ARRAY:
 +        if (r->elements == 0) {
 +            out = sdscat(out,"(empty list or set)\n");
 +        } else {
 +            unsigned int i, idxlen = 0;
 +            char _prefixlen[16];
 +            char _prefixfmt[16];
 +            sds _prefix;
 +            sds tmp;
 +
 +            /* Calculate chars needed to represent the largest index */
 +            i = r->elements;
 +            do {
 +                idxlen++;
 +                i /= 10;
 +            } while(i);
 +
 +            /* Prefix for nested multi bulks should grow with idxlen+2 spaces */
 +            memset(_prefixlen,' ',idxlen+2);
 +            _prefixlen[idxlen+2] = '\0';
 +            _prefix = sdscat(sdsnew(prefix),_prefixlen);
 +
 +            /* Setup prefix format for every entry */
 +            snprintf(_prefixfmt,sizeof(_prefixfmt),"%%s%%%dd) ",idxlen);
 +
 +            for (i = 0; i < r->elements; i++) {
 +                /* Don't use the prefix for the first element, as the parent
 +                 * caller already prepended the index number. */
 +                out = sdscatprintf(out,_prefixfmt,i == 0 ? "" : prefix,i+1);
 +
 +                /* Format the multi bulk entry */
 +                tmp = cliFormatReply(r->element[i],_prefix);
 +                out = sdscatlen(out,tmp,sdslen(tmp));
 +                sdsfree(tmp);
 +            }
 +            sdsfree(_prefix);
 +        }
 +    break;
      default:
 -        printf("protocol error, got '%c' as reply type byte", type);
 -        return 1;
 +        fprintf(stderr,"Unknown reply type: %d\n", r->type);
 +        exit(1);
      }
 +    return out;
  }
  
 -static int selectDb(int fd) {
 -    int retval;
 -    sds cmd;
 -    char type;
 -
 -    if (config.dbnum == 0)
 -        return 0;
 -
 -    cmd = sdsempty();
 -    cmd = sdscatprintf(cmd,"SELECT %d\r\n",config.dbnum);
 -    anetWrite(fd,cmd,sdslen(cmd));
 -    anetRead(fd,&type,1);
 -    if (type <= 0 || type != '+') return 1;
 -    retval = cliReadSingleLineReply(fd,1);
 -    if (retval) {
 -        return retval;
 +static int cliReadReply() {
 +    redisReply *reply;
 +    sds out;
 +
 +    if (redisGetReply(context,(void**)&reply) != REDIS_OK) {
 +        if (config.shutdown)
 +            return REDIS_OK;
 +        if (config.interactive) {
 +            /* Filter cases where we should reconnect */
 +            if (context->err == REDIS_ERR_IO && errno == ECONNRESET)
 +                return REDIS_ERR;
 +            if (context->err == REDIS_ERR_EOF)
 +                return REDIS_ERR;
 +        }
 +        cliPrintContextErrorAndExit();
 +        return REDIS_ERR; /* avoid compiler warning */
      }
 -    return 0;
 +
 +    out = cliFormatReply(reply,"");
 +    freeReplyObject(reply);
 +    fwrite(out,sdslen(out),1,stdout);
 +    sdsfree(out);
 +    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];
 -    int fd, j, retval = 0;
 -    sds cmd;
 +    size_t *argvlen;
 +    int j;
  
      config.raw_output = !strcasecmp(command,"info");
      if (!strcasecmp(command,"help")) {
-         showInteractiveHelp();
+         output_help(--argc, ++argv);
 -        return 0;
 +        return REDIS_OK;
      }
      if (!strcasecmp(command,"shutdown")) config.shutdown = 1;
      if (!strcasecmp(command,"monitor")) config.monitor_mode = 1;