hiredis 0.4.1 → 0.4.2.pre

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -40,7 +40,6 @@ task :default => [:rebuild, :test]
40
40
 
41
41
  desc "Run tests"
42
42
  Rake::TestTask.new(:test) do |t|
43
- t.libs << "test"
44
43
  t.pattern = 'test/**/*_test.rb'
45
44
  t.verbose = true
46
45
  end
@@ -32,14 +32,17 @@ module Hiredis
32
32
  method_index[?*] = :process_multi_bulk_reply
33
33
  METHOD_INDEX = method_index.freeze
34
34
 
35
- attr_accessor :parent, :child
35
+ attr_reader :parent
36
+ attr_reader :depth
36
37
  attr_accessor :multi_bulk
37
38
 
38
39
  def initialize(buffer, parent = nil, depth = 0)
39
- @buffer, @parent = buffer, parent
40
+ @buffer, @parent, @depth = buffer, parent, depth
41
+ end
40
42
 
41
- # Require 3 nested tasks
42
- @child = Task.new(@buffer, self, depth + 1) if depth < 2
43
+ # Note: task depth is not checked.
44
+ def child
45
+ @child ||= Task.new(@buffer, self, depth + 1)
43
46
  end
44
47
 
45
48
  def root
@@ -1,3 +1,3 @@
1
1
  module Hiredis
2
- VERSION = "0.4.1"
2
+ VERSION = "0.4.2.pre"
3
3
  end
@@ -1,58 +1,46 @@
1
1
  # Hiredis Makefile
2
- # Copyright (C) 2010 Salvatore Sanfilippo <antirez at gmail dot com>
2
+ # Copyright (C) 2010-2011 Salvatore Sanfilippo <antirez at gmail dot com>
3
+ # Copyright (C) 2010-2011 Pieter Noordhuis <pcnoordhuis at gmail dot com>
3
4
  # This file is released under the BSD license, see the COPYING file
4
5
 
5
6
  OBJ=net.o hiredis.o sds.o async.o
6
7
  BINS=hiredis-example hiredis-test
7
8
  LIBNAME=libhiredis
8
9
 
9
- uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
10
+ HIREDIS_MAJOR=0
11
+ HIREDIS_MINOR=10
12
+
13
+ # Fallback to gcc when $CC is not in $PATH.
14
+ CC:=$(shell sh -c 'type $(CC) >/dev/null 2>/dev/null && echo $(CC) || echo gcc')
10
15
  OPTIMIZATION?=-O3
16
+ WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings
17
+ DEBUG?= -g -ggdb
18
+ REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CFLAGS) $(WARNINGS) $(DEBUG)
19
+ REAL_LDFLAGS=$(LDFLAGS)
20
+
21
+ DYLIBSUFFIX=so
22
+ STLIBSUFFIX=a
23
+ DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR).$(HIREDIS_MINOR)
24
+ DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)
25
+ DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX)
26
+ DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
27
+ STLIBNAME=$(LIBNAME).$(STLIBSUFFIX)
28
+ STLIB_MAKE_CMD=ar rcs $(STLIBNAME)
29
+
30
+ # Platform-specific overrides
31
+ uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
11
32
  ifeq ($(uname_S),SunOS)
12
- CFLAGS?=$(OPTIMIZATION) -fPIC -Wall -W -D__EXTENSIONS__ -D_XPG6 $(ARCH) $(PROF)
13
- CCLINK?=-ldl -lnsl -lsocket -lm -lpthread
14
- LDFLAGS?=-L.
15
- DYLIBSUFFIX=so
16
- STLIBSUFFIX=a
17
- DYLIBNAME?=$(LIBNAME).$(DYLIBSUFFIX)
18
- DYLIB_MAKE_CMD?=$(CC) -G -o $(DYLIBNAME)
19
- STLIBNAME?=$(LIBNAME).$(STLIBSUFFIX)
20
- STLIB_MAKE_CMD?=ar rcs $(STLIBNAME)
21
- else
33
+ REAL_LDFLAGS+= -ldl -lnsl -lsocket
34
+ DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS)
35
+ INSTALL= cp -r
36
+ endif
22
37
  ifeq ($(uname_S),Darwin)
23
- CFLAGS?=$(OPTIMIZATION) -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings $(ARCH) $(PROF)
24
- CCLINK?=-lm -pthread
25
- LDFLAGS?=-L.
26
- OBJARCH?=-arch i386 -arch x86_64
27
38
  DYLIBSUFFIX=dylib
28
- STLIBSUFFIX=a
29
- DYLIBNAME?=$(LIBNAME).$(DYLIBSUFFIX)
30
- DYLIB_MAKE_CMD?=libtool -dynamic -o $(DYLIBNAME) -lm $(DEBUG) -
31
- STLIBNAME?=$(LIBNAME).$(STLIBSUFFIX)
32
- STLIB_MAKE_CMD?=libtool -static -o $(STLIBNAME) -
33
- else
34
- CFLAGS?=$(OPTIMIZATION) -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings $(ARCH) $(PROF)
35
- CCLINK?=-lm -pthread
36
- LDFLAGS?=-L.
37
- DYLIBSUFFIX=so
38
- STLIBSUFFIX=a
39
- DYLIBNAME?=$(LIBNAME).$(DYLIBSUFFIX)
40
- DYLIB_MAKE_CMD?=gcc -shared -Wl,-soname,$(DYLIBNAME) -o $(DYLIBNAME)
41
- STLIBNAME?=$(LIBNAME).$(STLIBSUFFIX)
42
- STLIB_MAKE_CMD?=ar rcs $(STLIBNAME)
43
- endif
39
+ DYLIB_MINOR_NAME=$(LIBNAME).$(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(DYLIBSUFFIX)
40
+ DYLIB_MAJOR_NAME=$(LIBNAME).$(HIREDIS_MAJOR).$(DYLIBSUFFIX)
41
+ DYLIB_MAKE_CMD=$(CC) -shared -Wl,-install_name,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
44
42
  endif
45
43
 
46
- CCOPT= $(CFLAGS) $(CCLINK)
47
- DEBUG?= -g -ggdb
48
-
49
- PREFIX?=/usr/local
50
- INCLUDE_PATH?=include/hiredis
51
- LIBRARY_PATH?=lib
52
- INSTALL_INCLUDE_PATH= $(PREFIX)/$(INCLUDE_PATH)
53
- INSTALL_LIBRARY_PATH= $(PREFIX)/$(LIBRARY_PATH)
54
- INSTALL= cp -a
55
-
56
44
  all: $(DYLIBNAME) $(BINS)
57
45
 
58
46
  # Deps (use make dep to generate this)
@@ -74,10 +62,10 @@ static: $(STLIBNAME)
74
62
 
75
63
  # Binaries:
76
64
  hiredis-example-libevent: example-libevent.c adapters/libevent.h $(STLIBNAME)
77
- $(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) $(STLIBNAME) example-libevent.c -levent
65
+ $(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -levent example-libevent.c $(STLIBNAME)
78
66
 
79
67
  hiredis-example-libev: example-libev.c adapters/libev.h $(STLIBNAME)
80
- $(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) $(STLIBNAME) example-libev.c -lev
68
+ $(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -lev example-libev.c $(STLIBNAME)
81
69
 
82
70
  ifndef AE_DIR
83
71
  hiredis-example-ae:
@@ -85,17 +73,29 @@ hiredis-example-ae:
85
73
  @false
86
74
  else
87
75
  hiredis-example-ae: example-ae.c adapters/ae.h $(STLIBNAME)
88
- $(CC) -o $@ $(CCOPT) $(DEBUG) -I$(AE_DIR) $(LDFLAGS) $(STLIBNAME) example-ae.c $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o
76
+ $(CC) -o $@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I$(AE_DIR) $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o example-ae.c $(STLIBNAME)
89
77
  endif
90
78
 
91
79
  hiredis-%: %.o $(STLIBNAME)
92
- $(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) $(STLIBNAME) $<
80
+ $(CC) -o $@ $(REAL_LDFLAGS) $< $(STLIBNAME)
93
81
 
94
82
  test: hiredis-test
95
83
  ./hiredis-test
96
84
 
85
+ check: hiredis-test
86
+ echo \
87
+ "daemonize yes\n" \
88
+ "pidfile /tmp/hiredis-test-redis.pid\n" \
89
+ "port 56379\n" \
90
+ "bind 127.0.0.1\n" \
91
+ "unixsocket /tmp/hiredis-test-redis.sock" \
92
+ | redis-server -
93
+ ./hiredis-test -h 127.0.0.1 -p 56379 -s /tmp/hiredis-test-redis.sock || \
94
+ ( kill `cat /tmp/hiredis-test-redis.pid` && false )
95
+ kill `cat /tmp/hiredis-test-redis.pid`
96
+
97
97
  .c.o:
98
- $(CC) -std=c99 -pedantic -c $(CFLAGS) $(OBJARCH) $(DEBUG) $(COMPILE_TIME) $<
98
+ $(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $<
99
99
 
100
100
  clean:
101
101
  rm -rf $(DYLIBNAME) $(STLIBNAME) $(BINS) hiredis-example* *.o *.gcda *.gcno *.gcov
@@ -103,22 +103,46 @@ clean:
103
103
  dep:
104
104
  $(CC) -MM *.c
105
105
 
106
+ # Installation related variables and target
107
+ PREFIX?=/usr/local
108
+ INCLUDE_PATH?=include/hiredis
109
+ LIBRARY_PATH?=lib
110
+ INSTALL_INCLUDE_PATH= $(PREFIX)/$(INCLUDE_PATH)
111
+ INSTALL_LIBRARY_PATH= $(PREFIX)/$(LIBRARY_PATH)
112
+
113
+ ifeq ($(uname_S),SunOS)
114
+ INSTALL?= cp -r
115
+ endif
116
+
117
+ INSTALL?= cp -a
118
+
106
119
  install: $(DYLIBNAME) $(STLIBNAME)
107
120
  mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH)
108
121
  $(INSTALL) hiredis.h async.h adapters $(INSTALL_INCLUDE_PATH)
109
- $(INSTALL) $(DYLIBNAME) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)
122
+ $(INSTALL) $(DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(DYLIB_MINOR_NAME)
123
+ cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MINOR_NAME) $(DYLIB_MAJOR_NAME)
124
+ cd $(INSTALL_LIBRARY_PATH) && ln -sf $(DYLIB_MAJOR_NAME) $(DYLIBNAME)
125
+ $(INSTALL) $(STLIBNAME) $(INSTALL_LIBRARY_PATH)
110
126
 
111
127
  32bit:
112
128
  @echo ""
113
- @echo "WARNING: if it fails under Linux you probably need to install libc6-dev-i386"
129
+ @echo "WARNING: if this fails under Linux you probably need to install libc6-dev-i386"
114
130
  @echo ""
115
- $(MAKE) ARCH="-m32"
131
+ $(MAKE) CFLAGS="-m32" LDFLAGS="-m32"
116
132
 
117
133
  gprof:
118
- $(MAKE) PROF="-pg"
134
+ $(MAKE) CFLAGS="-pg" LDFLAGS="-pg"
119
135
 
120
136
  gcov:
121
- $(MAKE) PROF="-fprofile-arcs -ftest-coverage"
137
+ $(MAKE) CFLAGS="-fprofile-arcs -ftest-coverage" LDFLAGS="-fprofile-arcs"
138
+
139
+ coverage: gcov
140
+ make check
141
+ mkdir -p tmp/lcov
142
+ lcov -d . -c -o tmp/lcov/hiredis.info
143
+ genhtml --legend -o tmp/lcov/report tmp/lcov/hiredis.info
122
144
 
123
145
  noopt:
124
146
  $(MAKE) OPTIMIZATION=""
147
+
148
+ .PHONY: all test check clean dep install 32bit gprof gcov noopt
@@ -29,14 +29,34 @@
29
29
  * POSSIBILITY OF SUCH DAMAGE.
30
30
  */
31
31
 
32
+ #include "fmacros.h"
33
+ #include <stdlib.h>
32
34
  #include <string.h>
33
35
  #include <strings.h>
34
36
  #include <assert.h>
35
37
  #include <ctype.h>
38
+ #include <errno.h>
36
39
  #include "async.h"
40
+ #include "net.h"
37
41
  #include "dict.c"
38
42
  #include "sds.h"
39
43
 
44
+ #define _EL_ADD_READ(ctx) do { \
45
+ if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \
46
+ } while(0)
47
+ #define _EL_DEL_READ(ctx) do { \
48
+ if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \
49
+ } while(0)
50
+ #define _EL_ADD_WRITE(ctx) do { \
51
+ if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \
52
+ } while(0)
53
+ #define _EL_DEL_WRITE(ctx) do { \
54
+ if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \
55
+ } while(0)
56
+ #define _EL_CLEANUP(ctx) do { \
57
+ if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \
58
+ } while(0);
59
+
40
60
  /* Forward declaration of function in hiredis.c */
41
61
  void __redisAppendCommand(redisContext *c, char *cmd, size_t len);
42
62
 
@@ -142,7 +162,7 @@ int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn
142
162
  /* The common way to detect an established connection is to wait for
143
163
  * the first write event to be fired. This assumes the related event
144
164
  * library functions are already set. */
145
- if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data);
165
+ _EL_ADD_WRITE(ac);
146
166
  return REDIS_OK;
147
167
  }
148
168
  return REDIS_ERR;
@@ -230,7 +250,7 @@ static void __redisAsyncFree(redisAsyncContext *ac) {
230
250
  dictRelease(ac->sub.patterns);
231
251
 
232
252
  /* Signal event lib to clean up */
233
- if (ac->ev.cleanup) ac->ev.cleanup(ac->ev.data);
253
+ _EL_CLEANUP(ac);
234
254
 
235
255
  /* Execute disconnect callback. When redisAsyncFree() initiated destroying
236
256
  * this context, the status will always be REDIS_OK. */
@@ -361,7 +381,20 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
361
381
  /* Even if the context is subscribed, pending regular callbacks will
362
382
  * get a reply before pub/sub messages arrive. */
363
383
  if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) {
364
- /* No more regular callbacks, the context *must* be subscribed. */
384
+ /* A spontaneous reply in a not-subscribed context can only be the
385
+ * error reply that is sent when a new connection exceeds the
386
+ * maximum number of allowed connections on the server side. This
387
+ * is seen as an error instead of a regular reply because the
388
+ * server closes the connection after sending it. To prevent the
389
+ * error from being overwritten by an EOF error the connection is
390
+ * closed here. See issue #43. */
391
+ if ( !(c->flags & REDIS_SUBSCRIBED) && ((redisReply*)reply)->type == REDIS_REPLY_ERROR ) {
392
+ c->err = REDIS_ERR_OTHER;
393
+ snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str);
394
+ __redisAsyncDisconnect(ac);
395
+ return;
396
+ }
397
+ /* No more regular callbacks and no errors, the context *must* be subscribed. */
365
398
  assert(c->flags & REDIS_SUBSCRIBED);
366
399
  __redisGetSubscribeCallback(ac,reply,&cb);
367
400
  }
@@ -389,17 +422,48 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
389
422
  __redisAsyncDisconnect(ac);
390
423
  }
391
424
 
425
+ /* Internal helper function to detect socket status the first time a read or
426
+ * write event fires. When connecting was not succesful, the connect callback
427
+ * is called with a REDIS_ERR status and the context is free'd. */
428
+ static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
429
+ redisContext *c = &(ac->c);
430
+
431
+ if (redisCheckSocketError(c,c->fd) == REDIS_ERR) {
432
+ /* Try again later when connect(2) is still in progress. */
433
+ if (errno == EINPROGRESS)
434
+ return REDIS_OK;
435
+
436
+ if (ac->onConnect) ac->onConnect(ac,REDIS_ERR);
437
+ __redisAsyncDisconnect(ac);
438
+ return REDIS_ERR;
439
+ }
440
+
441
+ /* Mark context as connected. */
442
+ c->flags |= REDIS_CONNECTED;
443
+ if (ac->onConnect) ac->onConnect(ac,REDIS_OK);
444
+ return REDIS_OK;
445
+ }
446
+
392
447
  /* This function should be called when the socket is readable.
393
448
  * It processes all replies that can be read and executes their callbacks.
394
449
  */
395
450
  void redisAsyncHandleRead(redisAsyncContext *ac) {
396
451
  redisContext *c = &(ac->c);
397
452
 
453
+ if (!(c->flags & REDIS_CONNECTED)) {
454
+ /* Abort connect was not successful. */
455
+ if (__redisAsyncHandleConnect(ac) != REDIS_OK)
456
+ return;
457
+ /* Try again later when the context is still not connected. */
458
+ if (!(c->flags & REDIS_CONNECTED))
459
+ return;
460
+ }
461
+
398
462
  if (redisBufferRead(c) == REDIS_ERR) {
399
463
  __redisAsyncDisconnect(ac);
400
464
  } else {
401
465
  /* Always re-schedule reads */
402
- if (ac->ev.addRead) ac->ev.addRead(ac->ev.data);
466
+ _EL_ADD_READ(ac);
403
467
  redisProcessCallbacks(ac);
404
468
  }
405
469
  }
@@ -408,24 +472,26 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
408
472
  redisContext *c = &(ac->c);
409
473
  int done = 0;
410
474
 
475
+ if (!(c->flags & REDIS_CONNECTED)) {
476
+ /* Abort connect was not successful. */
477
+ if (__redisAsyncHandleConnect(ac) != REDIS_OK)
478
+ return;
479
+ /* Try again later when the context is still not connected. */
480
+ if (!(c->flags & REDIS_CONNECTED))
481
+ return;
482
+ }
483
+
411
484
  if (redisBufferWrite(c,&done) == REDIS_ERR) {
412
485
  __redisAsyncDisconnect(ac);
413
486
  } else {
414
487
  /* Continue writing when not done, stop writing otherwise */
415
- if (!done) {
416
- if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data);
417
- } else {
418
- if (ac->ev.delWrite) ac->ev.delWrite(ac->ev.data);
419
- }
488
+ if (!done)
489
+ _EL_ADD_WRITE(ac);
490
+ else
491
+ _EL_DEL_WRITE(ac);
420
492
 
421
493
  /* Always schedule reads after writes */
422
- if (ac->ev.addRead) ac->ev.addRead(ac->ev.data);
423
-
424
- /* Fire onConnect when this is the first write event. */
425
- if (!(c->flags & REDIS_CONNECTED)) {
426
- c->flags |= REDIS_CONNECTED;
427
- if (ac->onConnect) ac->onConnect(ac);
428
- }
494
+ _EL_ADD_READ(ac);
429
495
  }
430
496
  }
431
497
 
@@ -503,7 +569,7 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
503
569
  __redisAppendCommand(c,cmd,len);
504
570
 
505
571
  /* Always schedule a write when the write buffer is non-empty */
506
- if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data);
572
+ _EL_ADD_WRITE(ac);
507
573
 
508
574
  return REDIS_OK;
509
575
  }
@@ -55,7 +55,7 @@ typedef struct redisCallbackList {
55
55
 
56
56
  /* Connection callback prototypes */
57
57
  typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status);
58
- typedef void (redisConnectCallback)(const struct redisAsyncContext*);
58
+ typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status);
59
59
 
60
60
  /* Context for an async connection to Redis */
61
61
  typedef struct redisAsyncContext {
@@ -1,12 +1,14 @@
1
1
  #ifndef __HIREDIS_FMACRO_H
2
2
  #define __HIREDIS_FMACRO_H
3
3
 
4
- #ifndef _BSD_SOURCE
4
+ #if !defined(_BSD_SOURCE)
5
5
  #define _BSD_SOURCE
6
6
  #endif
7
7
 
8
- #ifdef __linux__
9
- #define _XOPEN_SOURCE 700
8
+ #if defined(__sun__)
9
+ #define _POSIX_C_SOURCE 200112L
10
+ #elif defined(__linux__)
11
+ #define _XOPEN_SOURCE 600
10
12
  #else
11
13
  #define _XOPEN_SOURCE
12
14
  #endif
@@ -77,7 +77,7 @@ void freeReplyObject(void *reply) {
77
77
  case REDIS_REPLY_INTEGER:
78
78
  break; /* Nothing to free */
79
79
  case REDIS_REPLY_ARRAY:
80
- if (r->elements > 0 && r->element != NULL) {
80
+ if (r->element != NULL) {
81
81
  for (j = 0; j < r->elements; j++)
82
82
  if (r->element[j] != NULL)
83
83
  freeReplyObject(r->element[j]);
@@ -133,10 +133,12 @@ static void *createArrayObject(const redisReadTask *task, int elements) {
133
133
  if (r == NULL)
134
134
  return NULL;
135
135
 
136
- r->element = calloc(elements,sizeof(redisReply*));
137
- if (r->element == NULL) {
138
- freeReplyObject(r);
139
- return NULL;
136
+ if (elements > 0) {
137
+ r->element = calloc(elements,sizeof(redisReply*));
138
+ if (r->element == NULL) {
139
+ freeReplyObject(r);
140
+ return NULL;
141
+ }
140
142
  }
141
143
 
142
144
  r->elements = elements;
@@ -444,10 +446,10 @@ static int processMultiBulkItem(redisReader *r) {
444
446
  long elements;
445
447
  int root = 0;
446
448
 
447
- /* Set error for nested multi bulks with depth > 1 */
448
- if (r->ridx == 2) {
449
+ /* Set error for nested multi bulks with depth > 2 */
450
+ if (r->ridx == 3) {
449
451
  __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
450
- "No support for nested multi bulk replies with depth > 1");
452
+ "No support for nested multi bulk replies with depth > 2");
451
453
  return REDIS_ERR;
452
454
  }
453
455
 
@@ -768,33 +770,79 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
768
770
  while (*_p != '\0' && isdigit(*_p)) _p++;
769
771
  }
770
772
 
771
- /* Modifiers */
772
- if (*_p != '\0') {
773
- if (*_p == 'h' || *_p == 'l') {
774
- /* Allow a single repetition for these modifiers */
775
- if (_p[0] == _p[1]) _p++;
776
- _p++;
773
+ /* Copy va_list before consuming with va_arg */
774
+ va_copy(_cpy,ap);
775
+
776
+ /* Integer conversion (without modifiers) */
777
+ if (strchr("diouxX",*_p) != NULL) {
778
+ va_arg(ap,int);
779
+ goto fmt_valid;
780
+ }
781
+
782
+ /* Double conversion (without modifiers) */
783
+ if (strchr("eEfFgGaA",*_p) != NULL) {
784
+ va_arg(ap,double);
785
+ goto fmt_valid;
786
+ }
787
+
788
+ /* Size: char */
789
+ if (_p[0] == 'h' && _p[1] == 'h') {
790
+ _p += 2;
791
+ if (*_p != '\0' && strchr("diouxX",*_p) != NULL) {
792
+ va_arg(ap,int); /* char gets promoted to int */
793
+ goto fmt_valid;
777
794
  }
795
+ goto fmt_invalid;
778
796
  }
779
797
 
780
- /* Conversion specifier */
781
- if (*_p != '\0' && strchr("diouxXeEfFgGaA",*_p) != NULL) {
782
- _l = (_p+1)-c;
783
- if (_l < sizeof(_format)-2) {
784
- memcpy(_format,c,_l);
785
- _format[_l] = '\0';
786
- va_copy(_cpy,ap);
787
- newarg = sdscatvprintf(curarg,_format,_cpy);
788
- va_end(_cpy);
789
-
790
- /* Update current position (note: outer blocks
791
- * increment c twice so compensate here) */
792
- c = _p-1;
798
+ /* Size: short */
799
+ if (_p[0] == 'h') {
800
+ _p += 1;
801
+ if (*_p != '\0' && strchr("diouxX",*_p) != NULL) {
802
+ va_arg(ap,int); /* short gets promoted to int */
803
+ goto fmt_valid;
793
804
  }
805
+ goto fmt_invalid;
806
+ }
807
+
808
+ /* Size: long long */
809
+ if (_p[0] == 'l' && _p[1] == 'l') {
810
+ _p += 2;
811
+ if (*_p != '\0' && strchr("diouxX",*_p) != NULL) {
812
+ va_arg(ap,long long);
813
+ goto fmt_valid;
814
+ }
815
+ goto fmt_invalid;
816
+ }
817
+
818
+ /* Size: long */
819
+ if (_p[0] == 'l') {
820
+ _p += 1;
821
+ if (*_p != '\0' && strchr("diouxX",*_p) != NULL) {
822
+ va_arg(ap,long);
823
+ goto fmt_valid;
824
+ }
825
+ goto fmt_invalid;
826
+ }
827
+
828
+ fmt_invalid:
829
+ va_end(_cpy);
830
+ goto err;
831
+
832
+ fmt_valid:
833
+ _l = (_p+1)-c;
834
+ if (_l < sizeof(_format)-2) {
835
+ memcpy(_format,c,_l);
836
+ _format[_l] = '\0';
837
+ newarg = sdscatvprintf(curarg,_format,_cpy);
838
+
839
+ /* Update current position (note: outer blocks
840
+ * increment c twice so compensate here) */
841
+ c = _p-1;
794
842
  }
795
843
 
796
- /* Consume and discard vararg */
797
- va_arg(ap,void);
844
+ va_end(_cpy);
845
+ break;
798
846
  }
799
847
  }
800
848
 
@@ -1049,10 +1097,10 @@ int redisBufferRead(redisContext *c) {
1049
1097
  *
1050
1098
  * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was
1051
1099
  * succesfully written to the socket. When the buffer is empty after the
1052
- * write operation, "wdone" is set to 1 (if given).
1100
+ * write operation, "done" is set to 1 (if given).
1053
1101
  *
1054
1102
  * Returns REDIS_ERR if an error occured trying to write and sets
1055
- * c->error to hold the appropriate error string.
1103
+ * c->errstr to hold the appropriate error string.
1056
1104
  */
1057
1105
  int redisBufferWrite(redisContext *c, int *done) {
1058
1106
  int nwritten;