diff --git a/README.markdown b/README.markdown index 38b6def..f89a214 100644 --- a/README.markdown +++ b/README.markdown @@ -182,6 +182,7 @@ Several content-types are available: * `.png` for `image/png` * `jpg` or `jpeg` for `image/jpeg` * Any other with the `?type=anything/youwant` query string. +* Add a custom separator for list responses with `?sep=,` query string.
 curl -v "http://127.0.0.1:7379/GET/hello.html"
diff --git a/client.c b/client.c
index e19708b..a17cf39 100644
--- a/client.c
+++ b/client.c
@@ -124,6 +124,9 @@ http_client_on_query_string(struct http_parser *parser, const char *at, size_t s
 				|| (key_len == 8 && strncmp(key, "callback", 8) == 0)) {
 				c->jsonp = calloc(1 + val_len, 1);
 				memcpy(c->jsonp, val, val_len);
+			} else if(key_len == 3 && strncmp(key, "sep", 3) == 0) {
+				c->separator = calloc(1 + val_len, 1);
+				memcpy(c->separator, val, val_len);
 			} else if(key_len == 8 && strncmp(key, "filename", 8) == 0) {
 				c->filename = wrap_filename(val, val_len);
 			}
diff --git a/client.h b/client.h
index ae05fce..71369e5 100644
--- a/client.h
+++ b/client.h
@@ -51,6 +51,7 @@ struct http_client {
 
 	char *type; /* forced output content-type */
 	char *jsonp; /* jsonp wrapper */
+	char *separator; /* list separator for raw lists */
 	char *filename; /* content-disposition */
 
 	struct cmd *pub_sub;
diff --git a/cmd.c b/cmd.c
index d193bf8..f761bc4 100644
--- a/cmd.c
+++ b/cmd.c
@@ -48,6 +48,7 @@ cmd_free(struct cmd *c) {
 	free(c->argv_len);
 
 	free(c->jsonp);
+	free(c->separator);
 	free(c->if_none_match);
 	if(c->mime_free) free(c->mime);
 
@@ -113,6 +114,11 @@ cmd_setup(struct cmd *cmd, struct http_client *client) {
 		client->jsonp = NULL;
 	}
 
+	if(client->separator) {	/* transfer pointer ownership */
+		cmd->separator = client->separator;
+		client->separator = NULL;
+	}
+
 	if(client->filename) {	/* transfer pointer ownership */
 		cmd->filename = client->filename;
 		client->filename = NULL;
diff --git a/cmd.h b/cmd.h
index c527bf6..bcd21f3 100644
--- a/cmd.h
+++ b/cmd.h
@@ -34,6 +34,7 @@ struct cmd {
 
 	char *if_none_match; /* used with ETags */
 	char *jsonp; /* jsonp wrapper */
+	char *separator; /* list separator for raw lists */
 	int keep_alive;
 
 	/* various flags */
diff --git a/formats/custom-type.c b/formats/custom-type.c
index 67b7b9c..3fc6e08 100644
--- a/formats/custom-type.c
+++ b/formats/custom-type.c
@@ -8,7 +8,7 @@
 #include 
 
 static char *
-custom_array(const redisReply *r, size_t *sz);
+custom_array(struct cmd *cmd, const redisReply *r, size_t *sz);
 
 void
 custom_type_reply(redisAsyncContext *c, void *r, void *privdata) {
@@ -54,7 +54,7 @@ custom_type_reply(redisAsyncContext *c, void *r, void *privdata) {
 				format_send_reply(cmd, int_buffer, int_len, cmd->mime);
 				return;
 			case REDIS_REPLY_ARRAY:
-				array_out = custom_array(r, &sz);
+				array_out = custom_array(cmd, r, &sz);
 				format_send_reply(cmd, array_out, sz, cmd->mime);
 				free(array_out);
 				return;
@@ -73,10 +73,14 @@ custom_type_reply(redisAsyncContext *c, void *r, void *privdata) {
 }
 
 static char *
-custom_array(const redisReply *r, size_t *sz) {
+custom_array(struct cmd *cmd, const redisReply *r, size_t *sz) {
 
 	unsigned int i;
 	char *ret, *p;
+	size_t sep_len = 0;
+
+	if(cmd->separator)
+		sep_len = strlen(cmd->separator);
 
 	/* compute size */
 	*sz = 0;
@@ -84,6 +88,8 @@ custom_array(const redisReply *r, size_t *sz) {
 		redisReply *e = r->element[i];
 		switch(e->type) {
 			case REDIS_REPLY_STRING:
+				if(sep_len && i != 0)
+					*sz += sep_len;
 				*sz += e->len;
 				break;
 
@@ -98,6 +104,10 @@ custom_array(const redisReply *r, size_t *sz) {
 		redisReply *e = r->element[i];
 		switch(e->type) {
 			case REDIS_REPLY_STRING:
+				if(sep_len && i != 0) {
+					memcpy(p, cmd->separator, sep_len);
+					p += sep_len;
+				}
 				memcpy(p, e->str, e->len);
 				p += e->len;
 				break;
diff --git a/tests/basic.py b/tests/basic.py
index 1d55d2d..99c3565 100755
--- a/tests/basic.py
+++ b/tests/basic.py
@@ -92,6 +92,23 @@ class TestJSON(TestWebdis):
 		self.assertTrue(obj['UNKNOWN'][0] == False)
 		self.assertTrue(isinstance(obj['UNKNOWN'][1], unicode))
 
+class TestCustom(TestWebdis):
+	def test_list(self):
+		"List responses with custom format"
+		self.query('DEL/hello')
+		self.query('RPUSH/hello/a/b/c')
+		f = self.query('LRANGE/hello/0/-1.txt')
+		self.assertTrue(f.headers.getheader('Content-Type') == 'text/plain')
+		self.assertTrue(f.read() == "abc")
+
+	def test_separator(self):
+		"Separator in list responses with custom format"
+		self.query('DEL/hello')
+		self.query('RPUSH/hello/a/b/c')
+		f = self.query('LRANGE/hello/0/-1.txt?sep=--')
+		self.assertTrue(f.headers.getheader('Content-Type') == 'text/plain')
+		self.assertTrue(f.read() == "a--b--c")
+
 class TestRaw(TestWebdis):
 
 	def test_set(self):