]> git.saurik.com Git - redis.git/blame_incremental - deps/hiredis/hiredis.c
Merge conflicts resolved.
[redis.git] / deps / hiredis / hiredis.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
3 * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
4 *
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 "fmacros.h"
33#include <string.h>
34#include <stdlib.h>
35#include <unistd.h>
36#include <assert.h>
37#include <errno.h>
38#include <ctype.h>
39
40#include "hiredis.h"
41#include "net.h"
42#include "sds.h"
43
44static redisReply *createReplyObject(int type);
45static void *createStringObject(const redisReadTask *task, char *str, size_t len);
46static void *createArrayObject(const redisReadTask *task, int elements);
47static void *createIntegerObject(const redisReadTask *task, long long value);
48static void *createNilObject(const redisReadTask *task);
49
50/* Default set of functions to build the reply. Keep in mind that such a
51 * function returning NULL is interpreted as OOM. */
52static redisReplyObjectFunctions defaultFunctions = {
53 createStringObject,
54 createArrayObject,
55 createIntegerObject,
56 createNilObject,
57 freeReplyObject
58};
59
60/* Create a reply object */
61static redisReply *createReplyObject(int type) {
62 redisReply *r = calloc(1,sizeof(*r));
63
64 if (r == NULL)
65 return NULL;
66
67 r->type = type;
68 return r;
69}
70
71/* Free a reply object */
72void freeReplyObject(void *reply) {
73 redisReply *r = reply;
74 size_t j;
75
76 switch(r->type) {
77 case REDIS_REPLY_INTEGER:
78 break; /* Nothing to free */
79 case REDIS_REPLY_ARRAY:
80 if (r->element != NULL) {
81 for (j = 0; j < r->elements; j++)
82 if (r->element[j] != NULL)
83 freeReplyObject(r->element[j]);
84 free(r->element);
85 }
86 break;
87 case REDIS_REPLY_ERROR:
88 case REDIS_REPLY_STATUS:
89 case REDIS_REPLY_STRING:
90 if (r->str != NULL)
91 free(r->str);
92 break;
93 }
94 free(r);
95}
96
97static void *createStringObject(const redisReadTask *task, char *str, size_t len) {
98 redisReply *r, *parent;
99 char *buf;
100
101 r = createReplyObject(task->type);
102 if (r == NULL)
103 return NULL;
104
105 buf = malloc(len+1);
106 if (buf == NULL) {
107 freeReplyObject(r);
108 return NULL;
109 }
110
111 assert(task->type == REDIS_REPLY_ERROR ||
112 task->type == REDIS_REPLY_STATUS ||
113 task->type == REDIS_REPLY_STRING);
114
115 /* Copy string value */
116 memcpy(buf,str,len);
117 buf[len] = '\0';
118 r->str = buf;
119 r->len = len;
120
121 if (task->parent) {
122 parent = task->parent->obj;
123 assert(parent->type == REDIS_REPLY_ARRAY);
124 parent->element[task->idx] = r;
125 }
126 return r;
127}
128
129static void *createArrayObject(const redisReadTask *task, int elements) {
130 redisReply *r, *parent;
131
132 r = createReplyObject(REDIS_REPLY_ARRAY);
133 if (r == NULL)
134 return NULL;
135
136 if (elements > 0) {
137 r->element = calloc(elements,sizeof(redisReply*));
138 if (r->element == NULL) {
139 freeReplyObject(r);
140 return NULL;
141 }
142 }
143
144 r->elements = elements;
145
146 if (task->parent) {
147 parent = task->parent->obj;
148 assert(parent->type == REDIS_REPLY_ARRAY);
149 parent->element[task->idx] = r;
150 }
151 return r;
152}
153
154static void *createIntegerObject(const redisReadTask *task, long long value) {
155 redisReply *r, *parent;
156
157 r = createReplyObject(REDIS_REPLY_INTEGER);
158 if (r == NULL)
159 return NULL;
160
161 r->integer = value;
162
163 if (task->parent) {
164 parent = task->parent->obj;
165 assert(parent->type == REDIS_REPLY_ARRAY);
166 parent->element[task->idx] = r;
167 }
168 return r;
169}
170
171static void *createNilObject(const redisReadTask *task) {
172 redisReply *r, *parent;
173
174 r = createReplyObject(REDIS_REPLY_NIL);
175 if (r == NULL)
176 return NULL;
177
178 if (task->parent) {
179 parent = task->parent->obj;
180 assert(parent->type == REDIS_REPLY_ARRAY);
181 parent->element[task->idx] = r;
182 }
183 return r;
184}
185
186static void __redisReaderSetError(redisReader *r, int type, const char *str) {
187 size_t len;
188
189 if (r->reply != NULL && r->fn && r->fn->freeObject) {
190 r->fn->freeObject(r->reply);
191 r->reply = NULL;
192 }
193
194 /* Clear input buffer on errors. */
195 if (r->buf != NULL) {
196 sdsfree(r->buf);
197 r->buf = NULL;
198 r->pos = r->len = 0;
199 }
200
201 /* Reset task stack. */
202 r->ridx = -1;
203
204 /* Set error. */
205 r->err = type;
206 len = strlen(str);
207 len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1);
208 memcpy(r->errstr,str,len);
209 r->errstr[len] = '\0';
210}
211
212static size_t chrtos(char *buf, size_t size, char byte) {
213 size_t len = 0;
214
215 switch(byte) {
216 case '\\':
217 case '"':
218 len = snprintf(buf,size,"\"\\%c\"",byte);
219 break;
220 case '\n': len = snprintf(buf,size,"\"\\n\""); break;
221 case '\r': len = snprintf(buf,size,"\"\\r\""); break;
222 case '\t': len = snprintf(buf,size,"\"\\t\""); break;
223 case '\a': len = snprintf(buf,size,"\"\\a\""); break;
224 case '\b': len = snprintf(buf,size,"\"\\b\""); break;
225 default:
226 if (isprint(byte))
227 len = snprintf(buf,size,"\"%c\"",byte);
228 else
229 len = snprintf(buf,size,"\"\\x%02x\"",(unsigned char)byte);
230 break;
231 }
232
233 return len;
234}
235
236static void __redisReaderSetErrorProtocolByte(redisReader *r, char byte) {
237 char cbuf[8], sbuf[128];
238
239 chrtos(cbuf,sizeof(cbuf),byte);
240 snprintf(sbuf,sizeof(sbuf),
241 "Protocol error, got %s as reply type byte", cbuf);
242 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf);
243}
244
245static void __redisReaderSetErrorOOM(redisReader *r) {
246 __redisReaderSetError(r,REDIS_ERR_OOM,"Out of memory");
247}
248
249static char *readBytes(redisReader *r, unsigned int bytes) {
250 char *p;
251 if (r->len-r->pos >= bytes) {
252 p = r->buf+r->pos;
253 r->pos += bytes;
254 return p;
255 }
256 return NULL;
257}
258
259/* Find pointer to \r\n. */
260static char *seekNewline(char *s, size_t len) {
261 int pos = 0;
262 int _len = len-1;
263
264 /* Position should be < len-1 because the character at "pos" should be
265 * followed by a \n. Note that strchr cannot be used because it doesn't
266 * allow to search a limited length and the buffer that is being searched
267 * might not have a trailing NULL character. */
268 while (pos < _len) {
269 while(pos < _len && s[pos] != '\r') pos++;
270 if (s[pos] != '\r') {
271 /* Not found. */
272 return NULL;
273 } else {
274 if (s[pos+1] == '\n') {
275 /* Found. */
276 return s+pos;
277 } else {
278 /* Continue searching. */
279 pos++;
280 }
281 }
282 }
283 return NULL;
284}
285
286/* Read a long long value starting at *s, under the assumption that it will be
287 * terminated by \r\n. Ambiguously returns -1 for unexpected input. */
288static long long readLongLong(char *s) {
289 long long v = 0;
290 int dec, mult = 1;
291 char c;
292
293 if (*s == '-') {
294 mult = -1;
295 s++;
296 } else if (*s == '+') {
297 mult = 1;
298 s++;
299 }
300
301 while ((c = *(s++)) != '\r') {
302 dec = c - '0';
303 if (dec >= 0 && dec < 10) {
304 v *= 10;
305 v += dec;
306 } else {
307 /* Should not happen... */
308 return -1;
309 }
310 }
311
312 return mult*v;
313}
314
315static char *readLine(redisReader *r, int *_len) {
316 char *p, *s;
317 int len;
318
319 p = r->buf+r->pos;
320 s = seekNewline(p,(r->len-r->pos));
321 if (s != NULL) {
322 len = s-(r->buf+r->pos);
323 r->pos += len+2; /* skip \r\n */
324 if (_len) *_len = len;
325 return p;
326 }
327 return NULL;
328}
329
330static void moveToNextTask(redisReader *r) {
331 redisReadTask *cur, *prv;
332 while (r->ridx >= 0) {
333 /* Return a.s.a.p. when the stack is now empty. */
334 if (r->ridx == 0) {
335 r->ridx--;
336 return;
337 }
338
339 cur = &(r->rstack[r->ridx]);
340 prv = &(r->rstack[r->ridx-1]);
341 assert(prv->type == REDIS_REPLY_ARRAY);
342 if (cur->idx == prv->elements-1) {
343 r->ridx--;
344 } else {
345 /* Reset the type because the next item can be anything */
346 assert(cur->idx < prv->elements);
347 cur->type = -1;
348 cur->elements = -1;
349 cur->idx++;
350 return;
351 }
352 }
353}
354
355static int processLineItem(redisReader *r) {
356 redisReadTask *cur = &(r->rstack[r->ridx]);
357 void *obj;
358 char *p;
359 int len;
360
361 if ((p = readLine(r,&len)) != NULL) {
362 if (cur->type == REDIS_REPLY_INTEGER) {
363 if (r->fn && r->fn->createInteger)
364 obj = r->fn->createInteger(cur,readLongLong(p));
365 else
366 obj = (void*)REDIS_REPLY_INTEGER;
367 } else {
368 /* Type will be error or status. */
369 if (r->fn && r->fn->createString)
370 obj = r->fn->createString(cur,p,len);
371 else
372 obj = (void*)(size_t)(cur->type);
373 }
374
375 if (obj == NULL) {
376 __redisReaderSetErrorOOM(r);
377 return REDIS_ERR;
378 }
379
380 /* Set reply if this is the root object. */
381 if (r->ridx == 0) r->reply = obj;
382 moveToNextTask(r);
383 return REDIS_OK;
384 }
385
386 return REDIS_ERR;
387}
388
389static int processBulkItem(redisReader *r) {
390 redisReadTask *cur = &(r->rstack[r->ridx]);
391 void *obj = NULL;
392 char *p, *s;
393 long len;
394 unsigned long bytelen;
395 int success = 0;
396
397 p = r->buf+r->pos;
398 s = seekNewline(p,r->len-r->pos);
399 if (s != NULL) {
400 p = r->buf+r->pos;
401 bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
402 len = readLongLong(p);
403
404 if (len < 0) {
405 /* The nil object can always be created. */
406 if (r->fn && r->fn->createNil)
407 obj = r->fn->createNil(cur);
408 else
409 obj = (void*)REDIS_REPLY_NIL;
410 success = 1;
411 } else {
412 /* Only continue when the buffer contains the entire bulk item. */
413 bytelen += len+2; /* include \r\n */
414 if (r->pos+bytelen <= r->len) {
415 if (r->fn && r->fn->createString)
416 obj = r->fn->createString(cur,s+2,len);
417 else
418 obj = (void*)REDIS_REPLY_STRING;
419 success = 1;
420 }
421 }
422
423 /* Proceed when obj was created. */
424 if (success) {
425 if (obj == NULL) {
426 __redisReaderSetErrorOOM(r);
427 return REDIS_ERR;
428 }
429
430 r->pos += bytelen;
431
432 /* Set reply if this is the root object. */
433 if (r->ridx == 0) r->reply = obj;
434 moveToNextTask(r);
435 return REDIS_OK;
436 }
437 }
438
439 return REDIS_ERR;
440}
441
442static int processMultiBulkItem(redisReader *r) {
443 redisReadTask *cur = &(r->rstack[r->ridx]);
444 void *obj;
445 char *p;
446 long elements;
447 int root = 0;
448
449 /* Set error for nested multi bulks with depth > 2 */
450 if (r->ridx == 8) {
451 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
452 "No support for nested multi bulk replies with depth > 7");
453 return REDIS_ERR;
454 }
455
456 if ((p = readLine(r,NULL)) != NULL) {
457 elements = readLongLong(p);
458 root = (r->ridx == 0);
459
460 if (elements == -1) {
461 if (r->fn && r->fn->createNil)
462 obj = r->fn->createNil(cur);
463 else
464 obj = (void*)REDIS_REPLY_NIL;
465
466 if (obj == NULL) {
467 __redisReaderSetErrorOOM(r);
468 return REDIS_ERR;
469 }
470
471 moveToNextTask(r);
472 } else {
473 if (r->fn && r->fn->createArray)
474 obj = r->fn->createArray(cur,elements);
475 else
476 obj = (void*)REDIS_REPLY_ARRAY;
477
478 if (obj == NULL) {
479 __redisReaderSetErrorOOM(r);
480 return REDIS_ERR;
481 }
482
483 /* Modify task stack when there are more than 0 elements. */
484 if (elements > 0) {
485 cur->elements = elements;
486 cur->obj = obj;
487 r->ridx++;
488 r->rstack[r->ridx].type = -1;
489 r->rstack[r->ridx].elements = -1;
490 r->rstack[r->ridx].idx = 0;
491 r->rstack[r->ridx].obj = NULL;
492 r->rstack[r->ridx].parent = cur;
493 r->rstack[r->ridx].privdata = r->privdata;
494 } else {
495 moveToNextTask(r);
496 }
497 }
498
499 /* Set reply if this is the root object. */
500 if (root) r->reply = obj;
501 return REDIS_OK;
502 }
503
504 return REDIS_ERR;
505}
506
507static int processItem(redisReader *r) {
508 redisReadTask *cur = &(r->rstack[r->ridx]);
509 char *p;
510
511 /* check if we need to read type */
512 if (cur->type < 0) {
513 if ((p = readBytes(r,1)) != NULL) {
514 switch (p[0]) {
515 case '-':
516 cur->type = REDIS_REPLY_ERROR;
517 break;
518 case '+':
519 cur->type = REDIS_REPLY_STATUS;
520 break;
521 case ':':
522 cur->type = REDIS_REPLY_INTEGER;
523 break;
524 case '$':
525 cur->type = REDIS_REPLY_STRING;
526 break;
527 case '*':
528 cur->type = REDIS_REPLY_ARRAY;
529 break;
530 default:
531 __redisReaderSetErrorProtocolByte(r,*p);
532 return REDIS_ERR;
533 }
534 } else {
535 /* could not consume 1 byte */
536 return REDIS_ERR;
537 }
538 }
539
540 /* process typed item */
541 switch(cur->type) {
542 case REDIS_REPLY_ERROR:
543 case REDIS_REPLY_STATUS:
544 case REDIS_REPLY_INTEGER:
545 return processLineItem(r);
546 case REDIS_REPLY_STRING:
547 return processBulkItem(r);
548 case REDIS_REPLY_ARRAY:
549 return processMultiBulkItem(r);
550 default:
551 assert(NULL);
552 return REDIS_ERR; /* Avoid warning. */
553 }
554}
555
556redisReader *redisReaderCreate(void) {
557 redisReader *r;
558
559 r = calloc(sizeof(redisReader),1);
560 if (r == NULL)
561 return NULL;
562
563 r->err = 0;
564 r->errstr[0] = '\0';
565 r->fn = &defaultFunctions;
566 r->buf = sdsempty();
567 if (r->buf == NULL) {
568 free(r);
569 return NULL;
570 }
571
572 r->ridx = -1;
573 return r;
574}
575
576void redisReaderFree(redisReader *r) {
577 if (r->reply != NULL && r->fn && r->fn->freeObject)
578 r->fn->freeObject(r->reply);
579 if (r->buf != NULL)
580 sdsfree(r->buf);
581 free(r);
582}
583
584int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
585 sds newbuf;
586
587 /* Return early when this reader is in an erroneous state. */
588 if (r->err)
589 return REDIS_ERR;
590
591 /* Copy the provided buffer. */
592 if (buf != NULL && len >= 1) {
593#if 0
594 /* Destroy internal buffer when it is empty and is quite large. */
595 if (r->len == 0 && sdsavail(r->buf) > 16*1024) {
596 sdsfree(r->buf);
597 r->buf = sdsempty();
598 r->pos = 0;
599
600 /* r->buf should not be NULL since we just free'd a larger one. */
601 assert(r->buf != NULL);
602 }
603#endif
604
605 newbuf = sdscatlen(r->buf,buf,len);
606 if (newbuf == NULL) {
607 __redisReaderSetErrorOOM(r);
608 return REDIS_ERR;
609 }
610
611 r->buf = newbuf;
612 r->len = sdslen(r->buf);
613 }
614
615 return REDIS_OK;
616}
617
618int redisReaderGetReply(redisReader *r, void **reply) {
619 /* Default target pointer to NULL. */
620 if (reply != NULL)
621 *reply = NULL;
622
623 /* Return early when this reader is in an erroneous state. */
624 if (r->err)
625 return REDIS_ERR;
626
627 /* When the buffer is empty, there will never be a reply. */
628 if (r->len == 0)
629 return REDIS_OK;
630
631 /* Set first item to process when the stack is empty. */
632 if (r->ridx == -1) {
633 r->rstack[0].type = -1;
634 r->rstack[0].elements = -1;
635 r->rstack[0].idx = -1;
636 r->rstack[0].obj = NULL;
637 r->rstack[0].parent = NULL;
638 r->rstack[0].privdata = r->privdata;
639 r->ridx = 0;
640 }
641
642 /* Process items in reply. */
643 while (r->ridx >= 0)
644 if (processItem(r) != REDIS_OK)
645 break;
646
647 /* Return ASAP when an error occurred. */
648 if (r->err)
649 return REDIS_ERR;
650
651 /* Discard part of the buffer when we've consumed at least 1k, to avoid
652 * doing unnecessary calls to memmove() in sds.c. */
653 if (r->pos >= 1024) {
654 r->buf = sdsrange(r->buf,r->pos,-1);
655 r->pos = 0;
656 r->len = sdslen(r->buf);
657 }
658
659 /* Emit a reply when there is one. */
660 if (r->ridx == -1) {
661 if (reply != NULL)
662 *reply = r->reply;
663 r->reply = NULL;
664 }
665 return REDIS_OK;
666}
667
668/* Calculate the number of bytes needed to represent an integer as string. */
669static int intlen(int i) {
670 int len = 0;
671 if (i < 0) {
672 len++;
673 i = -i;
674 }
675 do {
676 len++;
677 i /= 10;
678 } while(i);
679 return len;
680}
681
682/* Helper that calculates the bulk length given a certain string length. */
683static size_t bulklen(size_t len) {
684 return 1+intlen(len)+2+len+2;
685}
686
687int redisvFormatCommand(char **target, const char *format, va_list ap) {
688 const char *c = format;
689 char *cmd = NULL; /* final command */
690 int pos; /* position in final command */
691 sds curarg, newarg; /* current argument */
692 int touched = 0; /* was the current argument touched? */
693 char **curargv = NULL, **newargv = NULL;
694 int argc = 0;
695 int totlen = 0;
696 int j;
697
698 /* Abort if there is not target to set */
699 if (target == NULL)
700 return -1;
701
702 /* Build the command string accordingly to protocol */
703 curarg = sdsempty();
704 if (curarg == NULL)
705 return -1;
706
707 while(*c != '\0') {
708 if (*c != '%' || c[1] == '\0') {
709 if (*c == ' ') {
710 if (touched) {
711 newargv = realloc(curargv,sizeof(char*)*(argc+1));
712 if (newargv == NULL) goto err;
713 curargv = newargv;
714 curargv[argc++] = curarg;
715 totlen += bulklen(sdslen(curarg));
716
717 /* curarg is put in argv so it can be overwritten. */
718 curarg = sdsempty();
719 if (curarg == NULL) goto err;
720 touched = 0;
721 }
722 } else {
723 newarg = sdscatlen(curarg,c,1);
724 if (newarg == NULL) goto err;
725 curarg = newarg;
726 touched = 1;
727 }
728 } else {
729 char *arg;
730 size_t size;
731
732 /* Set newarg so it can be checked even if it is not touched. */
733 newarg = curarg;
734
735 switch(c[1]) {
736 case 's':
737 arg = va_arg(ap,char*);
738 size = strlen(arg);
739 if (size > 0)
740 newarg = sdscatlen(curarg,arg,size);
741 break;
742 case 'b':
743 arg = va_arg(ap,char*);
744 size = va_arg(ap,size_t);
745 if (size > 0)
746 newarg = sdscatlen(curarg,arg,size);
747 break;
748 case '%':
749 newarg = sdscat(curarg,"%");
750 break;
751 default:
752 /* Try to detect printf format */
753 {
754 static const char intfmts[] = "diouxX";
755 char _format[16];
756 const char *_p = c+1;
757 size_t _l = 0;
758 va_list _cpy;
759
760 /* Flags */
761 if (*_p != '\0' && *_p == '#') _p++;
762 if (*_p != '\0' && *_p == '0') _p++;
763 if (*_p != '\0' && *_p == '-') _p++;
764 if (*_p != '\0' && *_p == ' ') _p++;
765 if (*_p != '\0' && *_p == '+') _p++;
766
767 /* Field width */
768 while (*_p != '\0' && isdigit(*_p)) _p++;
769
770 /* Precision */
771 if (*_p == '.') {
772 _p++;
773 while (*_p != '\0' && isdigit(*_p)) _p++;
774 }
775
776 /* Copy va_list before consuming with va_arg */
777 va_copy(_cpy,ap);
778
779 /* Integer conversion (without modifiers) */
780 if (strchr(intfmts,*_p) != NULL) {
781 va_arg(ap,int);
782 goto fmt_valid;
783 }
784
785 /* Double conversion (without modifiers) */
786 if (strchr("eEfFgGaA",*_p) != NULL) {
787 va_arg(ap,double);
788 goto fmt_valid;
789 }
790
791 /* Size: char */
792 if (_p[0] == 'h' && _p[1] == 'h') {
793 _p += 2;
794 if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
795 va_arg(ap,int); /* char gets promoted to int */
796 goto fmt_valid;
797 }
798 goto fmt_invalid;
799 }
800
801 /* Size: short */
802 if (_p[0] == 'h') {
803 _p += 1;
804 if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
805 va_arg(ap,int); /* short gets promoted to int */
806 goto fmt_valid;
807 }
808 goto fmt_invalid;
809 }
810
811 /* Size: long long */
812 if (_p[0] == 'l' && _p[1] == 'l') {
813 _p += 2;
814 if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
815 va_arg(ap,long long);
816 goto fmt_valid;
817 }
818 goto fmt_invalid;
819 }
820
821 /* Size: long */
822 if (_p[0] == 'l') {
823 _p += 1;
824 if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
825 va_arg(ap,long);
826 goto fmt_valid;
827 }
828 goto fmt_invalid;
829 }
830
831 fmt_invalid:
832 va_end(_cpy);
833 goto err;
834
835 fmt_valid:
836 _l = (_p+1)-c;
837 if (_l < sizeof(_format)-2) {
838 memcpy(_format,c,_l);
839 _format[_l] = '\0';
840 newarg = sdscatvprintf(curarg,_format,_cpy);
841
842 /* Update current position (note: outer blocks
843 * increment c twice so compensate here) */
844 c = _p-1;
845 }
846
847 va_end(_cpy);
848 break;
849 }
850 }
851
852 if (newarg == NULL) goto err;
853 curarg = newarg;
854
855 touched = 1;
856 c++;
857 }
858 c++;
859 }
860
861 /* Add the last argument if needed */
862 if (touched) {
863 newargv = realloc(curargv,sizeof(char*)*(argc+1));
864 if (newargv == NULL) goto err;
865 curargv = newargv;
866 curargv[argc++] = curarg;
867 totlen += bulklen(sdslen(curarg));
868 } else {
869 sdsfree(curarg);
870 }
871
872 /* Clear curarg because it was put in curargv or was free'd. */
873 curarg = NULL;
874
875 /* Add bytes needed to hold multi bulk count */
876 totlen += 1+intlen(argc)+2;
877
878 /* Build the command at protocol level */
879 cmd = malloc(totlen+1);
880 if (cmd == NULL) goto err;
881
882 pos = sprintf(cmd,"*%d\r\n",argc);
883 for (j = 0; j < argc; j++) {
884 pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(curargv[j]));
885 memcpy(cmd+pos,curargv[j],sdslen(curargv[j]));
886 pos += sdslen(curargv[j]);
887 sdsfree(curargv[j]);
888 cmd[pos++] = '\r';
889 cmd[pos++] = '\n';
890 }
891 assert(pos == totlen);
892 cmd[pos] = '\0';
893
894 free(curargv);
895 *target = cmd;
896 return totlen;
897
898err:
899 while(argc--)
900 sdsfree(curargv[argc]);
901 free(curargv);
902
903 if (curarg != NULL)
904 sdsfree(curarg);
905
906 /* No need to check cmd since it is the last statement that can fail,
907 * but do it anyway to be as defensive as possible. */
908 if (cmd != NULL)
909 free(cmd);
910
911 return -1;
912}
913
914/* Format a command according to the Redis protocol. This function
915 * takes a format similar to printf:
916 *
917 * %s represents a C null terminated string you want to interpolate
918 * %b represents a binary safe string
919 *
920 * When using %b you need to provide both the pointer to the string
921 * and the length in bytes. Examples:
922 *
923 * len = redisFormatCommand(target, "GET %s", mykey);
924 * len = redisFormatCommand(target, "SET %s %b", mykey, myval, myvallen);
925 */
926int redisFormatCommand(char **target, const char *format, ...) {
927 va_list ap;
928 int len;
929 va_start(ap,format);
930 len = redisvFormatCommand(target,format,ap);
931 va_end(ap);
932 return len;
933}
934
935/* Format a command according to the Redis protocol. This function takes the
936 * number of arguments, an array with arguments and an array with their
937 * lengths. If the latter is set to NULL, strlen will be used to compute the
938 * argument lengths.
939 */
940int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) {
941 char *cmd = NULL; /* final command */
942 int pos; /* position in final command */
943 size_t len;
944 int totlen, j;
945
946 /* Calculate number of bytes needed for the command */
947 totlen = 1+intlen(argc)+2;
948 for (j = 0; j < argc; j++) {
949 len = argvlen ? argvlen[j] : strlen(argv[j]);
950 totlen += bulklen(len);
951 }
952
953 /* Build the command at protocol level */
954 cmd = malloc(totlen+1);
955 if (cmd == NULL)
956 return -1;
957
958 pos = sprintf(cmd,"*%d\r\n",argc);
959 for (j = 0; j < argc; j++) {
960 len = argvlen ? argvlen[j] : strlen(argv[j]);
961 pos += sprintf(cmd+pos,"$%zu\r\n",len);
962 memcpy(cmd+pos,argv[j],len);
963 pos += len;
964 cmd[pos++] = '\r';
965 cmd[pos++] = '\n';
966 }
967 assert(pos == totlen);
968 cmd[pos] = '\0';
969
970 *target = cmd;
971 return totlen;
972}
973
974void __redisSetError(redisContext *c, int type, const char *str) {
975 size_t len;
976
977 c->err = type;
978 if (str != NULL) {
979 len = strlen(str);
980 len = len < (sizeof(c->errstr)-1) ? len : (sizeof(c->errstr)-1);
981 memcpy(c->errstr,str,len);
982 c->errstr[len] = '\0';
983 } else {
984 /* Only REDIS_ERR_IO may lack a description! */
985 assert(type == REDIS_ERR_IO);
986 strerror_r(errno,c->errstr,sizeof(c->errstr));
987 }
988}
989
990static redisContext *redisContextInit(void) {
991 redisContext *c;
992
993 c = calloc(1,sizeof(redisContext));
994 if (c == NULL)
995 return NULL;
996
997 c->err = 0;
998 c->errstr[0] = '\0';
999 c->obuf = sdsempty();
1000 c->reader = redisReaderCreate();
1001 return c;
1002}
1003
1004void redisFree(redisContext *c) {
1005 if (c->fd > 0)
1006 close(c->fd);
1007 if (c->obuf != NULL)
1008 sdsfree(c->obuf);
1009 if (c->reader != NULL)
1010 redisReaderFree(c->reader);
1011 free(c);
1012}
1013
1014/* Connect to a Redis instance. On error the field error in the returned
1015 * context will be set to the return value of the error function.
1016 * When no set of reply functions is given, the default set will be used. */
1017redisContext *redisConnect(const char *ip, int port) {
1018 redisContext *c = redisContextInit();
1019 c->flags |= REDIS_BLOCK;
1020 redisContextConnectTcp(c,ip,port,NULL);
1021 return c;
1022}
1023
1024redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv) {
1025 redisContext *c = redisContextInit();
1026 c->flags |= REDIS_BLOCK;
1027 redisContextConnectTcp(c,ip,port,&tv);
1028 return c;
1029}
1030
1031redisContext *redisConnectNonBlock(const char *ip, int port) {
1032 redisContext *c = redisContextInit();
1033 c->flags &= ~REDIS_BLOCK;
1034 redisContextConnectTcp(c,ip,port,NULL);
1035 return c;
1036}
1037
1038redisContext *redisConnectUnix(const char *path) {
1039 redisContext *c = redisContextInit();
1040 c->flags |= REDIS_BLOCK;
1041 redisContextConnectUnix(c,path,NULL);
1042 return c;
1043}
1044
1045redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv) {
1046 redisContext *c = redisContextInit();
1047 c->flags |= REDIS_BLOCK;
1048 redisContextConnectUnix(c,path,&tv);
1049 return c;
1050}
1051
1052redisContext *redisConnectUnixNonBlock(const char *path) {
1053 redisContext *c = redisContextInit();
1054 c->flags &= ~REDIS_BLOCK;
1055 redisContextConnectUnix(c,path,NULL);
1056 return c;
1057}
1058
1059/* Set read/write timeout on a blocking socket. */
1060int redisSetTimeout(redisContext *c, struct timeval tv) {
1061 if (c->flags & REDIS_BLOCK)
1062 return redisContextSetTimeout(c,tv);
1063 return REDIS_ERR;
1064}
1065
1066/* Use this function to handle a read event on the descriptor. It will try
1067 * and read some bytes from the socket and feed them to the reply parser.
1068 *
1069 * After this function is called, you may use redisContextReadReply to
1070 * see if there is a reply available. */
1071int redisBufferRead(redisContext *c) {
1072 char buf[1024*16];
1073 int nread;
1074
1075 /* Return early when the context has seen an error. */
1076 if (c->err)
1077 return REDIS_ERR;
1078
1079 nread = read(c->fd,buf,sizeof(buf));
1080 if (nread == -1) {
1081 if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) {
1082 /* Try again later */
1083 } else {
1084 __redisSetError(c,REDIS_ERR_IO,NULL);
1085 return REDIS_ERR;
1086 }
1087 } else if (nread == 0) {
1088 __redisSetError(c,REDIS_ERR_EOF,"Server closed the connection");
1089 return REDIS_ERR;
1090 } else {
1091 if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) {
1092 __redisSetError(c,c->reader->err,c->reader->errstr);
1093 return REDIS_ERR;
1094 }
1095 }
1096 return REDIS_OK;
1097}
1098
1099/* Write the output buffer to the socket.
1100 *
1101 * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was
1102 * succesfully written to the socket. When the buffer is empty after the
1103 * write operation, "done" is set to 1 (if given).
1104 *
1105 * Returns REDIS_ERR if an error occured trying to write and sets
1106 * c->errstr to hold the appropriate error string.
1107 */
1108int redisBufferWrite(redisContext *c, int *done) {
1109 int nwritten;
1110
1111 /* Return early when the context has seen an error. */
1112 if (c->err)
1113 return REDIS_ERR;
1114
1115 if (sdslen(c->obuf) > 0) {
1116 nwritten = write(c->fd,c->obuf,sdslen(c->obuf));
1117 if (nwritten == -1) {
1118 if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) {
1119 /* Try again later */
1120 } else {
1121 __redisSetError(c,REDIS_ERR_IO,NULL);
1122 return REDIS_ERR;
1123 }
1124 } else if (nwritten > 0) {
1125 if (nwritten == (signed)sdslen(c->obuf)) {
1126 sdsfree(c->obuf);
1127 c->obuf = sdsempty();
1128 } else {
1129 c->obuf = sdsrange(c->obuf,nwritten,-1);
1130 }
1131 }
1132 }
1133 if (done != NULL) *done = (sdslen(c->obuf) == 0);
1134 return REDIS_OK;
1135}
1136
1137/* Internal helper function to try and get a reply from the reader,
1138 * or set an error in the context otherwise. */
1139int redisGetReplyFromReader(redisContext *c, void **reply) {
1140 if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) {
1141 __redisSetError(c,c->reader->err,c->reader->errstr);
1142 return REDIS_ERR;
1143 }
1144 return REDIS_OK;
1145}
1146
1147int redisGetReply(redisContext *c, void **reply) {
1148 int wdone = 0;
1149 void *aux = NULL;
1150
1151 /* Try to read pending replies */
1152 if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
1153 return REDIS_ERR;
1154
1155 /* For the blocking context, flush output buffer and read reply */
1156 if (aux == NULL && c->flags & REDIS_BLOCK) {
1157 /* Write until done */
1158 do {
1159 if (redisBufferWrite(c,&wdone) == REDIS_ERR)
1160 return REDIS_ERR;
1161 } while (!wdone);
1162
1163 /* Read until there is a reply */
1164 do {
1165 if (redisBufferRead(c) == REDIS_ERR)
1166 return REDIS_ERR;
1167 if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
1168 return REDIS_ERR;
1169 } while (aux == NULL);
1170 }
1171
1172 /* Set reply object */
1173 if (reply != NULL) *reply = aux;
1174 return REDIS_OK;
1175}
1176
1177
1178/* Helper function for the redisAppendCommand* family of functions.
1179 *
1180 * Write a formatted command to the output buffer. When this family
1181 * is used, you need to call redisGetReply yourself to retrieve
1182 * the reply (or replies in pub/sub).
1183 */
1184int __redisAppendCommand(redisContext *c, char *cmd, size_t len) {
1185 sds newbuf;
1186
1187 newbuf = sdscatlen(c->obuf,cmd,len);
1188 if (newbuf == NULL) {
1189 __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1190 return REDIS_ERR;
1191 }
1192
1193 c->obuf = newbuf;
1194 return REDIS_OK;
1195}
1196
1197int redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
1198 char *cmd;
1199 int len;
1200
1201 len = redisvFormatCommand(&cmd,format,ap);
1202 if (len == -1) {
1203 __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1204 return REDIS_ERR;
1205 }
1206
1207 if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
1208 free(cmd);
1209 return REDIS_ERR;
1210 }
1211
1212 free(cmd);
1213 return REDIS_OK;
1214}
1215
1216int redisAppendCommand(redisContext *c, const char *format, ...) {
1217 va_list ap;
1218 int ret;
1219
1220 va_start(ap,format);
1221 ret = redisvAppendCommand(c,format,ap);
1222 va_end(ap);
1223 return ret;
1224}
1225
1226int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1227 char *cmd;
1228 int len;
1229
1230 len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
1231 if (len == -1) {
1232 __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1233 return REDIS_ERR;
1234 }
1235
1236 if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
1237 free(cmd);
1238 return REDIS_ERR;
1239 }
1240
1241 free(cmd);
1242 return REDIS_OK;
1243}
1244
1245/* Helper function for the redisCommand* family of functions.
1246 *
1247 * Write a formatted command to the output buffer. If the given context is
1248 * blocking, immediately read the reply into the "reply" pointer. When the
1249 * context is non-blocking, the "reply" pointer will not be used and the
1250 * command is simply appended to the write buffer.
1251 *
1252 * Returns the reply when a reply was succesfully retrieved. Returns NULL
1253 * otherwise. When NULL is returned in a blocking context, the error field
1254 * in the context will be set.
1255 */
1256static void *__redisBlockForReply(redisContext *c) {
1257 void *reply;
1258
1259 if (c->flags & REDIS_BLOCK) {
1260 if (redisGetReply(c,&reply) != REDIS_OK)
1261 return NULL;
1262 return reply;
1263 }
1264 return NULL;
1265}
1266
1267void *redisvCommand(redisContext *c, const char *format, va_list ap) {
1268 if (redisvAppendCommand(c,format,ap) != REDIS_OK)
1269 return NULL;
1270 return __redisBlockForReply(c);
1271}
1272
1273void *redisCommand(redisContext *c, const char *format, ...) {
1274 va_list ap;
1275 void *reply = NULL;
1276 va_start(ap,format);
1277 reply = redisvCommand(c,format,ap);
1278 va_end(ap);
1279 return reply;
1280}
1281
1282void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
1283 if (redisAppendCommandArgv(c,argc,argv,argvlen) != REDIS_OK)
1284 return NULL;
1285 return __redisBlockForReply(c);
1286}