]> git.saurik.com Git - redis.git/blame - deps/hiredis/hiredis.c
The hiredis lib shipped with Redis was updated to latest version.
[redis.git] / deps / hiredis / hiredis.c
CommitLineData
24f753a8 1/*
b66e5add 2 * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
3 * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
a1e97d69 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
b66e5add 32#include "fmacros.h"
24f753a8
PN
33#include <string.h>
34#include <stdlib.h>
35#include <unistd.h>
36#include <assert.h>
37#include <errno.h>
a1e97d69 38#include <ctype.h>
24f753a8
PN
39
40#include "hiredis.h"
41#include "net.h"
42#include "sds.h"
24f753a8
PN
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);
24f753a8 49
b66e5add 50/* Default set of functions to build the reply. Keep in mind that such a
51 * function returning NULL is interpreted as OOM. */
24f753a8
PN
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) {
b66e5add 62 redisReply *r = calloc(1,sizeof(*r));
63
64 if (r == NULL)
65 return NULL;
24f753a8 66
24f753a8
PN
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:
b66e5add 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 }
24f753a8 86 break;
a1e97d69
PN
87 case REDIS_REPLY_ERROR:
88 case REDIS_REPLY_STATUS:
89 case REDIS_REPLY_STRING:
b66e5add 90 if (r->str != NULL)
91 free(r->str);
24f753a8
PN
92 break;
93 }
94 free(r);
95}
96
97static void *createStringObject(const redisReadTask *task, char *str, size_t len) {
b66e5add 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 ||
24f753a8
PN
112 task->type == REDIS_REPLY_STATUS ||
113 task->type == REDIS_REPLY_STRING);
114
115 /* Copy string value */
b66e5add 116 memcpy(buf,str,len);
117 buf[len] = '\0';
118 r->str = buf;
24f753a8
PN
119 r->len = len;
120
121 if (task->parent) {
b66e5add 122 parent = task->parent->obj;
24f753a8
PN
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) {
b66e5add 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
24f753a8 144 r->elements = elements;
b66e5add 145
24f753a8 146 if (task->parent) {
b66e5add 147 parent = task->parent->obj;
24f753a8
PN
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) {
b66e5add 155 redisReply *r, *parent;
156
157 r = createReplyObject(REDIS_REPLY_INTEGER);
158 if (r == NULL)
159 return NULL;
160
24f753a8 161 r->integer = value;
b66e5add 162
24f753a8 163 if (task->parent) {
b66e5add 164 parent = task->parent->obj;
24f753a8
PN
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) {
b66e5add 172 redisReply *r, *parent;
173
174 r = createReplyObject(REDIS_REPLY_NIL);
175 if (r == NULL)
176 return NULL;
177
24f753a8 178 if (task->parent) {
b66e5add 179 parent = task->parent->obj;
24f753a8
PN
180 assert(parent->type == REDIS_REPLY_ARRAY);
181 parent->element[task->idx] = r;
182 }
183 return r;
184}
185
b66e5add 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
24f753a8
PN
249static char *readBytes(redisReader *r, unsigned int bytes) {
250 char *p;
a1e97d69 251 if (r->len-r->pos >= bytes) {
24f753a8
PN
252 p = r->buf+r->pos;
253 r->pos += bytes;
254 return p;
255 }
256 return NULL;
257}
258
a1e97d69
PN
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;
57c9babd 273 } else {
a1e97d69
PN
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;
57c9babd
PN
309 }
310 }
a1e97d69
PN
311
312 return mult*v;
afc156c2
PN
313}
314
24f753a8 315static char *readLine(redisReader *r, int *_len) {
afc156c2 316 char *p, *s;
24f753a8 317 int len;
afc156c2
PN
318
319 p = r->buf+r->pos;
a1e97d69 320 s = seekNewline(p,(r->len-r->pos));
24f753a8 321 if (s != NULL) {
24f753a8
PN
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;
afc156c2
PN
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 }
24f753a8 338
afc156c2
PN
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 }
24f753a8
PN
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) {
9703b1b3
PN
362 if (cur->type == REDIS_REPLY_INTEGER) {
363 if (r->fn && r->fn->createInteger)
a1e97d69 364 obj = r->fn->createInteger(cur,readLongLong(p));
9703b1b3
PN
365 else
366 obj = (void*)REDIS_REPLY_INTEGER;
24f753a8 367 } else {
9703b1b3
PN
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);
24f753a8
PN
373 }
374
b66e5add 375 if (obj == NULL) {
376 __redisReaderSetErrorOOM(r);
377 return REDIS_ERR;
378 }
379
a1e97d69
PN
380 /* Set reply if this is the root object. */
381 if (r->ridx == 0) r->reply = obj;
24f753a8 382 moveToNextTask(r);
b66e5add 383 return REDIS_OK;
24f753a8 384 }
b66e5add 385
386 return REDIS_ERR;
24f753a8
PN
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;
a1e97d69 395 int success = 0;
24f753a8
PN
396
397 p = r->buf+r->pos;
a1e97d69 398 s = seekNewline(p,r->len-r->pos);
24f753a8
PN
399 if (s != NULL) {
400 p = r->buf+r->pos;
401 bytelen = s-(r->buf+r->pos)+2; /* include \r\n */
a1e97d69 402 len = readLongLong(p);
24f753a8
PN
403
404 if (len < 0) {
405 /* The nil object can always be created. */
9703b1b3
PN
406 if (r->fn && r->fn->createNil)
407 obj = r->fn->createNil(cur);
408 else
409 obj = (void*)REDIS_REPLY_NIL;
a1e97d69 410 success = 1;
24f753a8
PN
411 } else {
412 /* Only continue when the buffer contains the entire bulk item. */
413 bytelen += len+2; /* include \r\n */
a1e97d69 414 if (r->pos+bytelen <= r->len) {
9703b1b3
PN
415 if (r->fn && r->fn->createString)
416 obj = r->fn->createString(cur,s+2,len);
417 else
418 obj = (void*)REDIS_REPLY_STRING;
a1e97d69 419 success = 1;
24f753a8
PN
420 }
421 }
422
423 /* Proceed when obj was created. */
a1e97d69 424 if (success) {
b66e5add 425 if (obj == NULL) {
426 __redisReaderSetErrorOOM(r);
427 return REDIS_ERR;
428 }
429
24f753a8 430 r->pos += bytelen;
a1e97d69
PN
431
432 /* Set reply if this is the root object. */
433 if (r->ridx == 0) r->reply = obj;
24f753a8 434 moveToNextTask(r);
b66e5add 435 return REDIS_OK;
24f753a8
PN
436 }
437 }
b66e5add 438
439 return REDIS_ERR;
24f753a8
PN
440}
441
442static int processMultiBulkItem(redisReader *r) {
443 redisReadTask *cur = &(r->rstack[r->ridx]);
444 void *obj;
445 char *p;
446 long elements;
a1e97d69
PN
447 int root = 0;
448
b66e5add 449 /* Set error for nested multi bulks with depth > 2 */
450 if (r->ridx == 3) {
451 __redisReaderSetError(r,REDIS_ERR_PROTOCOL,
452 "No support for nested multi bulk replies with depth > 2");
453 return REDIS_ERR;
a1e97d69 454 }
24f753a8
PN
455
456 if ((p = readLine(r,NULL)) != NULL) {
a1e97d69
PN
457 elements = readLongLong(p);
458 root = (r->ridx == 0);
459
24f753a8 460 if (elements == -1) {
9703b1b3
PN
461 if (r->fn && r->fn->createNil)
462 obj = r->fn->createNil(cur);
463 else
464 obj = (void*)REDIS_REPLY_NIL;
b66e5add 465
466 if (obj == NULL) {
467 __redisReaderSetErrorOOM(r);
468 return REDIS_ERR;
469 }
470
24f753a8
PN
471 moveToNextTask(r);
472 } else {
9703b1b3
PN
473 if (r->fn && r->fn->createArray)
474 obj = r->fn->createArray(cur,elements);
475 else
476 obj = (void*)REDIS_REPLY_ARRAY;
24f753a8 477
b66e5add 478 if (obj == NULL) {
479 __redisReaderSetErrorOOM(r);
480 return REDIS_ERR;
481 }
482
24f753a8
PN
483 /* Modify task stack when there are more than 0 elements. */
484 if (elements > 0) {
485 cur->elements = elements;
a1e97d69 486 cur->obj = obj;
24f753a8
PN
487 r->ridx++;
488 r->rstack[r->ridx].type = -1;
489 r->rstack[r->ridx].elements = -1;
24f753a8 490 r->rstack[r->ridx].idx = 0;
a1e97d69
PN
491 r->rstack[r->ridx].obj = NULL;
492 r->rstack[r->ridx].parent = cur;
493 r->rstack[r->ridx].privdata = r->privdata;
24f753a8
PN
494 } else {
495 moveToNextTask(r);
496 }
497 }
498
a1e97d69
PN
499 /* Set reply if this is the root object. */
500 if (root) r->reply = obj;
b66e5add 501 return REDIS_OK;
24f753a8 502 }
b66e5add 503
504 return REDIS_ERR;
24f753a8
PN
505}
506
507static int processItem(redisReader *r) {
508 redisReadTask *cur = &(r->rstack[r->ridx]);
509 char *p;
24f753a8
PN
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:
b66e5add 531 __redisReaderSetErrorProtocolByte(r,*p);
532 return REDIS_ERR;
24f753a8
PN
533 }
534 } else {
535 /* could not consume 1 byte */
b66e5add 536 return REDIS_ERR;
24f753a8
PN
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:
a1e97d69 551 assert(NULL);
b66e5add 552 return REDIS_ERR; /* Avoid warning. */
24f753a8
PN
553 }
554}
555
b66e5add 556redisReader *redisReaderCreate(void) {
557 redisReader *r;
24f753a8 558
b66e5add 559 r = calloc(sizeof(redisReader),1);
560 if (r == NULL)
561 return NULL;
afc156c2 562
b66e5add 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;
a1e97d69 570 }
a1e97d69 571
b66e5add 572 r->ridx = -1;
573 return r;
24f753a8
PN
574}
575
b66e5add 576void redisReaderFree(redisReader *r) {
577 if (r->reply != NULL && r->fn && r->fn->freeObject)
24f753a8
PN
578 r->fn->freeObject(r->reply);
579 if (r->buf != NULL)
580 sdsfree(r->buf);
581 free(r);
582}
583
b66e5add 584int redisReaderFeed(redisReader *r, const char *buf, size_t len) {
585 sds newbuf;
24f753a8 586
b66e5add 587 /* Return early when this reader is in an erroneous state. */
588 if (r->err)
589 return REDIS_ERR;
24f753a8
PN
590
591 /* Copy the provided buffer. */
a1e97d69 592 if (buf != NULL && len >= 1) {
9703b1b3
PN
593 /* Destroy internal buffer when it is empty and is quite large. */
594 if (r->len == 0 && sdsavail(r->buf) > 16*1024) {
595 sdsfree(r->buf);
596 r->buf = sdsempty();
597 r->pos = 0;
b66e5add 598
599 /* r->buf should not be NULL since we just free'd a larger one. */
600 assert(r->buf != NULL);
601 }
602
603 newbuf = sdscatlen(r->buf,buf,len);
604 if (newbuf == NULL) {
605 __redisReaderSetErrorOOM(r);
606 return REDIS_ERR;
9703b1b3 607 }
b66e5add 608
609 r->buf = newbuf;
a1e97d69
PN
610 r->len = sdslen(r->buf);
611 }
b66e5add 612
613 return REDIS_OK;
24f753a8
PN
614}
615
b66e5add 616int redisReaderGetReply(redisReader *r, void **reply) {
617 /* Default target pointer to NULL. */
618 if (reply != NULL)
619 *reply = NULL;
620
621 /* Return early when this reader is in an erroneous state. */
622 if (r->err)
623 return REDIS_ERR;
24f753a8
PN
624
625 /* When the buffer is empty, there will never be a reply. */
a1e97d69 626 if (r->len == 0)
24f753a8
PN
627 return REDIS_OK;
628
629 /* Set first item to process when the stack is empty. */
630 if (r->ridx == -1) {
631 r->rstack[0].type = -1;
632 r->rstack[0].elements = -1;
24f753a8 633 r->rstack[0].idx = -1;
a1e97d69
PN
634 r->rstack[0].obj = NULL;
635 r->rstack[0].parent = NULL;
636 r->rstack[0].privdata = r->privdata;
24f753a8
PN
637 r->ridx = 0;
638 }
639
640 /* Process items in reply. */
641 while (r->ridx >= 0)
b66e5add 642 if (processItem(r) != REDIS_OK)
24f753a8
PN
643 break;
644
b66e5add 645 /* Return ASAP when an error occurred. */
646 if (r->err)
647 return REDIS_ERR;
648
9703b1b3
PN
649 /* Discard part of the buffer when we've consumed at least 1k, to avoid
650 * doing unnecessary calls to memmove() in sds.c. */
651 if (r->pos >= 1024) {
652 r->buf = sdsrange(r->buf,r->pos,-1);
24f753a8 653 r->pos = 0;
a1e97d69 654 r->len = sdslen(r->buf);
24f753a8
PN
655 }
656
657 /* Emit a reply when there is one. */
658 if (r->ridx == -1) {
b66e5add 659 if (reply != NULL)
660 *reply = r->reply;
24f753a8 661 r->reply = NULL;
24f753a8
PN
662 }
663 return REDIS_OK;
664}
665
666/* Calculate the number of bytes needed to represent an integer as string. */
667static int intlen(int i) {
668 int len = 0;
669 if (i < 0) {
670 len++;
671 i = -i;
672 }
673 do {
674 len++;
675 i /= 10;
676 } while(i);
677 return len;
678}
679
b66e5add 680/* Helper that calculates the bulk length given a certain string length. */
681static size_t bulklen(size_t len) {
682 return 1+intlen(len)+2+len+2;
24f753a8
PN
683}
684
685int redisvFormatCommand(char **target, const char *format, va_list ap) {
b66e5add 686 const char *c = format;
24f753a8
PN
687 char *cmd = NULL; /* final command */
688 int pos; /* position in final command */
b66e5add 689 sds curarg, newarg; /* current argument */
9703b1b3 690 int touched = 0; /* was the current argument touched? */
b66e5add 691 char **curargv = NULL, **newargv = NULL;
692 int argc = 0;
24f753a8 693 int totlen = 0;
b66e5add 694 int j;
24f753a8
PN
695
696 /* Abort if there is not target to set */
697 if (target == NULL)
698 return -1;
699
700 /* Build the command string accordingly to protocol */
b66e5add 701 curarg = sdsempty();
702 if (curarg == NULL)
703 return -1;
704
24f753a8
PN
705 while(*c != '\0') {
706 if (*c != '%' || c[1] == '\0') {
707 if (*c == ' ') {
9703b1b3 708 if (touched) {
b66e5add 709 newargv = realloc(curargv,sizeof(char*)*(argc+1));
710 if (newargv == NULL) goto err;
711 curargv = newargv;
712 curargv[argc++] = curarg;
713 totlen += bulklen(sdslen(curarg));
714
715 /* curarg is put in argv so it can be overwritten. */
716 curarg = sdsempty();
717 if (curarg == NULL) goto err;
9703b1b3 718 touched = 0;
24f753a8
PN
719 }
720 } else {
b66e5add 721 newarg = sdscatlen(curarg,c,1);
722 if (newarg == NULL) goto err;
723 curarg = newarg;
9703b1b3 724 touched = 1;
24f753a8
PN
725 }
726 } else {
b66e5add 727 char *arg;
728 size_t size;
729
730 /* Set newarg so it can be checked even if it is not touched. */
731 newarg = curarg;
732
24f753a8
PN
733 switch(c[1]) {
734 case 's':
735 arg = va_arg(ap,char*);
a1e97d69
PN
736 size = strlen(arg);
737 if (size > 0)
b66e5add 738 newarg = sdscatlen(curarg,arg,size);
24f753a8
PN
739 break;
740 case 'b':
741 arg = va_arg(ap,char*);
742 size = va_arg(ap,size_t);
a1e97d69 743 if (size > 0)
b66e5add 744 newarg = sdscatlen(curarg,arg,size);
24f753a8
PN
745 break;
746 case '%':
b66e5add 747 newarg = sdscat(curarg,"%");
24f753a8 748 break;
a1e97d69
PN
749 default:
750 /* Try to detect printf format */
751 {
b66e5add 752 static const char intfmts[] = "diouxX";
a1e97d69
PN
753 char _format[16];
754 const char *_p = c+1;
755 size_t _l = 0;
756 va_list _cpy;
757
758 /* Flags */
759 if (*_p != '\0' && *_p == '#') _p++;
760 if (*_p != '\0' && *_p == '0') _p++;
761 if (*_p != '\0' && *_p == '-') _p++;
762 if (*_p != '\0' && *_p == ' ') _p++;
763 if (*_p != '\0' && *_p == '+') _p++;
764
765 /* Field width */
766 while (*_p != '\0' && isdigit(*_p)) _p++;
767
768 /* Precision */
769 if (*_p == '.') {
770 _p++;
771 while (*_p != '\0' && isdigit(*_p)) _p++;
772 }
773
b66e5add 774 /* Copy va_list before consuming with va_arg */
775 va_copy(_cpy,ap);
776
777 /* Integer conversion (without modifiers) */
778 if (strchr(intfmts,*_p) != NULL) {
779 va_arg(ap,int);
780 goto fmt_valid;
781 }
782
783 /* Double conversion (without modifiers) */
784 if (strchr("eEfFgGaA",*_p) != NULL) {
785 va_arg(ap,double);
786 goto fmt_valid;
787 }
788
789 /* Size: char */
790 if (_p[0] == 'h' && _p[1] == 'h') {
791 _p += 2;
792 if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
793 va_arg(ap,int); /* char gets promoted to int */
794 goto fmt_valid;
a1e97d69 795 }
b66e5add 796 goto fmt_invalid;
a1e97d69
PN
797 }
798
b66e5add 799 /* Size: short */
800 if (_p[0] == 'h') {
801 _p += 1;
802 if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
803 va_arg(ap,int); /* short gets promoted to int */
804 goto fmt_valid;
a1e97d69 805 }
b66e5add 806 goto fmt_invalid;
a1e97d69
PN
807 }
808
b66e5add 809 /* Size: long long */
810 if (_p[0] == 'l' && _p[1] == 'l') {
811 _p += 2;
812 if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
813 va_arg(ap,long long);
814 goto fmt_valid;
815 }
816 goto fmt_invalid;
817 }
818
819 /* Size: long */
820 if (_p[0] == 'l') {
821 _p += 1;
822 if (*_p != '\0' && strchr(intfmts,*_p) != NULL) {
823 va_arg(ap,long);
824 goto fmt_valid;
825 }
826 goto fmt_invalid;
827 }
828
829 fmt_invalid:
830 va_end(_cpy);
831 goto err;
832
833 fmt_valid:
834 _l = (_p+1)-c;
835 if (_l < sizeof(_format)-2) {
836 memcpy(_format,c,_l);
837 _format[_l] = '\0';
838 newarg = sdscatvprintf(curarg,_format,_cpy);
839
840 /* Update current position (note: outer blocks
841 * increment c twice so compensate here) */
842 c = _p-1;
843 }
844
845 va_end(_cpy);
846 break;
a1e97d69 847 }
24f753a8 848 }
b66e5add 849
850 if (newarg == NULL) goto err;
851 curarg = newarg;
852
9703b1b3 853 touched = 1;
24f753a8
PN
854 c++;
855 }
856 c++;
857 }
858
859 /* Add the last argument if needed */
9703b1b3 860 if (touched) {
b66e5add 861 newargv = realloc(curargv,sizeof(char*)*(argc+1));
862 if (newargv == NULL) goto err;
863 curargv = newargv;
864 curargv[argc++] = curarg;
865 totlen += bulklen(sdslen(curarg));
24f753a8 866 } else {
b66e5add 867 sdsfree(curarg);
24f753a8
PN
868 }
869
b66e5add 870 /* Clear curarg because it was put in curargv or was free'd. */
871 curarg = NULL;
872
24f753a8
PN
873 /* Add bytes needed to hold multi bulk count */
874 totlen += 1+intlen(argc)+2;
875
876 /* Build the command at protocol level */
877 cmd = malloc(totlen+1);
b66e5add 878 if (cmd == NULL) goto err;
879
24f753a8
PN
880 pos = sprintf(cmd,"*%d\r\n",argc);
881 for (j = 0; j < argc; j++) {
b66e5add 882 pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(curargv[j]));
883 memcpy(cmd+pos,curargv[j],sdslen(curargv[j]));
884 pos += sdslen(curargv[j]);
885 sdsfree(curargv[j]);
24f753a8
PN
886 cmd[pos++] = '\r';
887 cmd[pos++] = '\n';
888 }
889 assert(pos == totlen);
b66e5add 890 cmd[pos] = '\0';
891
892 free(curargv);
24f753a8
PN
893 *target = cmd;
894 return totlen;
b66e5add 895
896err:
897 while(argc--)
898 sdsfree(curargv[argc]);
899 free(curargv);
900
901 if (curarg != NULL)
902 sdsfree(curarg);
903
904 /* No need to check cmd since it is the last statement that can fail,
905 * but do it anyway to be as defensive as possible. */
906 if (cmd != NULL)
907 free(cmd);
908
909 return -1;
24f753a8
PN
910}
911
912/* Format a command according to the Redis protocol. This function
913 * takes a format similar to printf:
914 *
915 * %s represents a C null terminated string you want to interpolate
916 * %b represents a binary safe string
917 *
918 * When using %b you need to provide both the pointer to the string
919 * and the length in bytes. Examples:
920 *
921 * len = redisFormatCommand(target, "GET %s", mykey);
922 * len = redisFormatCommand(target, "SET %s %b", mykey, myval, myvallen);
923 */
924int redisFormatCommand(char **target, const char *format, ...) {
925 va_list ap;
926 int len;
927 va_start(ap,format);
928 len = redisvFormatCommand(target,format,ap);
929 va_end(ap);
930 return len;
931}
932
933/* Format a command according to the Redis protocol. This function takes the
934 * number of arguments, an array with arguments and an array with their
935 * lengths. If the latter is set to NULL, strlen will be used to compute the
936 * argument lengths.
937 */
938int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) {
939 char *cmd = NULL; /* final command */
940 int pos; /* position in final command */
941 size_t len;
942 int totlen, j;
943
944 /* Calculate number of bytes needed for the command */
945 totlen = 1+intlen(argc)+2;
946 for (j = 0; j < argc; j++) {
947 len = argvlen ? argvlen[j] : strlen(argv[j]);
b66e5add 948 totlen += bulklen(len);
24f753a8
PN
949 }
950
951 /* Build the command at protocol level */
952 cmd = malloc(totlen+1);
b66e5add 953 if (cmd == NULL)
954 return -1;
955
24f753a8
PN
956 pos = sprintf(cmd,"*%d\r\n",argc);
957 for (j = 0; j < argc; j++) {
958 len = argvlen ? argvlen[j] : strlen(argv[j]);
959 pos += sprintf(cmd+pos,"$%zu\r\n",len);
960 memcpy(cmd+pos,argv[j],len);
961 pos += len;
962 cmd[pos++] = '\r';
963 cmd[pos++] = '\n';
964 }
965 assert(pos == totlen);
b66e5add 966 cmd[pos] = '\0';
967
24f753a8
PN
968 *target = cmd;
969 return totlen;
970}
971
b66e5add 972void __redisSetError(redisContext *c, int type, const char *str) {
973 size_t len;
974
24f753a8 975 c->err = type;
b66e5add 976 if (str != NULL) {
977 len = strlen(str);
978 len = len < (sizeof(c->errstr)-1) ? len : (sizeof(c->errstr)-1);
979 memcpy(c->errstr,str,len);
980 c->errstr[len] = '\0';
24f753a8
PN
981 } else {
982 /* Only REDIS_ERR_IO may lack a description! */
983 assert(type == REDIS_ERR_IO);
b66e5add 984 strerror_r(errno,c->errstr,sizeof(c->errstr));
24f753a8
PN
985 }
986}
987
9703b1b3 988static redisContext *redisContextInit(void) {
b66e5add 989 redisContext *c;
990
991 c = calloc(1,sizeof(redisContext));
992 if (c == NULL)
993 return NULL;
994
24f753a8 995 c->err = 0;
b66e5add 996 c->errstr[0] = '\0';
24f753a8 997 c->obuf = sdsempty();
b66e5add 998 c->reader = redisReaderCreate();
24f753a8
PN
999 return c;
1000}
1001
1002void redisFree(redisContext *c) {
9703b1b3 1003 if (c->fd > 0)
24f753a8 1004 close(c->fd);
24f753a8
PN
1005 if (c->obuf != NULL)
1006 sdsfree(c->obuf);
1007 if (c->reader != NULL)
b66e5add 1008 redisReaderFree(c->reader);
24f753a8
PN
1009 free(c);
1010}
1011
1012/* Connect to a Redis instance. On error the field error in the returned
1013 * context will be set to the return value of the error function.
1014 * When no set of reply functions is given, the default set will be used. */
1015redisContext *redisConnect(const char *ip, int port) {
1016 redisContext *c = redisContextInit();
1017 c->flags |= REDIS_BLOCK;
9703b1b3
PN
1018 redisContextConnectTcp(c,ip,port,NULL);
1019 return c;
1020}
1021
1022redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv) {
1023 redisContext *c = redisContextInit();
1024 c->flags |= REDIS_BLOCK;
1025 redisContextConnectTcp(c,ip,port,&tv);
24f753a8
PN
1026 return c;
1027}
1028
1029redisContext *redisConnectNonBlock(const char *ip, int port) {
1030 redisContext *c = redisContextInit();
1031 c->flags &= ~REDIS_BLOCK;
9703b1b3 1032 redisContextConnectTcp(c,ip,port,NULL);
24f753a8
PN
1033 return c;
1034}
1035
1036redisContext *redisConnectUnix(const char *path) {
1037 redisContext *c = redisContextInit();
1038 c->flags |= REDIS_BLOCK;
9703b1b3
PN
1039 redisContextConnectUnix(c,path,NULL);
1040 return c;
1041}
1042
1043redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv) {
1044 redisContext *c = redisContextInit();
1045 c->flags |= REDIS_BLOCK;
1046 redisContextConnectUnix(c,path,&tv);
24f753a8
PN
1047 return c;
1048}
1049
1050redisContext *redisConnectUnixNonBlock(const char *path) {
1051 redisContext *c = redisContextInit();
1052 c->flags &= ~REDIS_BLOCK;
9703b1b3 1053 redisContextConnectUnix(c,path,NULL);
24f753a8
PN
1054 return c;
1055}
1056
9703b1b3
PN
1057/* Set read/write timeout on a blocking socket. */
1058int redisSetTimeout(redisContext *c, struct timeval tv) {
1059 if (c->flags & REDIS_BLOCK)
1060 return redisContextSetTimeout(c,tv);
1061 return REDIS_ERR;
1062}
1063
24f753a8
PN
1064/* Use this function to handle a read event on the descriptor. It will try
1065 * and read some bytes from the socket and feed them to the reply parser.
1066 *
1067 * After this function is called, you may use redisContextReadReply to
1068 * see if there is a reply available. */
1069int redisBufferRead(redisContext *c) {
b66e5add 1070 char buf[2048];
1071 int nread;
1072
1073 /* Return early when the context has seen an error. */
1074 if (c->err)
1075 return REDIS_ERR;
1076
1077 nread = read(c->fd,buf,sizeof(buf));
24f753a8 1078 if (nread == -1) {
9703b1b3 1079 if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) {
24f753a8
PN
1080 /* Try again later */
1081 } else {
1082 __redisSetError(c,REDIS_ERR_IO,NULL);
1083 return REDIS_ERR;
1084 }
1085 } else if (nread == 0) {
b66e5add 1086 __redisSetError(c,REDIS_ERR_EOF,"Server closed the connection");
24f753a8
PN
1087 return REDIS_ERR;
1088 } else {
b66e5add 1089 if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) {
1090 __redisSetError(c,c->reader->err,c->reader->errstr);
1091 return REDIS_ERR;
1092 }
24f753a8
PN
1093 }
1094 return REDIS_OK;
1095}
1096
1097/* Write the output buffer to the socket.
1098 *
1099 * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was
1100 * succesfully written to the socket. When the buffer is empty after the
b66e5add 1101 * write operation, "done" is set to 1 (if given).
24f753a8
PN
1102 *
1103 * Returns REDIS_ERR if an error occured trying to write and sets
b66e5add 1104 * c->errstr to hold the appropriate error string.
24f753a8
PN
1105 */
1106int redisBufferWrite(redisContext *c, int *done) {
1107 int nwritten;
b66e5add 1108
1109 /* Return early when the context has seen an error. */
1110 if (c->err)
1111 return REDIS_ERR;
1112
24f753a8
PN
1113 if (sdslen(c->obuf) > 0) {
1114 nwritten = write(c->fd,c->obuf,sdslen(c->obuf));
1115 if (nwritten == -1) {
9703b1b3 1116 if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) {
24f753a8
PN
1117 /* Try again later */
1118 } else {
1119 __redisSetError(c,REDIS_ERR_IO,NULL);
1120 return REDIS_ERR;
1121 }
1122 } else if (nwritten > 0) {
1123 if (nwritten == (signed)sdslen(c->obuf)) {
1124 sdsfree(c->obuf);
1125 c->obuf = sdsempty();
1126 } else {
1127 c->obuf = sdsrange(c->obuf,nwritten,-1);
1128 }
1129 }
1130 }
1131 if (done != NULL) *done = (sdslen(c->obuf) == 0);
1132 return REDIS_OK;
1133}
1134
1135/* Internal helper function to try and get a reply from the reader,
1136 * or set an error in the context otherwise. */
1137int redisGetReplyFromReader(redisContext *c, void **reply) {
b66e5add 1138 if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) {
1139 __redisSetError(c,c->reader->err,c->reader->errstr);
24f753a8
PN
1140 return REDIS_ERR;
1141 }
1142 return REDIS_OK;
1143}
1144
1145int redisGetReply(redisContext *c, void **reply) {
1146 int wdone = 0;
1147 void *aux = NULL;
1148
1149 /* Try to read pending replies */
1150 if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
1151 return REDIS_ERR;
1152
1153 /* For the blocking context, flush output buffer and read reply */
1154 if (aux == NULL && c->flags & REDIS_BLOCK) {
1155 /* Write until done */
1156 do {
1157 if (redisBufferWrite(c,&wdone) == REDIS_ERR)
1158 return REDIS_ERR;
1159 } while (!wdone);
1160
1161 /* Read until there is a reply */
1162 do {
1163 if (redisBufferRead(c) == REDIS_ERR)
1164 return REDIS_ERR;
1165 if (redisGetReplyFromReader(c,&aux) == REDIS_ERR)
1166 return REDIS_ERR;
1167 } while (aux == NULL);
1168 }
1169
1170 /* Set reply object */
1171 if (reply != NULL) *reply = aux;
1172 return REDIS_OK;
1173}
1174
1175
1176/* Helper function for the redisAppendCommand* family of functions.
1177 *
1178 * Write a formatted command to the output buffer. When this family
1179 * is used, you need to call redisGetReply yourself to retrieve
1180 * the reply (or replies in pub/sub).
1181 */
b66e5add 1182int __redisAppendCommand(redisContext *c, char *cmd, size_t len) {
1183 sds newbuf;
1184
1185 newbuf = sdscatlen(c->obuf,cmd,len);
1186 if (newbuf == NULL) {
1187 __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1188 return REDIS_ERR;
1189 }
1190
1191 c->obuf = newbuf;
1192 return REDIS_OK;
24f753a8
PN
1193}
1194
b66e5add 1195int redisvAppendCommand(redisContext *c, const char *format, va_list ap) {
24f753a8
PN
1196 char *cmd;
1197 int len;
b66e5add 1198
24f753a8 1199 len = redisvFormatCommand(&cmd,format,ap);
b66e5add 1200 if (len == -1) {
1201 __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1202 return REDIS_ERR;
1203 }
1204
1205 if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
1206 free(cmd);
1207 return REDIS_ERR;
1208 }
1209
24f753a8 1210 free(cmd);
b66e5add 1211 return REDIS_OK;
24f753a8
PN
1212}
1213
b66e5add 1214int redisAppendCommand(redisContext *c, const char *format, ...) {
24f753a8 1215 va_list ap;
b66e5add 1216 int ret;
1217
24f753a8 1218 va_start(ap,format);
b66e5add 1219 ret = redisvAppendCommand(c,format,ap);
24f753a8 1220 va_end(ap);
b66e5add 1221 return ret;
24f753a8
PN
1222}
1223
b66e5add 1224int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
24f753a8
PN
1225 char *cmd;
1226 int len;
b66e5add 1227
24f753a8 1228 len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
b66e5add 1229 if (len == -1) {
1230 __redisSetError(c,REDIS_ERR_OOM,"Out of memory");
1231 return REDIS_ERR;
1232 }
1233
1234 if (__redisAppendCommand(c,cmd,len) != REDIS_OK) {
1235 free(cmd);
1236 return REDIS_ERR;
1237 }
1238
24f753a8 1239 free(cmd);
b66e5add 1240 return REDIS_OK;
24f753a8
PN
1241}
1242
1243/* Helper function for the redisCommand* family of functions.
1244 *
1245 * Write a formatted command to the output buffer. If the given context is
1246 * blocking, immediately read the reply into the "reply" pointer. When the
1247 * context is non-blocking, the "reply" pointer will not be used and the
1248 * command is simply appended to the write buffer.
1249 *
1250 * Returns the reply when a reply was succesfully retrieved. Returns NULL
1251 * otherwise. When NULL is returned in a blocking context, the error field
1252 * in the context will be set.
1253 */
b66e5add 1254static void *__redisBlockForReply(redisContext *c) {
1255 void *reply;
24f753a8
PN
1256
1257 if (c->flags & REDIS_BLOCK) {
b66e5add 1258 if (redisGetReply(c,&reply) != REDIS_OK)
1259 return NULL;
1260 return reply;
24f753a8
PN
1261 }
1262 return NULL;
1263}
1264
1265void *redisvCommand(redisContext *c, const char *format, va_list ap) {
b66e5add 1266 if (redisvAppendCommand(c,format,ap) != REDIS_OK)
1267 return NULL;
1268 return __redisBlockForReply(c);
24f753a8
PN
1269}
1270
1271void *redisCommand(redisContext *c, const char *format, ...) {
1272 va_list ap;
1273 void *reply = NULL;
1274 va_start(ap,format);
1275 reply = redisvCommand(c,format,ap);
1276 va_end(ap);
1277 return reply;
1278}
1279
1280void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) {
b66e5add 1281 if (redisAppendCommandArgv(c,argc,argv,argvlen) != REDIS_OK)
1282 return NULL;
1283 return __redisBlockForReply(c);
24f753a8 1284}