hiredis 0.4.1 → 0.4.2.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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;