]> git.saurik.com Git - redis.git/blame - deps/hiredis/hiredis.c
Use custom string2ll and strchr
[redis.git] / deps / hiredis / hiredis.c
CommitLineData
24f753a8
PN
1/*
2 * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
a1e97d69
PN
3 * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
4 *
24f753a8
PN
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * * Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Redis nor the names of its contributors may be used
16 * to endorse or promote products derived from this software without
17 * specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <string.h>
33#include <stdlib.h>
34#include <unistd.h>
35#include <assert.h>
36#include <errno.h>
a1e97d69 37#include <ctype.h>
24f753a8
PN
38
39#include "hiredis.h"
40#include "net.h"
41#include "sds.h"
42#include "util.h"
43
44typedef struct redisReader {
45 struct redisReplyObjectFunctions *fn;
46 sds error; /* holds optional error */
47 void *reply; /* holds temporary reply */
48
49 sds buf; /* read buffer */
a1e97d69
PN
50 size_t pos; /* buffer cursor */
51 size_t len; /* buffer length */
24f753a8
PN
52
53 redisReadTask rstack[3]; /* stack of read tasks */
54 int ridx; /* index of stack */
a1e97d69 55 void *privdata; /* user-settable arbitrary field */
24f753a8
PN
56} redisReader;
57
58static redisReply *createReplyObject(int type);
59static void *createStringObject(const redisReadTask *task, char *str, size_t len);
60static void *createArrayObject(const redisReadTask *task, int elements);
61static void *createIntegerObject(const redisReadTask *task, long long value);
62static void *createNilObject(const redisReadTask *task);
63static void redisSetReplyReaderError(redisReader *r, sds err);
64
65/* Default set of functions to build the reply. */
66static redisReplyObjectFunctions defaultFunctions = {
67 createStringObject,
68 createArrayObject,
69 createIntegerObject,
70 createNilObject,
71 freeReplyObject
72};
73
74/* Create a reply object */
75static redisReply *createReplyObject(int type) {
a1e97d69 76 redisReply *r = malloc(sizeof(*r));
24f753a8
PN
77
78 if (!r) redisOOM();
79 r->type = type;
80 return r;
81}
82
83/* Free a reply object */
84void freeReplyObject(void *reply) {
85 redisReply *r = reply;
86 size_t j;
87
88 switch(r->type) {
89 case REDIS_REPLY_INTEGER:
90 break; /* Nothing to free */
91 case REDIS_REPLY_ARRAY:
92 for (j = 0; j < r->elements; j++)
93 if (r->element[j]) freeReplyObject(r->element[j]);
94 free(r->element);
95 break;
a1e97d69
PN
96 case REDIS_REPLY_ERROR:
97 case REDIS_REPLY_STATUS:
98 case REDIS_REPLY_STRING:
99 free(r->str);
24f753a8
PN
100 break;
101 }
102 free(r);
103}
104
105static void *createStringObject(const redisReadTask *task, char *str, size_t len) {
106 redisReply *r = createReplyObject(task->type);
107 char *value = malloc(len+1);
108 if (!value) redisOOM();
109 assert(task->type == REDIS_REPLY_ERROR ||
110 task->type == REDIS_REPLY_STATUS ||
111 task->type == REDIS_REPLY_STRING);
112
113 /* Copy string value */
114 memcpy(value,str,len);
115 value[len] = '\0';
116 r->str = value;
117 r->len = len;
118
119 if (task->parent) {
a1e97d69 120 redisReply *parent = task->parent->obj;
24f753a8
PN
121 assert(parent->type == REDIS_REPLY_ARRAY);
122 parent->element[task->idx] = r;
123 }
124 return r;
125}
126
127static void *createArrayObject(const redisReadTask *task, int elements) {
128 redisReply *r = createReplyObject(REDIS_REPLY_ARRAY);
129 r->elements = elements;
130 if ((r->element = calloc(sizeof(redisReply*),elements)) == NULL)
131 redisOOM();
132 if (task->parent) {
a1e97d69 133 redisReply *parent = task->parent->obj;
24f753a8
PN
134 assert(parent->type == REDIS_REPLY_ARRAY);
135 parent->element[task->idx] = r;
136 }
137 return r;
138}
139
140static void *createIntegerObject(const redisReadTask *task, long long value) {
141 redisReply *r = createReplyObject(REDIS_REPLY_INTEGER);
142 r->integer = value;
143 if (task->parent) {
a1e97d69 144 redisReply *parent = task->parent->obj;
24f753a8
PN
145 assert(parent->type == REDIS_REPLY_ARRAY);
146 parent->element[task->idx] = r;
147 }
148 return r;
149}
150
151static void *createNilObject(const redisReadTask *task) {
152 redisReply *r = createReplyObject(REDIS_REPLY_NIL);
153 if (task->parent) {
a1e97d69 154 redisReply *parent = task->parent->obj;
24f753a8
PN
155 assert(parent->type == REDIS_REPLY_ARRAY);
156 parent->element[task->idx] = r;
157 }
158 return r;
159}
160
161static char *readBytes(redisReader *r, unsigned int bytes) {
162 char *p;
a1e97d69 163 if (r->len-r->pos >= bytes) {
24f753a8
PN
164 p = r->buf+r->pos;
165 r->pos += bytes;
166 return p;
167 }
168 return NULL;
169}
170
a1e97d69
PN
171/* Find pointer to \r\n. */
172static char *seekNewline(char *s, size_t len) {
173 int pos = 0;
174 int _len = len-1;
175
176 /* Position should be < len-1 because the character at "pos" should be
177 * followed by a \n. Note that strchr cannot be used because it doesn't
178 * allow to search a limited length and the buffer that is being searched
179 * might not have a trailing NULL character. */
180 while (pos < _len) {
181 while(pos < _len && s[pos] != '\r') pos++;
182 if (s[pos] != '\r') {
183 /* Not found. */
184 return NULL;
57c9babd 185 } else {
a1e97d69
PN
186 if (s[pos+1] == '\n') {
187 /* Found. */
188 return s+pos;
189 } else {
190 /* Continue searching. */
191 pos++;
192 }
193 }
194 }
195 return NULL;
196}
197
198/* Read a long long value starting at *s, under the assumption that it will be
199 * terminated by \r\n. Ambiguously returns -1 for unexpected input. */
200static long long readLongLong(char *s) {
201 long long v = 0;
202 int dec, mult = 1;
203 char c;
204
205 if (*s == '-') {
206 mult = -1;
207 s++;
208 } else if (*s == '+') {
209 mult = 1;
210 s++;
211 }
212
213 while ((c = *(s++)) != '\r') {
214 dec = c - '0';
215 if (dec >= 0 && dec < 10) {
216 v *= 10;
217 v += dec;
218 } else {
219 /* Should not happen... */
220 return -1;
57c9babd
PN
221 }
222 }
a1e97d69
PN
223
224 return mult*v;
afc156c2
PN
225}
226
24f753a8 227static char *readLine(redisReader *r, int *_len) {
afc156c2 228 char *p, *s;
24f753a8 229 int len;
afc156c2
PN
230
231 p = r->buf+r->pos;
a1e97d69 232 s = seekNewline(p,(r->len-r->pos));
24f753a8 233 if (s != NULL) {
24f753a8
PN
234 len = s-(r->buf+r->pos);
235 r->pos += len+2; /* skip \r\n */
236 if (_len) *_len = len;
237 return p;
238 }
239 return NULL;
240}
241
242static void moveToNextTask(redisReader *r) {
243 redisReadTask *cur, *prv;
afc156c2
PN
244 while (r->ridx >= 0) {
245 /* Return a.s.a.p. when the stack is now empty. */
246 if (r->ridx == 0) {
247 r->ridx--;
248 return;
249 }
24f753a8 250
afc156c2
PN
251 cur = &(r->rstack[r->ridx]);
252 prv = &(r->rstack[r->ridx-1]);
253 assert(prv->type == REDIS_REPLY_ARRAY);
254 if (cur->idx == prv->elements-1) {
255 r->ridx--;
256 } else {
257 /* Reset the type because the next item can be anything */
258 assert(cur->idx < prv->elements);
259 cur->type = -1;
260 cur->elements = -1;
261 cur->idx++;
262 return;
263 }
24f753a8
PN
264 }
265}
266
267static int processLineItem(redisReader *r) {
268 redisReadTask *cur = &(r->rstack[r->ridx]);
269 void *obj;
270 char *p;
271 int len;
272
273 if ((p = readLine(r,&len)) != NULL) {
afc156c2
PN
274 if (r->fn) {
275 if (cur->type == REDIS_REPLY_INTEGER) {
a1e97d69 276 obj = r->fn->createInteger(cur,readLongLong(p));
afc156c2
PN
277 } else {
278 obj = r->fn->createString(cur,p,len);
279 }
24f753a8 280 } else {
afc156c2 281 obj = (void*)(size_t)(cur->type);
24f753a8
PN
282 }
283
a1e97d69
PN
284 /* Set reply if this is the root object. */
285 if (r->ridx == 0) r->reply = obj;
24f753a8
PN
286 moveToNextTask(r);
287 return 0;
288 }
289 return -1;
290}
291
292static int processBulkItem(redisReader *r) {
293 redisReadTask *cur = &(r->rstack[r->ridx]);
294 void *obj = NULL;
295 char *p, *s;
296 long len;
297 unsigned long bytelen;
a1e97d69 298 int success = 0;
24f753a8
PN
299
300 p = r->buf+r->pos;
a1e97d69 301 s = seekNewline(p,r->len-r->pos);
24f753a8
PN
302 if (s != NULL) {
303 p = r->buf+r->pos;
304 bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
a1e97d69 305 len = readLongLong(p);
24f753a8
PN
306
307 if (len < 0) {
308 /* The nil object can always be created. */
afc156c2
PN
309 obj = r->fn ? r->fn->createNil(cur) :
310 (void*)REDIS_REPLY_NIL;
a1e97d69 311 success = 1;
24f753a8
PN
312 } else {
313 /* Only continue when the buffer contains the entire bulk item. */
314 bytelen += len+2; /* include \r\n */
a1e97d69 315 if (r->pos+bytelen <= r->len) {
afc156c2
PN
316 obj = r->fn ? r->fn->createString(cur,s+2,len) :
317 (void*)REDIS_REPLY_STRING;
a1e97d69 318 success = 1;
24f753a8
PN
319 }
320 }
321
322 /* Proceed when obj was created. */
a1e97d69 323 if (success) {
24f753a8 324 r->pos += bytelen;
a1e97d69
PN
325
326 /* Set reply if this is the root object. */
327 if (r->ridx == 0) r->reply = obj;
24f753a8
PN
328 moveToNextTask(r);
329 return 0;
330 }
331 }
332 return -1;
333}
334
335static int processMultiBulkItem(redisReader *r) {
336 redisReadTask *cur = &(r->rstack[r->ridx]);
337 void *obj;
338 char *p;
339 long elements;
a1e97d69
PN
340 int root = 0;
341
342 /* Set error for nested multi bulks with depth > 1 */
343 if (r->ridx == 2) {
344 redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
345 "No support for nested multi bulk replies with depth > 1"));
346 return -1;
347 }
24f753a8
PN
348
349 if ((p = readLine(r,NULL)) != NULL) {
a1e97d69
PN
350 elements = readLongLong(p);
351 root = (r->ridx == 0);
352
24f753a8 353 if (elements == -1) {
afc156c2
PN
354 obj = r->fn ? r->fn->createNil(cur) :
355 (void*)REDIS_REPLY_NIL;
24f753a8
PN
356 moveToNextTask(r);
357 } else {
afc156c2
PN
358 obj = r->fn ? r->fn->createArray(cur,elements) :
359 (void*)REDIS_REPLY_ARRAY;
24f753a8
PN
360
361 /* Modify task stack when there are more than 0 elements. */
362 if (elements > 0) {
363 cur->elements = elements;
a1e97d69 364 cur->obj = obj;
24f753a8
PN
365 r->ridx++;
366 r->rstack[r->ridx].type = -1;
367 r->rstack[r->ridx].elements = -1;
24f753a8 368 r->rstack[r->ridx].idx = 0;
a1e97d69
PN
369 r->rstack[r->ridx].obj = NULL;
370 r->rstack[r->ridx].parent = cur;
371 r->rstack[r->ridx].privdata = r->privdata;
24f753a8
PN
372 } else {
373 moveToNextTask(r);
374 }
375 }
376
a1e97d69
PN
377 /* Set reply if this is the root object. */
378 if (root) r->reply = obj;
24f753a8
PN
379 return 0;
380 }
381 return -1;
382}
383
384static int processItem(redisReader *r) {
385 redisReadTask *cur = &(r->rstack[r->ridx]);
386 char *p;
387 sds byte;
388
389 /* check if we need to read type */
390 if (cur->type < 0) {
391 if ((p = readBytes(r,1)) != NULL) {
392 switch (p[0]) {
393 case '-':
394 cur->type = REDIS_REPLY_ERROR;
395 break;
396 case '+':
397 cur->type = REDIS_REPLY_STATUS;
398 break;
399 case ':':
400 cur->type = REDIS_REPLY_INTEGER;
401 break;
402 case '$':
403 cur->type = REDIS_REPLY_STRING;
404 break;
405 case '*':
406 cur->type = REDIS_REPLY_ARRAY;
407 break;
408 default:
409 byte = sdscatrepr(sdsempty(),p,1);
410 redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
a1e97d69 411 "Protocol error, got %s as reply type byte", byte));
24f753a8
PN
412 sdsfree(byte);
413 return -1;
414 }
415 } else {
416 /* could not consume 1 byte */
417 return -1;
418 }
419 }
420
421 /* process typed item */
422 switch(cur->type) {
423 case REDIS_REPLY_ERROR:
424 case REDIS_REPLY_STATUS:
425 case REDIS_REPLY_INTEGER:
426 return processLineItem(r);
427 case REDIS_REPLY_STRING:
428 return processBulkItem(r);
429 case REDIS_REPLY_ARRAY:
430 return processMultiBulkItem(r);
431 default:
a1e97d69 432 assert(NULL);
24f753a8
PN
433 return -1;
434 }
435}
436
afc156c2 437void *redisReplyReaderCreate() {
24f753a8
PN
438 redisReader *r = calloc(sizeof(redisReader),1);
439 r->error = NULL;
afc156c2 440 r->fn = &defaultFunctions;
24f753a8
PN
441 r->buf = sdsempty();
442 r->ridx = -1;
443 return r;
444}
445
afc156c2
PN
446/* Set the function set to build the reply. Returns REDIS_OK when there
447 * is no temporary object and it can be set, REDIS_ERR otherwise. */
448int redisReplyReaderSetReplyObjectFunctions(void *reader, redisReplyObjectFunctions *fn) {
449 redisReader *r = reader;
450 if (r->reply == NULL) {
451 r->fn = fn;
452 return REDIS_OK;
453 }
454 return REDIS_ERR;
455}
456
a1e97d69
PN
457/* Set the private data field that is used in the read tasks. This argument can
458 * be used to curry arbitrary data to the custom reply object functions. */
459int redisReplyReaderSetPrivdata(void *reader, void *privdata) {
460 redisReader *r = reader;
461 if (r->reply == NULL) {
462 r->privdata = privdata;
463 return REDIS_OK;
464 }
465 return REDIS_ERR;
466}
467
24f753a8
PN
468/* External libraries wrapping hiredis might need access to the temporary
469 * variable while the reply is built up. When the reader contains an
470 * object in between receiving some bytes to parse, this object might
471 * otherwise be free'd by garbage collection. */
472void *redisReplyReaderGetObject(void *reader) {
473 redisReader *r = reader;
474 return r->reply;
475}
476
477void redisReplyReaderFree(void *reader) {
478 redisReader *r = reader;
479 if (r->error != NULL)
480 sdsfree(r->error);
afc156c2 481 if (r->reply != NULL && r->fn)
24f753a8
PN
482 r->fn->freeObject(r->reply);
483 if (r->buf != NULL)
484 sdsfree(r->buf);
485 free(r);
486}
487
488static void redisSetReplyReaderError(redisReader *r, sds err) {
489 if (r->reply != NULL)
490 r->fn->freeObject(r->reply);
491
492 /* Clear remaining buffer when we see a protocol error. */
493 if (r->buf != NULL) {
494 sdsfree(r->buf);
495 r->buf = sdsempty();
496 r->pos = 0;
497 }
498 r->ridx = -1;
499 r->error = err;
500}
501
502char *redisReplyReaderGetError(void *reader) {
503 redisReader *r = reader;
504 return r->error;
505}
506
57c9babd 507void redisReplyReaderFeed(void *reader, char *buf, size_t len) {
24f753a8
PN
508 redisReader *r = reader;
509
510 /* Copy the provided buffer. */
a1e97d69 511 if (buf != NULL && len >= 1) {
24f753a8 512 r->buf = sdscatlen(r->buf,buf,len);
a1e97d69
PN
513 r->len = sdslen(r->buf);
514 }
24f753a8
PN
515}
516
517int redisReplyReaderGetReply(void *reader, void **reply) {
518 redisReader *r = reader;
519 if (reply != NULL) *reply = NULL;
520
521 /* When the buffer is empty, there will never be a reply. */
a1e97d69 522 if (r->len == 0)
24f753a8
PN
523 return REDIS_OK;
524
525 /* Set first item to process when the stack is empty. */
526 if (r->ridx == -1) {
527 r->rstack[0].type = -1;
528 r->rstack[0].elements = -1;
24f753a8 529 r->rstack[0].idx = -1;
a1e97d69
PN
530 r->rstack[0].obj = NULL;
531 r->rstack[0].parent = NULL;
532 r->rstack[0].privdata = r->privdata;
24f753a8
PN
533 r->ridx = 0;
534 }
535
536 /* Process items in reply. */
537 while (r->ridx >= 0)
538 if (processItem(r) < 0)
539 break;
540
541 /* Discard the consumed part of the buffer. */
542 if (r->pos > 0) {
a1e97d69 543 if (r->pos == r->len) {
24f753a8
PN
544 /* sdsrange has a quirck on this edge case. */
545 sdsfree(r->buf);
546 r->buf = sdsempty();
547 } else {
a1e97d69 548 r->buf = sdsrange(r->buf,r->pos,r->len);
24f753a8
PN
549 }
550 r->pos = 0;
a1e97d69 551 r->len = sdslen(r->buf);
24f753a8
PN
552 }
553
554 /* Emit a reply when there is one. */
555 if (r->ridx == -1) {
556 void *aux = r->reply;
557 r->reply = NULL;
558
559 /* Destroy the buffer when it is empty and is quite large. */
a1e97d69 560 if (r->len == 0 && sdsavail(r->buf) > 16*1024) {
24f753a8
PN
561 sdsfree(r->buf);
562 r->buf = sdsempty();
563 r->pos = 0;
564 }
565
566 /* Check if there actually *is* a reply. */
567 if (r->error != NULL) {
568 return REDIS_ERR;
569 } else {
570 if (reply != NULL) *reply = aux;
571 }
572 }
573 return REDIS_OK;
574}
575
576/* Calculate the number of bytes needed to represent an integer as string. */
577static int intlen(int i) {
578 int len = 0;
579 if (i < 0) {
580 len++;
581 i = -i;
582 }
583 do {
584 len++;
585 i /= 10;
586 } while(i);
587 return len;
588}
589
590/* Helper function for redisvFormatCommand(). */
591static void addArgument(sds a, char ***argv, int *argc, int *totlen) {
592 (*argc)++;
593 if ((*argv = realloc(*argv, sizeof(char*)*(*argc))) == NULL) redisOOM();
594 if (totlen) *totlen = *totlen+1+intlen(sdslen(a))+2+sdslen(a)+2;
595 (*argv)[(*argc)-1] = a;
596}
597
598int redisvFormatCommand(char **target, const char *format, va_list ap) {
599 size_t size;
600 const char *arg, *c = format;
601 char *cmd = NULL; /* final command */
602 int pos; /* position in final command */
603 sds current; /* current argument */
a1e97d69 604 int interpolated = 0; /* did we do interpolation on an argument? */
24f753a8
PN
605 char **argv = NULL;
606 int argc = 0, j;
607 int totlen = 0;
608
609 /* Abort if there is not target to set */
610 if (target == NULL)
611 return -1;
612
613 /* Build the command string accordingly to protocol */
614 current = sdsempty();
615 while(*c != '\0') {
616 if (*c != '%' || c[1] == '\0') {
617 if (*c == ' ') {
618 if (sdslen(current) != 0) {
619 addArgument(current, &argv, &argc, &totlen);
620 current = sdsempty();
a1e97d69 621 interpolated = 0;
24f753a8
PN
622 }
623 } else {
624 current = sdscatlen(current,c,1);
625 }
626 } else {
627 switch(c[1]) {
628 case 's':
629 arg = va_arg(ap,char*);
a1e97d69
PN
630 size = strlen(arg);
631 if (size > 0)
632 current = sdscatlen(current,arg,size);
633 interpolated = 1;
24f753a8
PN
634 break;
635 case 'b':
636 arg = va_arg(ap,char*);
637 size = va_arg(ap,size_t);
a1e97d69
PN
638 if (size > 0)
639 current = sdscatlen(current,arg,size);
640 interpolated = 1;
24f753a8
PN
641 break;
642 case '%':
a1e97d69 643 current = sdscat(current,"%");
24f753a8 644 break;
a1e97d69
PN
645 default:
646 /* Try to detect printf format */
647 {
648 char _format[16];
649 const char *_p = c+1;
650 size_t _l = 0;
651 va_list _cpy;
652
653 /* Flags */
654 if (*_p != '\0' && *_p == '#') _p++;
655 if (*_p != '\0' && *_p == '0') _p++;
656 if (*_p != '\0' && *_p == '-') _p++;
657 if (*_p != '\0' && *_p == ' ') _p++;
658 if (*_p != '\0' && *_p == '+') _p++;
659
660 /* Field width */
661 while (*_p != '\0' && isdigit(*_p)) _p++;
662
663 /* Precision */
664 if (*_p == '.') {
665 _p++;
666 while (*_p != '\0' && isdigit(*_p)) _p++;
667 }
668
669 /* Modifiers */
670 if (*_p != '\0') {
671 if (*_p == 'h' || *_p == 'l') {
672 /* Allow a single repetition for these modifiers */
673 if (_p[0] == _p[1]) _p++;
674 _p++;
675 }
676 }
677
678 /* Conversion specifier */
679 if (*_p != '\0' && strchr("diouxXeEfFgGaA",*_p) != NULL) {
680 _l = (_p+1)-c;
681 if (_l < sizeof(_format)-2) {
682 memcpy(_format,c,_l);
683 _format[_l] = '\0';
684 va_copy(_cpy,ap);
685 current = sdscatvprintf(current,_format,_cpy);
686 interpolated = 1;
687 va_end(_cpy);
688
689 /* Update current position (note: outer blocks
690 * increment c twice so compensate here) */
691 c = _p-1;
692 }
693 }
694
695 /* Consume and discard vararg */
696 va_arg(ap,void);
697 }
24f753a8
PN
698 }
699 c++;
700 }
701 c++;
702 }
703
704 /* Add the last argument if needed */
a1e97d69 705 if (interpolated || sdslen(current) != 0) {
24f753a8
PN
706 addArgument(current, &argv, &argc, &totlen);
707 } else {
708 sdsfree(current);
709 }
710
711 /* Add bytes needed to hold multi bulk count */
712 totlen += 1+intlen(argc)+2;
713
714 /* Build the command at protocol level */
715 cmd = malloc(totlen+1);
716 if (!cmd) redisOOM();
717 pos = sprintf(cmd,"*%d\r\n",argc);
718 for (j = 0; j < argc; j++) {
719 pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(argv[j]));
720 memcpy(cmd+pos,argv[j],sdslen(argv[j]));
721 pos += sdslen(argv[j]);
722 sdsfree(argv[j]);
723 cmd[pos++] = '\r';
724 cmd[pos++] = '\n';
725 }
726 assert(pos == totlen);
727 free(argv);
728 cmd[totlen] = '\0';
729 *target = cmd;
730 return totlen;
731}
732
733/* Format a command according to the Redis protocol. This function
734 * takes a format similar to printf:
735 *
736 * %s represents a C null terminated string you want to interpolate
737 * %b represents a binary safe string
738 *
739 * When using %b you need to provide both the pointer to the string
740 * and the length in bytes. Examples:
741 *
742 * len = redisFormatCommand(target, "GET %s", mykey);
743 * len = redisFormatCommand(target, "SET %s %b", mykey, myval, myvallen);
744 */
745int redisFormatCommand(char **target, const char *format, ...) {
746 va_list ap;
747 int len;
748 va_start(ap,format);
749 len = redisvFormatCommand(target,format,ap);
750 va_end(ap);
751 return len;
752}
753
754/* Format a command according to the Redis protocol. This function takes the
755 * number of arguments, an array with arguments and an array with their
756 * lengths. If the latter is set to NULL, strlen will be used to compute the
757 * argument lengths.
758 */
759int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) {
760 char *cmd = NULL; /* final command */
761 int pos; /* position in final command */
762 size_t len;
763 int totlen, j;
764
765 /* Calculate number of bytes needed for the command */
766 totlen = 1+intlen(argc)+2;
767 for (j = 0; j < argc; j++) {
768 len = argvlen ? argvlen[j] : strlen(argv[j]);
769 totlen += 1+intlen(len)+2+len+2;
770 }
771
772 /* Build the command at protocol level */
773 cmd = malloc(totlen+1);
774 if (!cmd) redisOOM();
775 pos = sprintf(cmd,"*%d\r\n",argc);
776 for (j = 0; j < argc; j++) {
777 len = argvlen ? argvlen[j] : strlen(argv[j]);
778 pos += sprintf(cmd+pos,"$%zu\r\n",len);
779 memcpy(cmd+pos,argv[j],len);
780 pos += len;
781 cmd[pos++] = '\r';
782 cmd[pos++] = '\n';
783 }
784 assert(pos == totlen);
785 cmd[totlen] = '\0';
786 *target = cmd;
787 return totlen;
788}
789
790void __redisSetError(redisContext *c, int type, const sds errstr) {
791 c->err = type;
792 if (errstr != NULL) {
793 c->errstr = errstr;
794 } else {
795 /* Only REDIS_ERR_IO may lack a description! */
796 assert(type == REDIS_ERR_IO);
797 c->errstr = sdsnew(strerror(errno));
798 }
799}
800
801static redisContext *redisContextInit() {
802 redisContext *c = calloc(sizeof(redisContext),1);
803 c->err = 0;
804 c->errstr = NULL;
805 c->obuf = sdsempty();
806 c->fn = &defaultFunctions;
807 c->reader = NULL;
808 return c;
809}
810
811void redisFree(redisContext *c) {
812 /* Disconnect before free'ing if not yet disconnected. */
813 if (c->flags & REDIS_CONNECTED)
814 close(c->fd);
815 if (c->errstr != NULL)
816 sdsfree(c->errstr);
817 if (c->obuf != NULL)
818 sdsfree(c->obuf);
819 if (c->reader != NULL)
820 redisReplyReaderFree(c->reader);
821 free(c);
822}
823
824/* Connect to a Redis instance. On error the field error in the returned
825 * context will be set to the return value of the error function.
826 * When no set of reply functions is given, the default set will be used. */
827redisContext *redisConnect(const char *ip, int port) {
828 redisContext *c = redisContextInit();
829 c->flags |= REDIS_BLOCK;
24f753a8
PN
830 redisContextConnectTcp(c,ip,port);
831 return c;
832}
833
834redisContext *redisConnectNonBlock(const char *ip, int port) {
835 redisContext *c = redisContextInit();
836 c->flags &= ~REDIS_BLOCK;
24f753a8
PN
837 redisContextConnectTcp(c,ip,port);
838 return c;
839}
840
841redisContext *redisConnectUnix(const char *path) {
842 redisContext *c = redisContextInit();
843 c->flags |= REDIS_BLOCK;
24f753a8
PN
844 redisContextConnectUnix(c,path);
845 return c;
846}
847
848redisContext *redisConnectUnixNonBlock(const char *path) {
849 redisContext *c = redisContextInit();
850 c->flags &= ~REDIS_BLOCK;
24f753a8
PN
851 redisContextConnectUnix(c,path);
852 return c;
853}
854
855/* Set the replyObjectFunctions to use. Returns REDIS_ERR when the reader
856 * was already initialized and the function set could not be re-set.
857 * Return REDIS_OK when they could be set. */
858int redisSetReplyObjectFunctions(redisContext *c, redisReplyObjectFunctions *fn) {
859 if (c->reader != NULL)
860 return REDIS_ERR;
861 c->fn = fn;
862 return REDIS_OK;
863}
864
865/* Helper function to lazily create a reply reader. */
866static void __redisCreateReplyReader(redisContext *c) {
afc156c2
PN
867 if (c->reader == NULL) {
868 c->reader = redisReplyReaderCreate();
869 assert(redisReplyReaderSetReplyObjectFunctions(c->reader,c->fn) == REDIS_OK);
870 }
24f753a8
PN
871}
872
873/* Use this function to handle a read event on the descriptor. It will try
874 * and read some bytes from the socket and feed them to the reply parser.
875 *
876 * After this function is called, you may use redisContextReadReply to
877 * see if there is a reply available. */
878int redisBufferRead(redisContext *c) {
879 char buf[2048];
880 int nread = read(c->fd,buf,sizeof(buf));
881 if (nread == -1) {
882 if (errno == EAGAIN) {
883 /* Try again later */
884 } else {
885 __redisSetError(c,REDIS_ERR_IO,NULL);
886 return REDIS_ERR;
887 }
888 } else if (nread == 0) {
889 __redisSetError(c,REDIS_ERR_EOF,
890 sdsnew("Server closed the connection"));
891 return REDIS_ERR;
892 } else {
893 __redisCreateReplyReader(c);
894 redisReplyReaderFeed(c->reader,buf,nread);
895 }
896 return REDIS_OK;
897}
898
899/* Write the output buffer to the socket.
900 *
901 * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was
902 * succesfully written to the socket. When the buffer is empty after the
903 * write operation, "wdone" is set to 1 (if given).
904 *
905 * Returns REDIS_ERR if an error occured trying to write and sets
906 * c->error to hold the appropriate error string.
907 */
908int redisBufferWrite(redisContext *c, int *done) {
909 int nwritten;
910 if (sdslen(c->obuf) > 0) {
911 nwritten = write(c->fd,c->obuf,sdslen(c->obuf));
912 if (nwritten == -1) {
913 if (errno == EAGAIN) {
914 /* Try again later */
915 } else {
916 __redisSetError(c,REDIS_ERR_IO,NULL);
917 return REDIS_ERR;
918 }
919 } else if (nwritten > 0) {
920 if (nwritten == (signed)sdslen(c->obuf)) {
921 sdsfree(c->obuf);
922 c->obuf = sdsempty();
923 } else {
924 c->obuf = sdsrange(c->obuf,nwritten,-1);
925 }
926 }
927 }
928 if (done != NULL) *done = (sdslen(c->obuf) == 0);
929 return REDIS_OK;
930}
931
932/* Internal helper function to try and get a reply from the reader,
933 * or set an error in the context otherwise. */
934int redisGetReplyFromReader(redisContext *c, void **reply) {
935 __redisCreateReplyReader(c);
936 if (redisReplyReaderGetReply(c->reader,reply) == REDIS_ERR) {
937 __redisSetError(c,REDIS_ERR_PROTOCOL,
938 sdsnew(((redisReader*)c->reader)->error));
939 return REDIS_ERR;
940 }
941 return REDIS_OK;
942}
943
944int redisGetReply(redisContext *c, void **reply) {
945 int wdone = 0;
946 void *aux = NULL;
947
948 /* Try to read pending replies */
949 if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
950 return REDIS_ERR;
951
952 /* For the blocking context, flush output buffer and read reply */
953 if (aux == NULL && c->flags & REDIS_BLOCK) {
954 /* Write until done */
955 do {
956 if (redisBufferWrite(c,&wdone) == REDIS_ERR)
957 return REDIS_ERR;
958 } while (!wdone);
959
960 /* Read until there is a reply */
961 do {
962 if (redisBufferRead(c) == REDIS_ERR)
963 return REDIS_ERR;
964 if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
965 return REDIS_ERR;
966 } while (aux == NULL);
967 }
968
969 /* Set reply object */
970 if (reply != NULL) *reply = aux;
971 return REDIS_OK;
972}
973
974
975/* Helper function for the redisAppendCommand* family of functions.
976 *
977 * Write a formatted command to the output buffer. When this family
978 * is used, you need to call redisGetReply yourself to retrieve
979 * the reply (or replies in pub/sub).
980 */
981void __redisAppendCommand(redisContext *c, char *cmd, size_t len) {
982 c->obuf = sdscatlen(c->obuf,cmd,len);
983}
984
985void redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
986 char *cmd;
987 int len;
988 len = redisvFormatCommand(&cmd,format,ap);
989 __redisAppendCommand(c,cmd,len);
990 free(cmd);
991}
992
993void redisAppendCommand(redisContext *c, const char *format, ...) {
994 va_list ap;
995 va_start(ap,format);
996 redisvAppendCommand(c,format,ap);
997 va_end(ap);
998}
999
1000void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1001 char *cmd;
1002 int len;
1003 len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
1004 __redisAppendCommand(c,cmd,len);
1005 free(cmd);
1006}
1007
1008/* Helper function for the redisCommand* family of functions.
1009 *
1010 * Write a formatted command to the output buffer. If the given context is
1011 * blocking, immediately read the reply into the "reply" pointer. When the
1012 * context is non-blocking, the "reply" pointer will not be used and the
1013 * command is simply appended to the write buffer.
1014 *
1015 * Returns the reply when a reply was succesfully retrieved. Returns NULL
1016 * otherwise. When NULL is returned in a blocking context, the error field
1017 * in the context will be set.
1018 */
1019static void *__redisCommand(redisContext *c, char *cmd, size_t len) {
1020 void *aux = NULL;
1021 __redisAppendCommand(c,cmd,len);
1022
1023 if (c->flags & REDIS_BLOCK) {
1024 if (redisGetReply(c,&aux) == REDIS_OK)
1025 return aux;
1026 return NULL;
1027 }
1028 return NULL;
1029}
1030
1031void *redisvCommand(redisContext *c, const char *format, va_list ap) {
1032 char *cmd;
1033 int len;
1034 void *reply = NULL;
1035 len = redisvFormatCommand(&cmd,format,ap);
1036 reply = __redisCommand(c,cmd,len);
1037 free(cmd);
1038 return reply;
1039}
1040
1041void *redisCommand(redisContext *c, const char *format, ...) {
1042 va_list ap;
1043 void *reply = NULL;
1044 va_start(ap,format);
1045 reply = redisvCommand(c,format,ap);
1046 va_end(ap);
1047 return reply;
1048}
1049
1050void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1051 char *cmd;
1052 int len;
1053 void *reply = NULL;
1054 len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
1055 reply = __redisCommand(c,cmd,len);
1056 free(cmd);
1057 return reply;
1058}