]> git.saurik.com Git - redis.git/blame - deps/hiredis/hiredis.c
minor refactoring to networking.c adding a separated function to get a string represe...
[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 52
63d62eb7 53 redisReadTask rstack[9]; /* stack of read tasks */
24f753a8 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) {
9703b1b3
PN
274 if (cur->type == REDIS_REPLY_INTEGER) {
275 if (r->fn && r->fn->createInteger)
a1e97d69 276 obj = r->fn->createInteger(cur,readLongLong(p));
9703b1b3
PN
277 else
278 obj = (void*)REDIS_REPLY_INTEGER;
24f753a8 279 } else {
9703b1b3
PN
280 /* Type will be error or status. */
281 if (r->fn && r->fn->createString)
282 obj = r->fn->createString(cur,p,len);
283 else
284 obj = (void*)(size_t)(cur->type);
24f753a8
PN
285 }
286
a1e97d69
PN
287 /* Set reply if this is the root object. */
288 if (r->ridx == 0) r->reply = obj;
24f753a8
PN
289 moveToNextTask(r);
290 return 0;
291 }
292 return -1;
293}
294
295static int processBulkItem(redisReader *r) {
296 redisReadTask *cur = &(r->rstack[r->ridx]);
297 void *obj = NULL;
298 char *p, *s;
299 long len;
300 unsigned long bytelen;
a1e97d69 301 int success = 0;
24f753a8
PN
302
303 p = r->buf+r->pos;
a1e97d69 304 s = seekNewline(p,r->len-r->pos);
24f753a8
PN
305 if (s != NULL) {
306 p = r->buf+r->pos;
307 bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
a1e97d69 308 len = readLongLong(p);
24f753a8
PN
309
310 if (len < 0) {
311 /* The nil object can always be created. */
9703b1b3
PN
312 if (r->fn && r->fn->createNil)
313 obj = r->fn->createNil(cur);
314 else
315 obj = (void*)REDIS_REPLY_NIL;
a1e97d69 316 success = 1;
24f753a8
PN
317 } else {
318 /* Only continue when the buffer contains the entire bulk item. */
319 bytelen += len+2; /* include \r\n */
a1e97d69 320 if (r->pos+bytelen <= r->len) {
9703b1b3
PN
321 if (r->fn && r->fn->createString)
322 obj = r->fn->createString(cur,s+2,len);
323 else
324 obj = (void*)REDIS_REPLY_STRING;
a1e97d69 325 success = 1;
24f753a8
PN
326 }
327 }
328
329 /* Proceed when obj was created. */
a1e97d69 330 if (success) {
24f753a8 331 r->pos += bytelen;
a1e97d69
PN
332
333 /* Set reply if this is the root object. */
334 if (r->ridx == 0) r->reply = obj;
24f753a8
PN
335 moveToNextTask(r);
336 return 0;
337 }
338 }
339 return -1;
340}
341
342static int processMultiBulkItem(redisReader *r) {
343 redisReadTask *cur = &(r->rstack[r->ridx]);
344 void *obj;
345 char *p;
346 long elements;
a1e97d69
PN
347 int root = 0;
348
349 /* Set error for nested multi bulks with depth > 1 */
63d62eb7 350 if (r->ridx == 8) {
a1e97d69 351 redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
63d62eb7 352 "No support for nested multi bulk replies with depth > 7"));
a1e97d69
PN
353 return -1;
354 }
24f753a8
PN
355
356 if ((p = readLine(r,NULL)) != NULL) {
a1e97d69
PN
357 elements = readLongLong(p);
358 root = (r->ridx == 0);
359
24f753a8 360 if (elements == -1) {
9703b1b3
PN
361 if (r->fn && r->fn->createNil)
362 obj = r->fn->createNil(cur);
363 else
364 obj = (void*)REDIS_REPLY_NIL;
24f753a8
PN
365 moveToNextTask(r);
366 } else {
9703b1b3
PN
367 if (r->fn && r->fn->createArray)
368 obj = r->fn->createArray(cur,elements);
369 else
370 obj = (void*)REDIS_REPLY_ARRAY;
24f753a8
PN
371
372 /* Modify task stack when there are more than 0 elements. */
373 if (elements > 0) {
374 cur->elements = elements;
a1e97d69 375 cur->obj = obj;
24f753a8
PN
376 r->ridx++;
377 r->rstack[r->ridx].type = -1;
378 r->rstack[r->ridx].elements = -1;
24f753a8 379 r->rstack[r->ridx].idx = 0;
a1e97d69
PN
380 r->rstack[r->ridx].obj = NULL;
381 r->rstack[r->ridx].parent = cur;
382 r->rstack[r->ridx].privdata = r->privdata;
24f753a8
PN
383 } else {
384 moveToNextTask(r);
385 }
386 }
387
a1e97d69
PN
388 /* Set reply if this is the root object. */
389 if (root) r->reply = obj;
24f753a8
PN
390 return 0;
391 }
392 return -1;
393}
394
395static int processItem(redisReader *r) {
396 redisReadTask *cur = &(r->rstack[r->ridx]);
397 char *p;
398 sds byte;
399
400 /* check if we need to read type */
401 if (cur->type < 0) {
402 if ((p = readBytes(r,1)) != NULL) {
403 switch (p[0]) {
404 case '-':
405 cur->type = REDIS_REPLY_ERROR;
406 break;
407 case '+':
408 cur->type = REDIS_REPLY_STATUS;
409 break;
410 case ':':
411 cur->type = REDIS_REPLY_INTEGER;
412 break;
413 case '$':
414 cur->type = REDIS_REPLY_STRING;
415 break;
416 case '*':
417 cur->type = REDIS_REPLY_ARRAY;
418 break;
419 default:
420 byte = sdscatrepr(sdsempty(),p,1);
421 redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
a1e97d69 422 "Protocol error, got %s as reply type byte", byte));
24f753a8
PN
423 sdsfree(byte);
424 return -1;
425 }
426 } else {
427 /* could not consume 1 byte */
428 return -1;
429 }
430 }
431
432 /* process typed item */
433 switch(cur->type) {
434 case REDIS_REPLY_ERROR:
435 case REDIS_REPLY_STATUS:
436 case REDIS_REPLY_INTEGER:
437 return processLineItem(r);
438 case REDIS_REPLY_STRING:
439 return processBulkItem(r);
440 case REDIS_REPLY_ARRAY:
441 return processMultiBulkItem(r);
442 default:
a1e97d69 443 assert(NULL);
24f753a8
PN
444 return -1;
445 }
446}
447
9703b1b3 448void *redisReplyReaderCreate(void) {
24f753a8
PN
449 redisReader *r = calloc(sizeof(redisReader),1);
450 r->error = NULL;
afc156c2 451 r->fn = &defaultFunctions;
24f753a8
PN
452 r->buf = sdsempty();
453 r->ridx = -1;
454 return r;
455}
456
afc156c2
PN
457/* Set the function set to build the reply. Returns REDIS_OK when there
458 * is no temporary object and it can be set, REDIS_ERR otherwise. */
459int redisReplyReaderSetReplyObjectFunctions(void *reader, redisReplyObjectFunctions *fn) {
460 redisReader *r = reader;
461 if (r->reply == NULL) {
462 r->fn = fn;
463 return REDIS_OK;
464 }
465 return REDIS_ERR;
466}
467
a1e97d69
PN
468/* Set the private data field that is used in the read tasks. This argument can
469 * be used to curry arbitrary data to the custom reply object functions. */
470int redisReplyReaderSetPrivdata(void *reader, void *privdata) {
471 redisReader *r = reader;
472 if (r->reply == NULL) {
473 r->privdata = privdata;
474 return REDIS_OK;
475 }
476 return REDIS_ERR;
477}
478
24f753a8
PN
479/* External libraries wrapping hiredis might need access to the temporary
480 * variable while the reply is built up. When the reader contains an
481 * object in between receiving some bytes to parse, this object might
482 * otherwise be free'd by garbage collection. */
483void *redisReplyReaderGetObject(void *reader) {
484 redisReader *r = reader;
485 return r->reply;
486}
487
488void redisReplyReaderFree(void *reader) {
489 redisReader *r = reader;
490 if (r->error != NULL)
491 sdsfree(r->error);
afc156c2 492 if (r->reply != NULL && r->fn)
24f753a8
PN
493 r->fn->freeObject(r->reply);
494 if (r->buf != NULL)
495 sdsfree(r->buf);
496 free(r);
497}
498
499static void redisSetReplyReaderError(redisReader *r, sds err) {
500 if (r->reply != NULL)
501 r->fn->freeObject(r->reply);
502
503 /* Clear remaining buffer when we see a protocol error. */
504 if (r->buf != NULL) {
505 sdsfree(r->buf);
506 r->buf = sdsempty();
9703b1b3 507 r->pos = r->len = 0;
24f753a8
PN
508 }
509 r->ridx = -1;
510 r->error = err;
511}
512
513char *redisReplyReaderGetError(void *reader) {
514 redisReader *r = reader;
515 return r->error;
516}
517
9703b1b3 518void redisReplyReaderFeed(void *reader, const char *buf, size_t len) {
24f753a8
PN
519 redisReader *r = reader;
520
521 /* Copy the provided buffer. */
a1e97d69 522 if (buf != NULL && len >= 1) {
65330bad 523#if 0
9703b1b3
PN
524 /* Destroy internal buffer when it is empty and is quite large. */
525 if (r->len == 0 && sdsavail(r->buf) > 16*1024) {
526 sdsfree(r->buf);
527 r->buf = sdsempty();
528 r->pos = 0;
529 }
65330bad 530#endif
24f753a8 531 r->buf = sdscatlen(r->buf,buf,len);
a1e97d69
PN
532 r->len = sdslen(r->buf);
533 }
24f753a8
PN
534}
535
536int redisReplyReaderGetReply(void *reader, void **reply) {
537 redisReader *r = reader;
538 if (reply != NULL) *reply = NULL;
539
540 /* When the buffer is empty, there will never be a reply. */
a1e97d69 541 if (r->len == 0)
24f753a8
PN
542 return REDIS_OK;
543
544 /* Set first item to process when the stack is empty. */
545 if (r->ridx == -1) {
546 r->rstack[0].type = -1;
547 r->rstack[0].elements = -1;
24f753a8 548 r->rstack[0].idx = -1;
a1e97d69
PN
549 r->rstack[0].obj = NULL;
550 r->rstack[0].parent = NULL;
551 r->rstack[0].privdata = r->privdata;
24f753a8
PN
552 r->ridx = 0;
553 }
554
555 /* Process items in reply. */
556 while (r->ridx >= 0)
557 if (processItem(r) < 0)
558 break;
559
9703b1b3
PN
560 /* Discard part of the buffer when we've consumed at least 1k, to avoid
561 * doing unnecessary calls to memmove() in sds.c. */
562 if (r->pos >= 1024) {
563 r->buf = sdsrange(r->buf,r->pos,-1);
24f753a8 564 r->pos = 0;
a1e97d69 565 r->len = sdslen(r->buf);
24f753a8
PN
566 }
567
568 /* Emit a reply when there is one. */
569 if (r->ridx == -1) {
570 void *aux = r->reply;
571 r->reply = NULL;
572
24f753a8
PN
573 /* Check if there actually *is* a reply. */
574 if (r->error != NULL) {
575 return REDIS_ERR;
576 } else {
577 if (reply != NULL) *reply = aux;
578 }
579 }
580 return REDIS_OK;
581}
582
583/* Calculate the number of bytes needed to represent an integer as string. */
584static int intlen(int i) {
585 int len = 0;
586 if (i < 0) {
587 len++;
588 i = -i;
589 }
590 do {
591 len++;
592 i /= 10;
593 } while(i);
594 return len;
595}
596
597/* Helper function for redisvFormatCommand(). */
598static void addArgument(sds a, char ***argv, int *argc, int *totlen) {
599 (*argc)++;
600 if ((*argv = realloc(*argv, sizeof(char*)*(*argc))) == NULL) redisOOM();
601 if (totlen) *totlen = *totlen+1+intlen(sdslen(a))+2+sdslen(a)+2;
602 (*argv)[(*argc)-1] = a;
603}
604
605int redisvFormatCommand(char **target, const char *format, va_list ap) {
606 size_t size;
607 const char *arg, *c = format;
608 char *cmd = NULL; /* final command */
609 int pos; /* position in final command */
610 sds current; /* current argument */
9703b1b3 611 int touched = 0; /* was the current argument touched? */
24f753a8
PN
612 char **argv = NULL;
613 int argc = 0, j;
614 int totlen = 0;
615
616 /* Abort if there is not target to set */
617 if (target == NULL)
618 return -1;
619
620 /* Build the command string accordingly to protocol */
621 current = sdsempty();
622 while(*c != '\0') {
623 if (*c != '%' || c[1] == '\0') {
624 if (*c == ' ') {
9703b1b3 625 if (touched) {
24f753a8
PN
626 addArgument(current, &argv, &argc, &totlen);
627 current = sdsempty();
9703b1b3 628 touched = 0;
24f753a8
PN
629 }
630 } else {
631 current = sdscatlen(current,c,1);
9703b1b3 632 touched = 1;
24f753a8
PN
633 }
634 } else {
635 switch(c[1]) {
636 case 's':
637 arg = va_arg(ap,char*);
a1e97d69
PN
638 size = strlen(arg);
639 if (size > 0)
640 current = sdscatlen(current,arg,size);
24f753a8
PN
641 break;
642 case 'b':
643 arg = va_arg(ap,char*);
644 size = va_arg(ap,size_t);
a1e97d69
PN
645 if (size > 0)
646 current = sdscatlen(current,arg,size);
24f753a8
PN
647 break;
648 case '%':
a1e97d69 649 current = sdscat(current,"%");
24f753a8 650 break;
a1e97d69
PN
651 default:
652 /* Try to detect printf format */
653 {
654 char _format[16];
655 const char *_p = c+1;
656 size_t _l = 0;
657 va_list _cpy;
658
659 /* Flags */
660 if (*_p != '\0' && *_p == '#') _p++;
661 if (*_p != '\0' && *_p == '0') _p++;
662 if (*_p != '\0' && *_p == '-') _p++;
663 if (*_p != '\0' && *_p == ' ') _p++;
664 if (*_p != '\0' && *_p == '+') _p++;
665
666 /* Field width */
667 while (*_p != '\0' && isdigit(*_p)) _p++;
668
669 /* Precision */
670 if (*_p == '.') {
671 _p++;
672 while (*_p != '\0' && isdigit(*_p)) _p++;
673 }
674
675 /* Modifiers */
676 if (*_p != '\0') {
677 if (*_p == 'h' || *_p == 'l') {
678 /* Allow a single repetition for these modifiers */
679 if (_p[0] == _p[1]) _p++;
680 _p++;
681 }
682 }
683
684 /* Conversion specifier */
685 if (*_p != '\0' && strchr("diouxXeEfFgGaA",*_p) != NULL) {
686 _l = (_p+1)-c;
687 if (_l < sizeof(_format)-2) {
688 memcpy(_format,c,_l);
689 _format[_l] = '\0';
690 va_copy(_cpy,ap);
691 current = sdscatvprintf(current,_format,_cpy);
a1e97d69
PN
692 va_end(_cpy);
693
694 /* Update current position (note: outer blocks
695 * increment c twice so compensate here) */
696 c = _p-1;
697 }
698 }
699
700 /* Consume and discard vararg */
701 va_arg(ap,void);
702 }
24f753a8 703 }
9703b1b3 704 touched = 1;
24f753a8
PN
705 c++;
706 }
707 c++;
708 }
709
710 /* Add the last argument if needed */
9703b1b3 711 if (touched) {
24f753a8
PN
712 addArgument(current, &argv, &argc, &totlen);
713 } else {
714 sdsfree(current);
715 }
716
717 /* Add bytes needed to hold multi bulk count */
718 totlen += 1+intlen(argc)+2;
719
720 /* Build the command at protocol level */
721 cmd = malloc(totlen+1);
722 if (!cmd) redisOOM();
723 pos = sprintf(cmd,"*%d\r\n",argc);
724 for (j = 0; j < argc; j++) {
725 pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(argv[j]));
726 memcpy(cmd+pos,argv[j],sdslen(argv[j]));
727 pos += sdslen(argv[j]);
728 sdsfree(argv[j]);
729 cmd[pos++] = '\r';
730 cmd[pos++] = '\n';
731 }
732 assert(pos == totlen);
733 free(argv);
734 cmd[totlen] = '\0';
735 *target = cmd;
736 return totlen;
737}
738
739/* Format a command according to the Redis protocol. This function
740 * takes a format similar to printf:
741 *
742 * %s represents a C null terminated string you want to interpolate
743 * %b represents a binary safe string
744 *
745 * When using %b you need to provide both the pointer to the string
746 * and the length in bytes. Examples:
747 *
748 * len = redisFormatCommand(target, "GET %s", mykey);
749 * len = redisFormatCommand(target, "SET %s %b", mykey, myval, myvallen);
750 */
751int redisFormatCommand(char **target, const char *format, ...) {
752 va_list ap;
753 int len;
754 va_start(ap,format);
755 len = redisvFormatCommand(target,format,ap);
756 va_end(ap);
757 return len;
758}
759
760/* Format a command according to the Redis protocol. This function takes the
761 * number of arguments, an array with arguments and an array with their
762 * lengths. If the latter is set to NULL, strlen will be used to compute the
763 * argument lengths.
764 */
765int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) {
766 char *cmd = NULL; /* final command */
767 int pos; /* position in final command */
768 size_t len;
769 int totlen, j;
770
771 /* Calculate number of bytes needed for the command */
772 totlen = 1+intlen(argc)+2;
773 for (j = 0; j < argc; j++) {
774 len = argvlen ? argvlen[j] : strlen(argv[j]);
775 totlen += 1+intlen(len)+2+len+2;
776 }
777
778 /* Build the command at protocol level */
779 cmd = malloc(totlen+1);
780 if (!cmd) redisOOM();
781 pos = sprintf(cmd,"*%d\r\n",argc);
782 for (j = 0; j < argc; j++) {
783 len = argvlen ? argvlen[j] : strlen(argv[j]);
784 pos += sprintf(cmd+pos,"$%zu\r\n",len);
785 memcpy(cmd+pos,argv[j],len);
786 pos += len;
787 cmd[pos++] = '\r';
788 cmd[pos++] = '\n';
789 }
790 assert(pos == totlen);
791 cmd[totlen] = '\0';
792 *target = cmd;
793 return totlen;
794}
795
796void __redisSetError(redisContext *c, int type, const sds errstr) {
797 c->err = type;
798 if (errstr != NULL) {
799 c->errstr = errstr;
800 } else {
801 /* Only REDIS_ERR_IO may lack a description! */
802 assert(type == REDIS_ERR_IO);
803 c->errstr = sdsnew(strerror(errno));
804 }
805}
806
9703b1b3 807static redisContext *redisContextInit(void) {
24f753a8
PN
808 redisContext *c = calloc(sizeof(redisContext),1);
809 c->err = 0;
810 c->errstr = NULL;
811 c->obuf = sdsempty();
812 c->fn = &defaultFunctions;
813 c->reader = NULL;
814 return c;
815}
816
817void redisFree(redisContext *c) {
9703b1b3 818 if (c->fd > 0)
24f753a8
PN
819 close(c->fd);
820 if (c->errstr != NULL)
821 sdsfree(c->errstr);
822 if (c->obuf != NULL)
823 sdsfree(c->obuf);
824 if (c->reader != NULL)
825 redisReplyReaderFree(c->reader);
826 free(c);
827}
828
829/* Connect to a Redis instance. On error the field error in the returned
830 * context will be set to the return value of the error function.
831 * When no set of reply functions is given, the default set will be used. */
832redisContext *redisConnect(const char *ip, int port) {
833 redisContext *c = redisContextInit();
834 c->flags |= REDIS_BLOCK;
9703b1b3
PN
835 redisContextConnectTcp(c,ip,port,NULL);
836 return c;
837}
838
839redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv) {
840 redisContext *c = redisContextInit();
841 c->flags |= REDIS_BLOCK;
842 redisContextConnectTcp(c,ip,port,&tv);
24f753a8
PN
843 return c;
844}
845
846redisContext *redisConnectNonBlock(const char *ip, int port) {
847 redisContext *c = redisContextInit();
848 c->flags &= ~REDIS_BLOCK;
9703b1b3 849 redisContextConnectTcp(c,ip,port,NULL);
24f753a8
PN
850 return c;
851}
852
853redisContext *redisConnectUnix(const char *path) {
854 redisContext *c = redisContextInit();
855 c->flags |= REDIS_BLOCK;
9703b1b3
PN
856 redisContextConnectUnix(c,path,NULL);
857 return c;
858}
859
860redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv) {
861 redisContext *c = redisContextInit();
862 c->flags |= REDIS_BLOCK;
863 redisContextConnectUnix(c,path,&tv);
24f753a8
PN
864 return c;
865}
866
867redisContext *redisConnectUnixNonBlock(const char *path) {
868 redisContext *c = redisContextInit();
869 c->flags &= ~REDIS_BLOCK;
9703b1b3 870 redisContextConnectUnix(c,path,NULL);
24f753a8
PN
871 return c;
872}
873
9703b1b3
PN
874/* Set read/write timeout on a blocking socket. */
875int redisSetTimeout(redisContext *c, struct timeval tv) {
876 if (c->flags & REDIS_BLOCK)
877 return redisContextSetTimeout(c,tv);
878 return REDIS_ERR;
879}
880
24f753a8
PN
881/* Set the replyObjectFunctions to use. Returns REDIS_ERR when the reader
882 * was already initialized and the function set could not be re-set.
883 * Return REDIS_OK when they could be set. */
884int redisSetReplyObjectFunctions(redisContext *c, redisReplyObjectFunctions *fn) {
885 if (c->reader != NULL)
886 return REDIS_ERR;
887 c->fn = fn;
888 return REDIS_OK;
889}
890
891/* Helper function to lazily create a reply reader. */
892static void __redisCreateReplyReader(redisContext *c) {
afc156c2
PN
893 if (c->reader == NULL) {
894 c->reader = redisReplyReaderCreate();
895 assert(redisReplyReaderSetReplyObjectFunctions(c->reader,c->fn) == REDIS_OK);
896 }
24f753a8
PN
897}
898
899/* Use this function to handle a read event on the descriptor. It will try
900 * and read some bytes from the socket and feed them to the reply parser.
901 *
902 * After this function is called, you may use redisContextReadReply to
903 * see if there is a reply available. */
904int redisBufferRead(redisContext *c) {
65330bad 905 char buf[1024*16];
24f753a8
PN
906 int nread = read(c->fd,buf,sizeof(buf));
907 if (nread == -1) {
9703b1b3 908 if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) {
24f753a8
PN
909 /* Try again later */
910 } else {
911 __redisSetError(c,REDIS_ERR_IO,NULL);
912 return REDIS_ERR;
913 }
914 } else if (nread == 0) {
915 __redisSetError(c,REDIS_ERR_EOF,
916 sdsnew("Server closed the connection"));
917 return REDIS_ERR;
918 } else {
919 __redisCreateReplyReader(c);
920 redisReplyReaderFeed(c->reader,buf,nread);
921 }
922 return REDIS_OK;
923}
924
925/* Write the output buffer to the socket.
926 *
927 * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was
928 * succesfully written to the socket. When the buffer is empty after the
929 * write operation, "wdone" is set to 1 (if given).
930 *
931 * Returns REDIS_ERR if an error occured trying to write and sets
932 * c->error to hold the appropriate error string.
933 */
934int redisBufferWrite(redisContext *c, int *done) {
935 int nwritten;
936 if (sdslen(c->obuf) > 0) {
937 nwritten = write(c->fd,c->obuf,sdslen(c->obuf));
938 if (nwritten == -1) {
9703b1b3 939 if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) {
24f753a8
PN
940 /* Try again later */
941 } else {
942 __redisSetError(c,REDIS_ERR_IO,NULL);
943 return REDIS_ERR;
944 }
945 } else if (nwritten > 0) {
946 if (nwritten == (signed)sdslen(c->obuf)) {
947 sdsfree(c->obuf);
948 c->obuf = sdsempty();
949 } else {
950 c->obuf = sdsrange(c->obuf,nwritten,-1);
951 }
952 }
953 }
954 if (done != NULL) *done = (sdslen(c->obuf) == 0);
955 return REDIS_OK;
956}
957
958/* Internal helper function to try and get a reply from the reader,
959 * or set an error in the context otherwise. */
960int redisGetReplyFromReader(redisContext *c, void **reply) {
961 __redisCreateReplyReader(c);
962 if (redisReplyReaderGetReply(c->reader,reply) == REDIS_ERR) {
963 __redisSetError(c,REDIS_ERR_PROTOCOL,
964 sdsnew(((redisReader*)c->reader)->error));
965 return REDIS_ERR;
966 }
967 return REDIS_OK;
968}
969
970int redisGetReply(redisContext *c, void **reply) {
971 int wdone = 0;
972 void *aux = NULL;
973
974 /* Try to read pending replies */
975 if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
976 return REDIS_ERR;
977
978 /* For the blocking context, flush output buffer and read reply */
979 if (aux == NULL && c->flags & REDIS_BLOCK) {
980 /* Write until done */
981 do {
982 if (redisBufferWrite(c,&wdone) == REDIS_ERR)
983 return REDIS_ERR;
984 } while (!wdone);
985
986 /* Read until there is a reply */
987 do {
988 if (redisBufferRead(c) == REDIS_ERR)
989 return REDIS_ERR;
990 if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
991 return REDIS_ERR;
992 } while (aux == NULL);
993 }
994
995 /* Set reply object */
996 if (reply != NULL) *reply = aux;
997 return REDIS_OK;
998}
999
1000
1001/* Helper function for the redisAppendCommand* family of functions.
1002 *
1003 * Write a formatted command to the output buffer. When this family
1004 * is used, you need to call redisGetReply yourself to retrieve
1005 * the reply (or replies in pub/sub).
1006 */
1007void __redisAppendCommand(redisContext *c, char *cmd, size_t len) {
1008 c->obuf = sdscatlen(c->obuf,cmd,len);
1009}
1010
1011void redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
1012 char *cmd;
1013 int len;
1014 len = redisvFormatCommand(&cmd,format,ap);
1015 __redisAppendCommand(c,cmd,len);
1016 free(cmd);
1017}
1018
1019void redisAppendCommand(redisContext *c, const char *format, ...) {
1020 va_list ap;
1021 va_start(ap,format);
1022 redisvAppendCommand(c,format,ap);
1023 va_end(ap);
1024}
1025
1026void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1027 char *cmd;
1028 int len;
1029 len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
1030 __redisAppendCommand(c,cmd,len);
1031 free(cmd);
1032}
1033
1034/* Helper function for the redisCommand* family of functions.
1035 *
1036 * Write a formatted command to the output buffer. If the given context is
1037 * blocking, immediately read the reply into the "reply" pointer. When the
1038 * context is non-blocking, the "reply" pointer will not be used and the
1039 * command is simply appended to the write buffer.
1040 *
1041 * Returns the reply when a reply was succesfully retrieved. Returns NULL
1042 * otherwise. When NULL is returned in a blocking context, the error field
1043 * in the context will be set.
1044 */
1045static void *__redisCommand(redisContext *c, char *cmd, size_t len) {
1046 void *aux = NULL;
1047 __redisAppendCommand(c,cmd,len);
1048
1049 if (c->flags & REDIS_BLOCK) {
1050 if (redisGetReply(c,&aux) == REDIS_OK)
1051 return aux;
1052 return NULL;
1053 }
1054 return NULL;
1055}
1056
1057void *redisvCommand(redisContext *c, const char *format, va_list ap) {
1058 char *cmd;
1059 int len;
1060 void *reply = NULL;
1061 len = redisvFormatCommand(&cmd,format,ap);
1062 reply = __redisCommand(c,cmd,len);
1063 free(cmd);
1064 return reply;
1065}
1066
1067void *redisCommand(redisContext *c, const char *format, ...) {
1068 va_list ap;
1069 void *reply = NULL;
1070 va_start(ap,format);
1071 reply = redisvCommand(c,format,ap);
1072 va_end(ap);
1073 return reply;
1074}
1075
1076void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1077 char *cmd;
1078 int len;
1079 void *reply = NULL;
1080 len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
1081 reply = __redisCommand(c,cmd,len);
1082 free(cmd);
1083 return reply;
1084}