]> git.saurik.com Git - redis.git/commitdiff
Merge master and move argument splitting patch to sds.c
authorPieter Noordhuis <pcnoordhuis@gmail.com>
Wed, 25 Aug 2010 07:54:02 +0000 (09:54 +0200)
committerPieter Noordhuis <pcnoordhuis@gmail.com>
Wed, 25 Aug 2010 11:08:43 +0000 (13:08 +0200)
src/redis-cli.c
src/sds.c
tests/integration/redis-cli.tcl [new file with mode: 0644]
tests/test_helper.tcl

index a2a909ba3d5a3ca510871479ef1e4265a50fd523..1bd0798b3f695d213096807963a19a0962acd317 100644 (file)
@@ -56,11 +56,12 @@ static struct config {
     long repeat;
     int dbnum;
     int argn_from_stdin;
-    int interactive;
     int shutdown;
     int monitor_mode;
     int pubsub_mode;
-    int raw_output;
+    int raw_output; /* output mode per command */
+    int tty; /* flag for default output format */
+    char mb_sep;
     char *auth;
     char *historyfile;
 } config;
@@ -111,7 +112,7 @@ static int cliReadSingleLineReply(int fd, int quiet) {
 
     if (reply == NULL) return 1;
     if (!quiet)
-        printf("%s\n", reply);
+        printf("%s", reply);
     sdsfree(reply);
     return 0;
 }
@@ -138,7 +139,7 @@ static void printStringRepr(char *s, int len) {
         }
         s++;
     }
-    printf("\"\n");
+    printf("\"");
 }
 
 static int cliReadBulkReply(int fd) {
@@ -156,7 +157,7 @@ static int cliReadBulkReply(int fd) {
     reply = zmalloc(bulklen);
     anetRead(fd,reply,bulklen);
     anetRead(fd,crlf,2);
-    if (config.raw_output || !isatty(fileno(stdout))) {
+    if (config.raw_output || !config.tty) {
         if (bulklen && fwrite(reply,bulklen,1,stdout) == 0) {
             zfree(reply);
             return 1;
@@ -186,8 +187,9 @@ static int cliReadMultiBulkReply(int fd) {
         printf("(empty list or set)\n");
     }
     while(elements--) {
-        printf("%d. ", c);
+        if (config.tty) printf("%d. ", c);
         if (cliReadReply(fd)) retval = 1;
+        if (elements) printf("%c",config.mb_sep);
         c++;
     }
     return retval;
@@ -210,13 +212,13 @@ static int cliReadReply(int fd) {
     }
     switch(type) {
     case '-':
-        printf("(error) ");
+        if (config.tty) printf("(error) ");
         cliReadSingleLineReply(fd,0);
         return 1;
     case '+':
         return cliReadSingleLineReply(fd,0);
     case ':':
-        printf("(integer) ");
+        if (config.tty) printf("(integer) ");
         return cliReadSingleLineReply(fd,0);
     case '$':
         return cliReadBulkReply(fd);
@@ -286,7 +288,7 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
             printf("Reading messages... (press Ctrl-c to quit)\n");
             while (1) {
                 cliReadReply(fd);
-                printf("\n");
+                printf("\n\n");
             }
         }
 
@@ -294,6 +296,9 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
         if (retval) {
             return retval;
         }
+        if (!config.raw_output && config.tty) {
+            printf("\n");
+        }
     }
     return 0;
 }
@@ -327,7 +332,10 @@ static int parseOptions(int argc, char **argv) {
             config.auth = argv[i+1];
             i++;
         } else if (!strcmp(argv[i],"-i")) {
-            config.interactive = 1;
+            fprintf(stderr,
+"Starting interactive mode using -i is deprecated. Interactive mode is started\n"
+"by default when redis-cli is executed without a command to execute.\n"
+            );
         } else if (!strcmp(argv[i],"-c")) {
             config.argn_from_stdin = 1;
         } else if (!strcmp(argv[i],"-v")) {
@@ -390,7 +398,10 @@ static void repl() {
             argv = sdssplitargs(line,&argc);
             linenoiseHistoryAdd(line);
             if (config.historyfile) linenoiseHistorySave(config.historyfile);
-            if (argc > 0) {
+            if (argv == NULL) {
+                printf("Invalid argument(s)\n");
+                continue;
+            } else if (argc > 0) {
                 if (strcasecmp(argv[0],"quit") == 0 ||
                     strcasecmp(argv[0],"exit") == 0)
                 {
@@ -430,12 +441,13 @@ int main(int argc, char **argv) {
     config.dbnum = 0;
     config.argn_from_stdin = 0;
     config.shutdown = 0;
-    config.interactive = 0;
     config.monitor_mode = 0;
     config.pubsub_mode = 0;
     config.raw_output = 0;
     config.auth = NULL;
     config.historyfile = NULL;
+    config.tty = isatty(fileno(stdout)) || (getenv("FAKETTY") != NULL);
+    config.mb_sep = '\n';
 
     if (getenv("HOME") != NULL) {
         config.historyfile = malloc(256);
@@ -455,8 +467,8 @@ int main(int argc, char **argv) {
         cliSendCommand(2, convertToSds(2, authargv), 1);
     }
 
-    if (argc == 0) config.interactive = 1;
-    if (config.interactive) repl();
+    /* Start interactive mode when no command is provided */
+    if (argc == 0) repl();
 
     argvcopy = convertToSds(argc+1, argv);
     if (config.argn_from_stdin) {
index 4878f8a625714ce4190555953954f5e24fbcd2af..d7d23c45a859a7aba07981637d7e25def7df8bf1 100644 (file)
--- a/src/sds.c
+++ b/src/sds.c
@@ -407,7 +407,7 @@ sds *sdssplitargs(char *line, int *argc) {
         if (*p) {
             /* get a token */
             int inq=0; /* set to 1 if we are in "quotes" */
-            int done = 0;
+            int done=0;
 
             if (current == NULL) current = sdsempty();
             while(!done) {
@@ -426,7 +426,12 @@ sds *sdssplitargs(char *line, int *argc) {
                         }
                         current = sdscatlen(current,&c,1);
                     } else if (*p == '"') {
-                        done = 1;
+                        /* closing quote must be followed by a space */
+                        if (*(p+1) && !isspace(*(p+1))) goto err;
+                        done=1;
+                    } else if (!*p) {
+                        /* unterminated quotes */
+                        goto err;
                     } else {
                         current = sdscatlen(current,p,1);
                     }
@@ -458,4 +463,11 @@ sds *sdssplitargs(char *line, int *argc) {
             return vector;
         }
     }
+
+err:
+    while(*argc--)
+        sdsfree(vector[*argc]);
+    zfree(vector);
+    if (current) sdsfree(current);
+    return NULL;
 }
diff --git a/tests/integration/redis-cli.tcl b/tests/integration/redis-cli.tcl
new file mode 100644 (file)
index 0000000..c495430
--- /dev/null
@@ -0,0 +1,156 @@
+start_server {tags {"cli"}} {
+    proc open_cli {} {
+        set ::env(TERM) dumb
+        set fd [open [format "|src/redis-cli -p %d -n 9" [srv port]] "r+"]
+        fconfigure $fd -buffering none
+        fconfigure $fd -blocking false
+        fconfigure $fd -translation binary
+        assert_equal "redis> " [read_cli $fd]
+        set _ $fd
+    }
+
+    proc close_cli {fd} {
+        close $fd
+    }
+
+    proc read_cli {fd} {
+        set buf [read $fd]
+        while {[string length $buf] == 0} {
+            # wait some time and try again
+            after 10
+            set buf [read $fd]
+        }
+        set _ $buf
+    }
+
+    proc write_cli {fd buf} {
+        puts $fd $buf
+        flush $fd
+    }
+
+    proc run_command {fd cmd} {
+        write_cli $fd $cmd
+        set lines [split [read_cli $fd] "\n"]
+        assert_equal "redis> " [lindex $lines end]
+        join [lrange $lines 0 end-1] "\n"
+    }
+
+    proc test_interactive_cli {name code} {
+        set fd [open_cli]
+        test "Interactive CLI: $name" $code
+        close_cli $fd
+    }
+
+    proc run_nontty_cli {args} {
+        set fd [open [format "|src/redis-cli -p %d -n 9 $args" [srv port]] "r"]
+        fconfigure $fd -buffering none
+        fconfigure $fd -translation binary
+        set resp [read $fd 1048576]
+        close $fd
+        set _ $resp
+    }
+
+    proc test_nontty_cli {name code} {
+        test "Non-interactive non-TTY CLI: $name" $code
+    }
+
+    proc run_tty_cli {args} {
+        set ::env(FAKETTY) 1
+        set resp [run_nontty_cli {*}$args]
+        unset ::env(FAKETTY)
+        set _ $resp
+    }
+
+    proc test_tty_cli {name code} {
+        test "Non-interactive TTY CLI: $name" $code
+    }
+
+    test_interactive_cli "INFO response should be printed raw" {
+        set lines [split [run_command $fd info] "\n"]
+        foreach line $lines {
+            assert [regexp {^[a-z0-9_]+:[a-z0-9_]+} $line]
+        }
+    }
+
+    test_interactive_cli "Status reply" {
+        assert_equal "OK" [run_command $fd "set key foo"]
+    }
+
+    test_interactive_cli "Integer reply" {
+        assert_equal "(integer) 1" [run_command $fd "incr counter"]
+    }
+
+    test_interactive_cli "Bulk reply" {
+        r set key foo
+        assert_equal "\"foo\"" [run_command $fd "get key"]
+    }
+
+    test_interactive_cli "Multi-bulk reply" {
+        r rpush list foo
+        r rpush list bar
+        assert_equal "1. \"foo\"\n2. \"bar\"" [run_command $fd "lrange list 0 -1"]
+    }
+
+    test_interactive_cli "Parsing quotes" {
+        assert_equal "OK" [run_command $fd "set key \"bar\""]
+        assert_equal "bar" [r get key]
+        assert_equal "OK" [run_command $fd "set key \" bar \""]
+        assert_equal " bar " [r get key]
+        assert_equal "OK" [run_command $fd "set key \"\\\"bar\\\"\""]
+        assert_equal "\"bar\"" [r get key]
+        assert_equal "OK" [run_command $fd "set key \"\tbar\t\""]
+        assert_equal "\tbar\t" [r get key]
+
+        # invalid quotation
+        assert_equal "Invalid argument(s)" [run_command $fd "get \"\"key"]
+        assert_equal "Invalid argument(s)" [run_command $fd "get \"key\"x"]
+
+        # quotes after the argument are weird, but should be allowed
+        assert_equal "OK" [run_command $fd "set key\"\" bar"]
+        assert_equal "bar" [r get key]
+    }
+
+    test_tty_cli "Status reply" {
+        assert_equal "OK\n" [run_tty_cli set key bar]
+        assert_equal "bar" [r get key]
+    }
+
+    test_tty_cli "Integer reply" {
+        r del counter
+        assert_equal "(integer) 1\n" [run_tty_cli incr counter]
+    }
+
+    test_tty_cli "Bulk reply" {
+        r set key "tab\tnewline\n"
+        assert_equal "\"tab\\tnewline\\n\"\n" [run_tty_cli get key]
+    }
+
+    test_tty_cli "Multi-bulk reply" {
+        r del list
+        r rpush list foo
+        r rpush list bar
+        assert_equal "1. \"foo\"\n2. \"bar\"\n" [run_tty_cli lrange list 0 -1]
+    }
+
+    test_nontty_cli "Status reply" {
+        assert_equal "OK" [run_nontty_cli set key bar]
+        assert_equal "bar" [r get key]
+    }
+
+    test_nontty_cli "Integer reply" {
+        r del counter
+        assert_equal "1" [run_nontty_cli incr counter]
+    }
+
+    test_nontty_cli "Bulk reply" {
+        r set key "tab\tnewline\n"
+        assert_equal "tab\tnewline\n" [run_nontty_cli get key]
+    }
+
+    test_nontty_cli "Multi-bulk reply" {
+        r del list
+        r rpush list foo
+        r rpush list bar
+        assert_equal "foo\nbar" [run_nontty_cli lrange list 0 -1]
+    }
+}
index ef1f99233434fe9682ff35ad217a5aaded9a2d51..4ae9cc65967f30b83af2cc70193960b7a06e1eec 100644 (file)
@@ -25,7 +25,14 @@ proc execute_tests name {
 # are nested, use "srv 0 pid" to get the pid of the inner server. To access
 # outer servers, use "srv -1 pid" etcetera.
 set ::servers {}
-proc srv {level property} {
+proc srv {args} {
+    set level 0
+    if {[string is integer [lindex $args 0]]} {
+        set level [lindex $args 0]
+        set property [lindex $args 1]
+    } else {
+        set property [lindex $args 0]
+    }
     set srv [lindex $::servers end+$level]
     dict get $srv $property
 }
@@ -88,6 +95,7 @@ proc main {} {
     execute_tests "unit/cas"
     execute_tests "integration/replication"
     execute_tests "integration/aof"
+    execute_tests "integration/redis-cli"
     execute_tests "unit/pubsub"
 
     # run tests with VM enabled