hiredis 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,157 @@
1
+ /*
2
+ * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
3
+ * All rights reserved.
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * * Redistributions of source code must retain the above copyright notice,
9
+ * this list of conditions and the following disclaimer.
10
+ * * Redistributions in binary form must reproduce the above copyright
11
+ * notice, this list of conditions and the following disclaimer in the
12
+ * documentation and/or other materials provided with the distribution.
13
+ * * Neither the name of Redis nor the names of its contributors may be used
14
+ * to endorse or promote products derived from this software without
15
+ * specific prior written permission.
16
+ *
17
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ * POSSIBILITY OF SUCH DAMAGE.
28
+ */
29
+
30
+ #ifndef __HIREDIS_H
31
+ #define __HIREDIS_H
32
+ #include <stdio.h> /* for size_t */
33
+ #include <stdarg.h> /* for va_list */
34
+
35
+ #define HIREDIS_MAJOR 0
36
+ #define HIREDIS_MINOR 9
37
+ #define HIREDIS_PATCH 0
38
+
39
+ #define REDIS_ERR -1
40
+ #define REDIS_OK 0
41
+
42
+ /* When an error occurs, the err flag in a context is set to hold the type of
43
+ * error that occured. REDIS_ERR_IO means there was an I/O error and you
44
+ * should use the "errno" variable to find out what is wrong.
45
+ * For other values, the "errstr" field will hold a description. */
46
+ #define REDIS_ERR_IO 1 /* error in read or write */
47
+ #define REDIS_ERR_EOF 3 /* eof */
48
+ #define REDIS_ERR_PROTOCOL 4 /* protocol error */
49
+ #define REDIS_ERR_OTHER 2 /* something else */
50
+
51
+ /* Connection type can be blocking or non-blocking and is set in the
52
+ * least significant bit of the flags field in redisContext. */
53
+ #define REDIS_BLOCK 0x1
54
+
55
+ /* Connection may be disconnected before being free'd. The second bit
56
+ * in the flags field is set when the context is connected. */
57
+ #define REDIS_CONNECTED 0x2
58
+
59
+ /* The async API might try to disconnect cleanly and flush the output
60
+ * buffer and read all subsequent replies before disconnecting.
61
+ * This flag means no new commands can come in and the connection
62
+ * should be terminated once all replies have been read. */
63
+ #define REDIS_DISCONNECTING 0x4
64
+
65
+ #define REDIS_REPLY_ERROR 0
66
+ #define REDIS_REPLY_STRING 1
67
+ #define REDIS_REPLY_ARRAY 2
68
+ #define REDIS_REPLY_INTEGER 3
69
+ #define REDIS_REPLY_NIL 4
70
+ #define REDIS_REPLY_STATUS 5
71
+
72
+ /* This is the reply object returned by redisCommand() */
73
+ typedef struct redisReply {
74
+ int type; /* REDIS_REPLY_* */
75
+ long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
76
+ int len; /* Length of string */
77
+ char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */
78
+ size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
79
+ struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
80
+ } redisReply;
81
+
82
+ typedef struct redisReadTask {
83
+ int type;
84
+ int elements; /* number of elements in multibulk container */
85
+ void *parent; /* optional pointer to parent object */
86
+ int idx; /* index in parent (array) object */
87
+ } redisReadTask;
88
+
89
+ typedef struct redisReplyObjectFunctions {
90
+ void *(*createString)(const redisReadTask*, char*, size_t);
91
+ void *(*createArray)(const redisReadTask*, int);
92
+ void *(*createInteger)(const redisReadTask*, long long);
93
+ void *(*createNil)(const redisReadTask*);
94
+ void (*freeObject)(void*);
95
+ } redisReplyObjectFunctions;
96
+
97
+ struct redisContext; /* need forward declaration of redisContext */
98
+
99
+ /* Context for a connection to Redis */
100
+ typedef struct redisContext {
101
+ int fd;
102
+ int flags;
103
+ char *obuf; /* Write buffer */
104
+ int err; /* Error flags, 0 when there is no error */
105
+ char *errstr; /* String representation of error when applicable */
106
+
107
+ /* Function set for reply buildup and reply reader */
108
+ redisReplyObjectFunctions *fn;
109
+ void *reader;
110
+ } redisContext;
111
+
112
+ void freeReplyObject(void *reply);
113
+ void *redisReplyReaderCreate();
114
+ int redisReplyReaderSetReplyObjectFunctions(void *reader, redisReplyObjectFunctions *fn);
115
+ void *redisReplyReaderGetObject(void *reader);
116
+ char *redisReplyReaderGetError(void *reader);
117
+ void redisReplyReaderFree(void *ptr);
118
+ void redisReplyReaderFeed(void *reader, char *buf, size_t len);
119
+ int redisReplyReaderGetReply(void *reader, void **reply);
120
+
121
+ /* Functions to format a command according to the protocol. */
122
+ int redisvFormatCommand(char **target, const char *format, va_list ap);
123
+ int redisFormatCommand(char **target, const char *format, ...);
124
+ int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
125
+
126
+ redisContext *redisConnect(const char *ip, int port);
127
+ redisContext *redisConnectNonBlock(const char *ip, int port);
128
+ redisContext *redisConnectUnix(const char *path);
129
+ redisContext *redisConnectUnixNonBlock(const char *path);
130
+ int redisSetReplyObjectFunctions(redisContext *c, redisReplyObjectFunctions *fn);
131
+ void redisFree(redisContext *c);
132
+ int redisBufferRead(redisContext *c);
133
+ int redisBufferWrite(redisContext *c, int *done);
134
+
135
+ /* In a blocking context, this function first checks if there are unconsumed
136
+ * replies to return and returns one if so. Otherwise, it flushes the output
137
+ * buffer to the socket and reads until it has a reply. In a non-blocking
138
+ * context, it will return unconsumed replies until there are no more. */
139
+ int redisGetReply(redisContext *c, void **reply);
140
+ int redisGetReplyFromReader(redisContext *c, void **reply);
141
+
142
+ /* Write a command to the output buffer. Use these functions in blocking mode
143
+ * to get a pipeline of commands. */
144
+ void redisvAppendCommand(redisContext *c, const char *format, va_list ap);
145
+ void redisAppendCommand(redisContext *c, const char *format, ...);
146
+ void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
147
+
148
+ /* Issue a command to Redis. In a blocking context, it is identical to calling
149
+ * redisAppendCommand, followed by redisGetReply. The function will return
150
+ * NULL if there was an error in performing the request, otherwise it will
151
+ * return the reply. In a non-blocking context, it is identical to calling
152
+ * only redisAppendCommand and will always return NULL. */
153
+ void *redisvCommand(redisContext *c, const char *format, va_list ap);
154
+ void *redisCommand(redisContext *c, const char *format, ...);
155
+ void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
156
+
157
+ #endif
@@ -0,0 +1,167 @@
1
+ /* Extracted from anet.c to work properly with Hiredis error reporting.
2
+ *
3
+ * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
4
+ * All rights reserved.
5
+ *
6
+ * Redistribution and use in source and binary forms, with or without
7
+ * modification, are permitted provided that the following conditions are met:
8
+ *
9
+ * * Redistributions of source code must retain the above copyright notice,
10
+ * this list of conditions and the following disclaimer.
11
+ * * Redistributions in binary form must reproduce the above copyright
12
+ * notice, this list of conditions and the following disclaimer in the
13
+ * documentation and/or other materials provided with the distribution.
14
+ * * Neither the name of Redis nor the names of its contributors may be used
15
+ * to endorse or promote products derived from this software without
16
+ * specific prior written permission.
17
+ *
18
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ * POSSIBILITY OF SUCH DAMAGE.
29
+ */
30
+
31
+ #include "fmacros.h"
32
+ #include <sys/types.h>
33
+ #include <sys/socket.h>
34
+ #include <sys/un.h>
35
+ #include <netinet/in.h>
36
+ #include <netinet/tcp.h>
37
+ #include <arpa/inet.h>
38
+ #include <unistd.h>
39
+ #include <fcntl.h>
40
+ #include <string.h>
41
+ #include <netdb.h>
42
+ #include <errno.h>
43
+ #include <stdarg.h>
44
+ #include <stdio.h>
45
+
46
+ #include "hiredis.h"
47
+ #include "sds.h"
48
+
49
+ /* Forward declaration */
50
+ void __redisSetError(redisContext *c, int type, sds err);
51
+
52
+ static int redisCreateSocket(redisContext *c, int type) {
53
+ int s, on = 1;
54
+ if ((s = socket(type, SOCK_STREAM, 0)) == -1) {
55
+ __redisSetError(c,REDIS_ERR_IO,NULL);
56
+ return REDIS_ERR;
57
+ }
58
+ if (type == AF_INET) {
59
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
60
+ __redisSetError(c,REDIS_ERR_IO,NULL);
61
+ close(s);
62
+ return REDIS_ERR;
63
+ }
64
+ }
65
+ return s;
66
+ }
67
+
68
+ static int redisSetNonBlock(redisContext *c, int fd) {
69
+ int flags;
70
+
71
+ /* Set the socket nonblocking.
72
+ * Note that fcntl(2) for F_GETFL and F_SETFL can't be
73
+ * interrupted by a signal. */
74
+ if ((flags = fcntl(fd, F_GETFL)) == -1) {
75
+ __redisSetError(c,REDIS_ERR_IO,
76
+ sdscatprintf(sdsempty(), "fcntl(F_GETFL): %s", strerror(errno)));
77
+ close(fd);
78
+ return REDIS_ERR;
79
+ }
80
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
81
+ __redisSetError(c,REDIS_ERR_IO,
82
+ sdscatprintf(sdsempty(), "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno)));
83
+ close(fd);
84
+ return REDIS_ERR;
85
+ }
86
+ return REDIS_OK;
87
+ }
88
+
89
+ static int redisSetTcpNoDelay(redisContext *c, int fd) {
90
+ int yes = 1;
91
+ if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
92
+ __redisSetError(c,REDIS_ERR_IO,
93
+ sdscatprintf(sdsempty(), "setsockopt(TCP_NODELAY): %s", strerror(errno)));
94
+ return REDIS_ERR;
95
+ }
96
+ return REDIS_OK;
97
+ }
98
+
99
+ int redisContextConnectTcp(redisContext *c, const char *addr, int port) {
100
+ int s;
101
+ int blocking = (c->flags & REDIS_BLOCK);
102
+ struct sockaddr_in sa;
103
+
104
+ if ((s = redisCreateSocket(c,AF_INET)) == REDIS_ERR)
105
+ return REDIS_ERR;
106
+ if (!blocking && redisSetNonBlock(c,s) == REDIS_ERR)
107
+ return REDIS_ERR;
108
+
109
+ sa.sin_family = AF_INET;
110
+ sa.sin_port = htons(port);
111
+ if (inet_aton(addr, &sa.sin_addr) == 0) {
112
+ struct hostent *he;
113
+
114
+ he = gethostbyname(addr);
115
+ if (he == NULL) {
116
+ __redisSetError(c,REDIS_ERR_OTHER,
117
+ sdscatprintf(sdsempty(),"can't resolve: %s",addr));
118
+ close(s);
119
+ return REDIS_ERR;
120
+ }
121
+ memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr));
122
+ }
123
+
124
+ if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
125
+ if (errno == EINPROGRESS && !blocking) {
126
+ /* This is ok. */
127
+ } else {
128
+ __redisSetError(c,REDIS_ERR_IO,NULL);
129
+ close(s);
130
+ return REDIS_ERR;
131
+ }
132
+ }
133
+
134
+ if (redisSetTcpNoDelay(c,s) != REDIS_OK) {
135
+ close(s);
136
+ return REDIS_ERR;
137
+ }
138
+
139
+ c->fd = s;
140
+ return REDIS_OK;
141
+ }
142
+
143
+ int redisContextConnectUnix(redisContext *c, const char *path) {
144
+ int s;
145
+ int blocking = (c->flags & REDIS_BLOCK);
146
+ struct sockaddr_un sa;
147
+
148
+ if ((s = redisCreateSocket(c,AF_LOCAL)) == REDIS_ERR)
149
+ return REDIS_ERR;
150
+ if (!blocking && redisSetNonBlock(c,s) != REDIS_OK)
151
+ return REDIS_ERR;
152
+
153
+ sa.sun_family = AF_LOCAL;
154
+ strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
155
+ if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
156
+ if (errno == EINPROGRESS && !blocking) {
157
+ /* This is ok. */
158
+ } else {
159
+ __redisSetError(c,REDIS_ERR_IO,NULL);
160
+ close(s);
161
+ return REDIS_ERR;
162
+ }
163
+ }
164
+
165
+ c->fd = s;
166
+ return REDIS_OK;
167
+ }
@@ -0,0 +1,37 @@
1
+ /* Extracted from anet.c to work properly with Hiredis error reporting.
2
+ *
3
+ * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
4
+ * All rights reserved.
5
+ *
6
+ * Redistribution and use in source and binary forms, with or without
7
+ * modification, are permitted provided that the following conditions are met:
8
+ *
9
+ * * Redistributions of source code must retain the above copyright notice,
10
+ * this list of conditions and the following disclaimer.
11
+ * * Redistributions in binary form must reproduce the above copyright
12
+ * notice, this list of conditions and the following disclaimer in the
13
+ * documentation and/or other materials provided with the distribution.
14
+ * * Neither the name of Redis nor the names of its contributors may be used
15
+ * to endorse or promote products derived from this software without
16
+ * specific prior written permission.
17
+ *
18
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ * POSSIBILITY OF SUCH DAMAGE.
29
+ */
30
+
31
+ #ifndef __NET_H
32
+ #define __NET_H
33
+
34
+ int redisContextConnectTcp(redisContext *c, const char *addr, int port);
35
+ int redisContextConnectUnix(redisContext *c, const char *path);
36
+
37
+ #endif
@@ -0,0 +1,479 @@
1
+ /* SDSLib, A C dynamic strings library
2
+ *
3
+ * Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
4
+ * All rights reserved.
5
+ *
6
+ * Redistribution and use in source and binary forms, with or without
7
+ * modification, are permitted provided that the following conditions are met:
8
+ *
9
+ * * Redistributions of source code must retain the above copyright notice,
10
+ * this list of conditions and the following disclaimer.
11
+ * * Redistributions in binary form must reproduce the above copyright
12
+ * notice, this list of conditions and the following disclaimer in the
13
+ * documentation and/or other materials provided with the distribution.
14
+ * * Neither the name of Redis nor the names of its contributors may be used
15
+ * to endorse or promote products derived from this software without
16
+ * specific prior written permission.
17
+ *
18
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ * POSSIBILITY OF SUCH DAMAGE.
29
+ */
30
+
31
+ #define SDS_ABORT_ON_OOM
32
+
33
+ #include "sds.h"
34
+ #include <stdio.h>
35
+ #include <stdlib.h>
36
+ #include <string.h>
37
+ #include <ctype.h>
38
+
39
+ static void sdsOomAbort(void) {
40
+ fprintf(stderr,"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n");
41
+ abort();
42
+ }
43
+
44
+ sds sdsnewlen(const void *init, size_t initlen) {
45
+ struct sdshdr *sh;
46
+
47
+ sh = malloc(sizeof(struct sdshdr)+initlen+1);
48
+ #ifdef SDS_ABORT_ON_OOM
49
+ if (sh == NULL) sdsOomAbort();
50
+ #else
51
+ if (sh == NULL) return NULL;
52
+ #endif
53
+ sh->len = initlen;
54
+ sh->free = 0;
55
+ if (initlen) {
56
+ if (init) memcpy(sh->buf, init, initlen);
57
+ else memset(sh->buf,0,initlen);
58
+ }
59
+ sh->buf[initlen] = '\0';
60
+ return (char*)sh->buf;
61
+ }
62
+
63
+ sds sdsempty(void) {
64
+ return sdsnewlen("",0);
65
+ }
66
+
67
+ sds sdsnew(const char *init) {
68
+ size_t initlen = (init == NULL) ? 0 : strlen(init);
69
+ return sdsnewlen(init, initlen);
70
+ }
71
+
72
+ size_t sdslen(const sds s) {
73
+ struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
74
+ return sh->len;
75
+ }
76
+
77
+ sds sdsdup(const sds s) {
78
+ return sdsnewlen(s, sdslen(s));
79
+ }
80
+
81
+ void sdsfree(sds s) {
82
+ if (s == NULL) return;
83
+ free(s-sizeof(struct sdshdr));
84
+ }
85
+
86
+ size_t sdsavail(sds s) {
87
+ struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
88
+ return sh->free;
89
+ }
90
+
91
+ void sdsupdatelen(sds s) {
92
+ struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
93
+ int reallen = strlen(s);
94
+ sh->free += (sh->len-reallen);
95
+ sh->len = reallen;
96
+ }
97
+
98
+ static sds sdsMakeRoomFor(sds s, size_t addlen) {
99
+ struct sdshdr *sh, *newsh;
100
+ size_t free = sdsavail(s);
101
+ size_t len, newlen;
102
+
103
+ if (free >= addlen) return s;
104
+ len = sdslen(s);
105
+ sh = (void*) (s-(sizeof(struct sdshdr)));
106
+ newlen = (len+addlen)*2;
107
+ newsh = realloc(sh, sizeof(struct sdshdr)+newlen+1);
108
+ #ifdef SDS_ABORT_ON_OOM
109
+ if (newsh == NULL) sdsOomAbort();
110
+ #else
111
+ if (newsh == NULL) return NULL;
112
+ #endif
113
+
114
+ newsh->free = newlen - len;
115
+ return newsh->buf;
116
+ }
117
+
118
+ sds sdscatlen(sds s, const void *t, size_t len) {
119
+ struct sdshdr *sh;
120
+ size_t curlen = sdslen(s);
121
+
122
+ s = sdsMakeRoomFor(s,len);
123
+ if (s == NULL) return NULL;
124
+ sh = (void*) (s-(sizeof(struct sdshdr)));
125
+ memcpy(s+curlen, t, len);
126
+ sh->len = curlen+len;
127
+ sh->free = sh->free-len;
128
+ s[curlen+len] = '\0';
129
+ return s;
130
+ }
131
+
132
+ sds sdscat(sds s, const char *t) {
133
+ return sdscatlen(s, t, strlen(t));
134
+ }
135
+
136
+ sds sdscpylen(sds s, char *t, size_t len) {
137
+ struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
138
+ size_t totlen = sh->free+sh->len;
139
+
140
+ if (totlen < len) {
141
+ s = sdsMakeRoomFor(s,len-sh->len);
142
+ if (s == NULL) return NULL;
143
+ sh = (void*) (s-(sizeof(struct sdshdr)));
144
+ totlen = sh->free+sh->len;
145
+ }
146
+ memcpy(s, t, len);
147
+ s[len] = '\0';
148
+ sh->len = len;
149
+ sh->free = totlen-len;
150
+ return s;
151
+ }
152
+
153
+ sds sdscpy(sds s, char *t) {
154
+ return sdscpylen(s, t, strlen(t));
155
+ }
156
+
157
+ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
158
+ va_list cpy;
159
+ char *buf, *t;
160
+ size_t buflen = 16;
161
+
162
+ while(1) {
163
+ buf = malloc(buflen);
164
+ #ifdef SDS_ABORT_ON_OOM
165
+ if (buf == NULL) sdsOomAbort();
166
+ #else
167
+ if (buf == NULL) return NULL;
168
+ #endif
169
+ buf[buflen-2] = '\0';
170
+ va_copy(cpy,ap);
171
+ vsnprintf(buf, buflen, fmt, cpy);
172
+ if (buf[buflen-2] != '\0') {
173
+ free(buf);
174
+ buflen *= 2;
175
+ continue;
176
+ }
177
+ break;
178
+ }
179
+ t = sdscat(s, buf);
180
+ free(buf);
181
+ return t;
182
+ }
183
+
184
+ sds sdscatprintf(sds s, const char *fmt, ...) {
185
+ va_list ap;
186
+ char *t;
187
+ va_start(ap, fmt);
188
+ t = sdscatvprintf(s,fmt,ap);
189
+ va_end(ap);
190
+ return t;
191
+ }
192
+
193
+ sds sdstrim(sds s, const char *cset) {
194
+ struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
195
+ char *start, *end, *sp, *ep;
196
+ size_t len;
197
+
198
+ sp = start = s;
199
+ ep = end = s+sdslen(s)-1;
200
+ while(sp <= end && strchr(cset, *sp)) sp++;
201
+ while(ep > start && strchr(cset, *ep)) ep--;
202
+ len = (sp > ep) ? 0 : ((ep-sp)+1);
203
+ if (sh->buf != sp) memmove(sh->buf, sp, len);
204
+ sh->buf[len] = '\0';
205
+ sh->free = sh->free+(sh->len-len);
206
+ sh->len = len;
207
+ return s;
208
+ }
209
+
210
+ sds sdsrange(sds s, int start, int end) {
211
+ struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
212
+ size_t newlen, len = sdslen(s);
213
+
214
+ if (len == 0) return s;
215
+ if (start < 0) {
216
+ start = len+start;
217
+ if (start < 0) start = 0;
218
+ }
219
+ if (end < 0) {
220
+ end = len+end;
221
+ if (end < 0) end = 0;
222
+ }
223
+ newlen = (start > end) ? 0 : (end-start)+1;
224
+ if (newlen != 0) {
225
+ if (start >= (signed)len) start = len-1;
226
+ if (end >= (signed)len) end = len-1;
227
+ newlen = (start > end) ? 0 : (end-start)+1;
228
+ } else {
229
+ start = 0;
230
+ }
231
+ if (start != 0) memmove(sh->buf, sh->buf+start, newlen);
232
+ sh->buf[newlen] = 0;
233
+ sh->free = sh->free+(sh->len-newlen);
234
+ sh->len = newlen;
235
+ return s;
236
+ }
237
+
238
+ void sdstolower(sds s) {
239
+ int len = sdslen(s), j;
240
+
241
+ for (j = 0; j < len; j++) s[j] = tolower(s[j]);
242
+ }
243
+
244
+ void sdstoupper(sds s) {
245
+ int len = sdslen(s), j;
246
+
247
+ for (j = 0; j < len; j++) s[j] = toupper(s[j]);
248
+ }
249
+
250
+ int sdscmp(sds s1, sds s2) {
251
+ size_t l1, l2, minlen;
252
+ int cmp;
253
+
254
+ l1 = sdslen(s1);
255
+ l2 = sdslen(s2);
256
+ minlen = (l1 < l2) ? l1 : l2;
257
+ cmp = memcmp(s1,s2,minlen);
258
+ if (cmp == 0) return l1-l2;
259
+ return cmp;
260
+ }
261
+
262
+ /* Split 's' with separator in 'sep'. An array
263
+ * of sds strings is returned. *count will be set
264
+ * by reference to the number of tokens returned.
265
+ *
266
+ * On out of memory, zero length string, zero length
267
+ * separator, NULL is returned.
268
+ *
269
+ * Note that 'sep' is able to split a string using
270
+ * a multi-character separator. For example
271
+ * sdssplit("foo_-_bar","_-_"); will return two
272
+ * elements "foo" and "bar".
273
+ *
274
+ * This version of the function is binary-safe but
275
+ * requires length arguments. sdssplit() is just the
276
+ * same function but for zero-terminated strings.
277
+ */
278
+ sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
279
+ int elements = 0, slots = 5, start = 0, j;
280
+
281
+ sds *tokens = malloc(sizeof(sds)*slots);
282
+ #ifdef SDS_ABORT_ON_OOM
283
+ if (tokens == NULL) sdsOomAbort();
284
+ #endif
285
+ if (seplen < 1 || len < 0 || tokens == NULL) return NULL;
286
+ if (len == 0) {
287
+ *count = 0;
288
+ return tokens;
289
+ }
290
+ for (j = 0; j < (len-(seplen-1)); j++) {
291
+ /* make sure there is room for the next element and the final one */
292
+ if (slots < elements+2) {
293
+ sds *newtokens;
294
+
295
+ slots *= 2;
296
+ newtokens = realloc(tokens,sizeof(sds)*slots);
297
+ if (newtokens == NULL) {
298
+ #ifdef SDS_ABORT_ON_OOM
299
+ sdsOomAbort();
300
+ #else
301
+ goto cleanup;
302
+ #endif
303
+ }
304
+ tokens = newtokens;
305
+ }
306
+ /* search the separator */
307
+ if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
308
+ tokens[elements] = sdsnewlen(s+start,j-start);
309
+ if (tokens[elements] == NULL) {
310
+ #ifdef SDS_ABORT_ON_OOM
311
+ sdsOomAbort();
312
+ #else
313
+ goto cleanup;
314
+ #endif
315
+ }
316
+ elements++;
317
+ start = j+seplen;
318
+ j = j+seplen-1; /* skip the separator */
319
+ }
320
+ }
321
+ /* Add the final element. We are sure there is room in the tokens array. */
322
+ tokens[elements] = sdsnewlen(s+start,len-start);
323
+ if (tokens[elements] == NULL) {
324
+ #ifdef SDS_ABORT_ON_OOM
325
+ sdsOomAbort();
326
+ #else
327
+ goto cleanup;
328
+ #endif
329
+ }
330
+ elements++;
331
+ *count = elements;
332
+ return tokens;
333
+
334
+ #ifndef SDS_ABORT_ON_OOM
335
+ cleanup:
336
+ {
337
+ int i;
338
+ for (i = 0; i < elements; i++) sdsfree(tokens[i]);
339
+ free(tokens);
340
+ return NULL;
341
+ }
342
+ #endif
343
+ }
344
+
345
+ void sdsfreesplitres(sds *tokens, int count) {
346
+ if (!tokens) return;
347
+ while(count--)
348
+ sdsfree(tokens[count]);
349
+ free(tokens);
350
+ }
351
+
352
+ sds sdsfromlonglong(long long value) {
353
+ char buf[32], *p;
354
+ unsigned long long v;
355
+
356
+ v = (value < 0) ? -value : value;
357
+ p = buf+31; /* point to the last character */
358
+ do {
359
+ *p-- = '0'+(v%10);
360
+ v /= 10;
361
+ } while(v);
362
+ if (value < 0) *p-- = '-';
363
+ p++;
364
+ return sdsnewlen(p,32-(p-buf));
365
+ }
366
+
367
+ sds sdscatrepr(sds s, char *p, size_t len) {
368
+ s = sdscatlen(s,"\"",1);
369
+ while(len--) {
370
+ switch(*p) {
371
+ case '\\':
372
+ case '"':
373
+ s = sdscatprintf(s,"\\%c",*p);
374
+ break;
375
+ case '\n': s = sdscatlen(s,"\\n",1); break;
376
+ case '\r': s = sdscatlen(s,"\\r",1); break;
377
+ case '\t': s = sdscatlen(s,"\\t",1); break;
378
+ case '\a': s = sdscatlen(s,"\\a",1); break;
379
+ case '\b': s = sdscatlen(s,"\\b",1); break;
380
+ default:
381
+ if (isprint(*p))
382
+ s = sdscatprintf(s,"%c",*p);
383
+ else
384
+ s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
385
+ break;
386
+ }
387
+ p++;
388
+ }
389
+ return sdscatlen(s,"\"",1);
390
+ }
391
+
392
+ /* Split a line into arguments, where every argument can be in the
393
+ * following programming-language REPL-alike form:
394
+ *
395
+ * foo bar "newline are supported\n" and "\xff\x00otherstuff"
396
+ *
397
+ * The number of arguments is stored into *argc, and an array
398
+ * of sds is returned. The caller should sdsfree() all the returned
399
+ * strings and finally free() the array itself.
400
+ *
401
+ * Note that sdscatrepr() is able to convert back a string into
402
+ * a quoted string in the same format sdssplitargs() is able to parse.
403
+ */
404
+ sds *sdssplitargs(char *line, int *argc) {
405
+ char *p = line;
406
+ char *current = NULL;
407
+ char **vector = NULL;
408
+
409
+ *argc = 0;
410
+ while(1) {
411
+ /* skip blanks */
412
+ while(*p && isspace(*p)) p++;
413
+ if (*p) {
414
+ /* get a token */
415
+ int inq=0; /* set to 1 if we are in "quotes" */
416
+ int done=0;
417
+
418
+ if (current == NULL) current = sdsempty();
419
+ while(!done) {
420
+ if (inq) {
421
+ if (*p == '\\' && *(p+1)) {
422
+ char c;
423
+
424
+ p++;
425
+ switch(*p) {
426
+ case 'n': c = '\n'; break;
427
+ case 'r': c = '\r'; break;
428
+ case 't': c = '\t'; break;
429
+ case 'b': c = '\b'; break;
430
+ case 'a': c = '\a'; break;
431
+ default: c = *p; break;
432
+ }
433
+ current = sdscatlen(current,&c,1);
434
+ } else if (*p == '"') {
435
+ /* closing quote must be followed by a space */
436
+ if (*(p+1) && !isspace(*(p+1))) goto err;
437
+ done=1;
438
+ } else if (!*p) {
439
+ /* unterminated quotes */
440
+ goto err;
441
+ } else {
442
+ current = sdscatlen(current,p,1);
443
+ }
444
+ } else {
445
+ switch(*p) {
446
+ case ' ':
447
+ case '\n':
448
+ case '\r':
449
+ case '\t':
450
+ case '\0':
451
+ done=1;
452
+ break;
453
+ case '"':
454
+ inq=1;
455
+ break;
456
+ default:
457
+ current = sdscatlen(current,p,1);
458
+ break;
459
+ }
460
+ }
461
+ if (*p) p++;
462
+ }
463
+ /* add the token to the vector */
464
+ vector = realloc(vector,((*argc)+1)*sizeof(char*));
465
+ vector[*argc] = current;
466
+ (*argc)++;
467
+ current = NULL;
468
+ } else {
469
+ return vector;
470
+ }
471
+ }
472
+
473
+ err:
474
+ while((*argc)--)
475
+ sdsfree(vector[*argc]);
476
+ free(vector);
477
+ if (current) sdsfree(current);
478
+ return NULL;
479
+ }