hiredis 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/hiredis/version.rb +1 -1
- data/vendor/hiredis/Makefile +78 -32
- data/vendor/hiredis/async.c +84 -21
- data/vendor/hiredis/async.h +4 -0
- data/vendor/hiredis/fmacros.h +7 -2
- data/vendor/hiredis/hiredis.c +228 -534
- data/vendor/hiredis/hiredis.h +53 -74
- data/vendor/hiredis/net.c +155 -46
- data/vendor/hiredis/net.h +8 -4
- data/vendor/hiredis/read.c +523 -0
- data/vendor/hiredis/read.h +116 -0
- data/vendor/hiredis/sds.c +610 -120
- data/vendor/hiredis/sds.h +27 -13
- data/vendor/hiredis/test.c +106 -12
- metadata +44 -28
- data/lib/hiredis/errors.rb +0 -5
@@ -0,0 +1,116 @@
|
|
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
|
+
|
33
|
+
#ifndef __HIREDIS_READ_H
|
34
|
+
#define __HIREDIS_READ_H
|
35
|
+
#include <stdio.h> /* for size_t */
|
36
|
+
|
37
|
+
#define REDIS_ERR -1
|
38
|
+
#define REDIS_OK 0
|
39
|
+
|
40
|
+
/* When an error occurs, the err flag in a context is set to hold the type of
|
41
|
+
* error that occured. REDIS_ERR_IO means there was an I/O error and you
|
42
|
+
* should use the "errno" variable to find out what is wrong.
|
43
|
+
* For other values, the "errstr" field will hold a description. */
|
44
|
+
#define REDIS_ERR_IO 1 /* Error in read or write */
|
45
|
+
#define REDIS_ERR_EOF 3 /* End of file */
|
46
|
+
#define REDIS_ERR_PROTOCOL 4 /* Protocol error */
|
47
|
+
#define REDIS_ERR_OOM 5 /* Out of memory */
|
48
|
+
#define REDIS_ERR_OTHER 2 /* Everything else... */
|
49
|
+
|
50
|
+
#define REDIS_REPLY_STRING 1
|
51
|
+
#define REDIS_REPLY_ARRAY 2
|
52
|
+
#define REDIS_REPLY_INTEGER 3
|
53
|
+
#define REDIS_REPLY_NIL 4
|
54
|
+
#define REDIS_REPLY_STATUS 5
|
55
|
+
#define REDIS_REPLY_ERROR 6
|
56
|
+
|
57
|
+
#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */
|
58
|
+
|
59
|
+
#ifdef __cplusplus
|
60
|
+
extern "C" {
|
61
|
+
#endif
|
62
|
+
|
63
|
+
typedef struct redisReadTask {
|
64
|
+
int type;
|
65
|
+
int elements; /* number of elements in multibulk container */
|
66
|
+
int idx; /* index in parent (array) object */
|
67
|
+
void *obj; /* holds user-generated value for a read task */
|
68
|
+
struct redisReadTask *parent; /* parent task */
|
69
|
+
void *privdata; /* user-settable arbitrary field */
|
70
|
+
} redisReadTask;
|
71
|
+
|
72
|
+
typedef struct redisReplyObjectFunctions {
|
73
|
+
void *(*createString)(const redisReadTask*, char*, size_t);
|
74
|
+
void *(*createArray)(const redisReadTask*, int);
|
75
|
+
void *(*createInteger)(const redisReadTask*, long long);
|
76
|
+
void *(*createNil)(const redisReadTask*);
|
77
|
+
void (*freeObject)(void*);
|
78
|
+
} redisReplyObjectFunctions;
|
79
|
+
|
80
|
+
typedef struct redisReader {
|
81
|
+
int err; /* Error flags, 0 when there is no error */
|
82
|
+
char errstr[128]; /* String representation of error when applicable */
|
83
|
+
|
84
|
+
char *buf; /* Read buffer */
|
85
|
+
size_t pos; /* Buffer cursor */
|
86
|
+
size_t len; /* Buffer length */
|
87
|
+
size_t maxbuf; /* Max length of unused buffer */
|
88
|
+
|
89
|
+
redisReadTask rstack[9];
|
90
|
+
int ridx; /* Index of current read task */
|
91
|
+
void *reply; /* Temporary reply pointer */
|
92
|
+
|
93
|
+
redisReplyObjectFunctions *fn;
|
94
|
+
void *privdata;
|
95
|
+
} redisReader;
|
96
|
+
|
97
|
+
/* Public API for the protocol parser. */
|
98
|
+
redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn);
|
99
|
+
void redisReaderFree(redisReader *r);
|
100
|
+
int redisReaderFeed(redisReader *r, const char *buf, size_t len);
|
101
|
+
int redisReaderGetReply(redisReader *r, void **reply);
|
102
|
+
|
103
|
+
/* Backwards compatibility, can be removed on big version bump. */
|
104
|
+
#define redisReplyReaderCreate redisReaderCreate
|
105
|
+
#define redisReplyReaderFree redisReaderFree
|
106
|
+
#define redisReplyReaderFeed redisReaderFeed
|
107
|
+
#define redisReplyReaderGetReply redisReaderGetReply
|
108
|
+
#define redisReplyReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))
|
109
|
+
#define redisReplyReaderGetObject(_r) (((redisReader*)(_r))->reply)
|
110
|
+
#define redisReplyReaderGetError(_r) (((redisReader*)(_r))->errstr)
|
111
|
+
|
112
|
+
#ifdef __cplusplus
|
113
|
+
}
|
114
|
+
#endif
|
115
|
+
|
116
|
+
#endif
|
data/vendor/hiredis/sds.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
/*
|
1
|
+
/* SDS (Simple Dynamic Strings), A C dynamic strings library.
|
2
2
|
*
|
3
|
-
* Copyright (c) 2006-
|
3
|
+
* Copyright (c) 2006-2014, Salvatore Sanfilippo <antirez at gmail dot com>
|
4
4
|
* All rights reserved.
|
5
5
|
*
|
6
6
|
* Redistribution and use in source and binary forms, with or without
|
@@ -32,83 +32,188 @@
|
|
32
32
|
#include <stdlib.h>
|
33
33
|
#include <string.h>
|
34
34
|
#include <ctype.h>
|
35
|
-
#include
|
35
|
+
#include <assert.h>
|
36
36
|
|
37
|
-
#
|
38
|
-
static void sdsOomAbort(void) {
|
39
|
-
fprintf(stderr,"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n");
|
40
|
-
abort();
|
41
|
-
}
|
42
|
-
#endif
|
37
|
+
#include "sds.h"
|
43
38
|
|
39
|
+
/* Create a new sds string with the content specified by the 'init' pointer
|
40
|
+
* and 'initlen'.
|
41
|
+
* If NULL is used for 'init' the string is initialized with zero bytes.
|
42
|
+
*
|
43
|
+
* The string is always null-termined (all the sds strings are, always) so
|
44
|
+
* even if you create an sds string with:
|
45
|
+
*
|
46
|
+
* mystring = sdsnewlen("abc",3");
|
47
|
+
*
|
48
|
+
* You can print the string with printf() as there is an implicit \0 at the
|
49
|
+
* end of the string. However the string is binary safe and can contain
|
50
|
+
* \0 characters in the middle, as the length is stored in the sds header. */
|
44
51
|
sds sdsnewlen(const void *init, size_t initlen) {
|
45
52
|
struct sdshdr *sh;
|
46
53
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
54
|
+
if (init) {
|
55
|
+
sh = malloc(sizeof *sh+initlen+1);
|
56
|
+
} else {
|
57
|
+
sh = calloc(sizeof *sh+initlen+1,1);
|
58
|
+
}
|
51
59
|
if (sh == NULL) return NULL;
|
52
|
-
#endif
|
53
60
|
sh->len = initlen;
|
54
61
|
sh->free = 0;
|
55
|
-
if (initlen)
|
56
|
-
|
57
|
-
else memset(sh->buf,0,initlen);
|
58
|
-
}
|
62
|
+
if (initlen && init)
|
63
|
+
memcpy(sh->buf, init, initlen);
|
59
64
|
sh->buf[initlen] = '\0';
|
60
65
|
return (char*)sh->buf;
|
61
66
|
}
|
62
67
|
|
68
|
+
/* Create an empty (zero length) sds string. Even in this case the string
|
69
|
+
* always has an implicit null term. */
|
63
70
|
sds sdsempty(void) {
|
64
71
|
return sdsnewlen("",0);
|
65
72
|
}
|
66
73
|
|
74
|
+
/* Create a new sds string starting from a null termined C string. */
|
67
75
|
sds sdsnew(const char *init) {
|
68
76
|
size_t initlen = (init == NULL) ? 0 : strlen(init);
|
69
77
|
return sdsnewlen(init, initlen);
|
70
78
|
}
|
71
79
|
|
80
|
+
/* Duplicate an sds string. */
|
72
81
|
sds sdsdup(const sds s) {
|
73
82
|
return sdsnewlen(s, sdslen(s));
|
74
83
|
}
|
75
84
|
|
85
|
+
/* Free an sds string. No operation is performed if 's' is NULL. */
|
76
86
|
void sdsfree(sds s) {
|
77
87
|
if (s == NULL) return;
|
78
88
|
free(s-sizeof(struct sdshdr));
|
79
89
|
}
|
80
90
|
|
91
|
+
/* Set the sds string length to the length as obtained with strlen(), so
|
92
|
+
* considering as content only up to the first null term character.
|
93
|
+
*
|
94
|
+
* This function is useful when the sds string is hacked manually in some
|
95
|
+
* way, like in the following example:
|
96
|
+
*
|
97
|
+
* s = sdsnew("foobar");
|
98
|
+
* s[2] = '\0';
|
99
|
+
* sdsupdatelen(s);
|
100
|
+
* printf("%d\n", sdslen(s));
|
101
|
+
*
|
102
|
+
* The output will be "2", but if we comment out the call to sdsupdatelen()
|
103
|
+
* the output will be "6" as the string was modified but the logical length
|
104
|
+
* remains 6 bytes. */
|
81
105
|
void sdsupdatelen(sds s) {
|
82
|
-
struct sdshdr *sh = (void*) (s-
|
106
|
+
struct sdshdr *sh = (void*) (s-sizeof *sh);;
|
83
107
|
int reallen = strlen(s);
|
84
108
|
sh->free += (sh->len-reallen);
|
85
109
|
sh->len = reallen;
|
86
110
|
}
|
87
111
|
|
88
|
-
|
112
|
+
/* Modify an sds string on-place to make it empty (zero length).
|
113
|
+
* However all the existing buffer is not discarded but set as free space
|
114
|
+
* so that next append operations will not require allocations up to the
|
115
|
+
* number of bytes previously available. */
|
116
|
+
void sdsclear(sds s) {
|
117
|
+
struct sdshdr *sh = (void*) (s-sizeof *sh);;
|
118
|
+
sh->free += sh->len;
|
119
|
+
sh->len = 0;
|
120
|
+
sh->buf[0] = '\0';
|
121
|
+
}
|
122
|
+
|
123
|
+
/* Enlarge the free space at the end of the sds string so that the caller
|
124
|
+
* is sure that after calling this function can overwrite up to addlen
|
125
|
+
* bytes after the end of the string, plus one more byte for nul term.
|
126
|
+
*
|
127
|
+
* Note: this does not change the *length* of the sds string as returned
|
128
|
+
* by sdslen(), but only the free buffer space we have. */
|
129
|
+
sds sdsMakeRoomFor(sds s, size_t addlen) {
|
89
130
|
struct sdshdr *sh, *newsh;
|
90
131
|
size_t free = sdsavail(s);
|
91
132
|
size_t len, newlen;
|
92
133
|
|
93
134
|
if (free >= addlen) return s;
|
94
135
|
len = sdslen(s);
|
95
|
-
sh = (void*) (s-
|
96
|
-
newlen = (len+addlen)
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
136
|
+
sh = (void*) (s-sizeof *sh);;
|
137
|
+
newlen = (len+addlen);
|
138
|
+
if (newlen < SDS_MAX_PREALLOC)
|
139
|
+
newlen *= 2;
|
140
|
+
else
|
141
|
+
newlen += SDS_MAX_PREALLOC;
|
142
|
+
newsh = realloc(sh, sizeof *newsh+newlen+1);
|
101
143
|
if (newsh == NULL) return NULL;
|
102
|
-
#endif
|
103
144
|
|
104
145
|
newsh->free = newlen - len;
|
105
146
|
return newsh->buf;
|
106
147
|
}
|
107
148
|
|
149
|
+
/* Reallocate the sds string so that it has no free space at the end. The
|
150
|
+
* contained string remains not altered, but next concatenation operations
|
151
|
+
* will require a reallocation.
|
152
|
+
*
|
153
|
+
* After the call, the passed sds string is no longer valid and all the
|
154
|
+
* references must be substituted with the new pointer returned by the call. */
|
155
|
+
sds sdsRemoveFreeSpace(sds s) {
|
156
|
+
struct sdshdr *sh;
|
157
|
+
|
158
|
+
sh = (void*) (s-sizeof *sh);;
|
159
|
+
sh = realloc(sh, sizeof *sh+sh->len+1);
|
160
|
+
sh->free = 0;
|
161
|
+
return sh->buf;
|
162
|
+
}
|
163
|
+
|
164
|
+
/* Return the total size of the allocation of the specifed sds string,
|
165
|
+
* including:
|
166
|
+
* 1) The sds header before the pointer.
|
167
|
+
* 2) The string.
|
168
|
+
* 3) The free buffer at the end if any.
|
169
|
+
* 4) The implicit null term.
|
170
|
+
*/
|
171
|
+
size_t sdsAllocSize(sds s) {
|
172
|
+
struct sdshdr *sh = (void*) (s-sizeof *sh);;
|
173
|
+
|
174
|
+
return sizeof(*sh)+sh->len+sh->free+1;
|
175
|
+
}
|
176
|
+
|
177
|
+
/* Increment the sds length and decrements the left free space at the
|
178
|
+
* end of the string according to 'incr'. Also set the null term
|
179
|
+
* in the new end of the string.
|
180
|
+
*
|
181
|
+
* This function is used in order to fix the string length after the
|
182
|
+
* user calls sdsMakeRoomFor(), writes something after the end of
|
183
|
+
* the current string, and finally needs to set the new length.
|
184
|
+
*
|
185
|
+
* Note: it is possible to use a negative increment in order to
|
186
|
+
* right-trim the string.
|
187
|
+
*
|
188
|
+
* Usage example:
|
189
|
+
*
|
190
|
+
* Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the
|
191
|
+
* following schema, to cat bytes coming from the kernel to the end of an
|
192
|
+
* sds string without copying into an intermediate buffer:
|
193
|
+
*
|
194
|
+
* oldlen = sdslen(s);
|
195
|
+
* s = sdsMakeRoomFor(s, BUFFER_SIZE);
|
196
|
+
* nread = read(fd, s+oldlen, BUFFER_SIZE);
|
197
|
+
* ... check for nread <= 0 and handle it ...
|
198
|
+
* sdsIncrLen(s, nread);
|
199
|
+
*/
|
200
|
+
void sdsIncrLen(sds s, int incr) {
|
201
|
+
struct sdshdr *sh = (void*) (s-sizeof *sh);;
|
202
|
+
|
203
|
+
assert(sh->free >= incr);
|
204
|
+
sh->len += incr;
|
205
|
+
sh->free -= incr;
|
206
|
+
assert(sh->free >= 0);
|
207
|
+
s[sh->len] = '\0';
|
208
|
+
}
|
209
|
+
|
108
210
|
/* Grow the sds to have the specified length. Bytes that were not part of
|
109
|
-
* the original length of the sds will be set to zero.
|
211
|
+
* the original length of the sds will be set to zero.
|
212
|
+
*
|
213
|
+
* if the specified length is smaller than the current length, no operation
|
214
|
+
* is performed. */
|
110
215
|
sds sdsgrowzero(sds s, size_t len) {
|
111
|
-
struct sdshdr *sh = (void*)(s-
|
216
|
+
struct sdshdr *sh = (void*) (s-sizeof *sh);
|
112
217
|
size_t totlen, curlen = sh->len;
|
113
218
|
|
114
219
|
if (len <= curlen) return s;
|
@@ -116,7 +221,7 @@ sds sdsgrowzero(sds s, size_t len) {
|
|
116
221
|
if (s == NULL) return NULL;
|
117
222
|
|
118
223
|
/* Make sure added region doesn't contain garbage */
|
119
|
-
sh = (void*)(s-
|
224
|
+
sh = (void*)(s-sizeof *sh);
|
120
225
|
memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
|
121
226
|
totlen = sh->len+sh->free;
|
122
227
|
sh->len = len;
|
@@ -124,13 +229,18 @@ sds sdsgrowzero(sds s, size_t len) {
|
|
124
229
|
return s;
|
125
230
|
}
|
126
231
|
|
232
|
+
/* Append the specified binary-safe string pointed by 't' of 'len' bytes to the
|
233
|
+
* end of the specified sds string 's'.
|
234
|
+
*
|
235
|
+
* After the call, the passed sds string is no longer valid and all the
|
236
|
+
* references must be substituted with the new pointer returned by the call. */
|
127
237
|
sds sdscatlen(sds s, const void *t, size_t len) {
|
128
238
|
struct sdshdr *sh;
|
129
239
|
size_t curlen = sdslen(s);
|
130
240
|
|
131
241
|
s = sdsMakeRoomFor(s,len);
|
132
242
|
if (s == NULL) return NULL;
|
133
|
-
sh = (void*) (s-
|
243
|
+
sh = (void*) (s-sizeof *sh);;
|
134
244
|
memcpy(s+curlen, t, len);
|
135
245
|
sh->len = curlen+len;
|
136
246
|
sh->free = sh->free-len;
|
@@ -138,18 +248,32 @@ sds sdscatlen(sds s, const void *t, size_t len) {
|
|
138
248
|
return s;
|
139
249
|
}
|
140
250
|
|
251
|
+
/* Append the specified null termianted C string to the sds string 's'.
|
252
|
+
*
|
253
|
+
* After the call, the passed sds string is no longer valid and all the
|
254
|
+
* references must be substituted with the new pointer returned by the call. */
|
141
255
|
sds sdscat(sds s, const char *t) {
|
142
256
|
return sdscatlen(s, t, strlen(t));
|
143
257
|
}
|
144
258
|
|
145
|
-
|
146
|
-
|
259
|
+
/* Append the specified sds 't' to the existing sds 's'.
|
260
|
+
*
|
261
|
+
* After the call, the modified sds string is no longer valid and all the
|
262
|
+
* references must be substituted with the new pointer returned by the call. */
|
263
|
+
sds sdscatsds(sds s, const sds t) {
|
264
|
+
return sdscatlen(s, t, sdslen(t));
|
265
|
+
}
|
266
|
+
|
267
|
+
/* Destructively modify the sds string 's' to hold the specified binary
|
268
|
+
* safe string pointed by 't' of length 'len' bytes. */
|
269
|
+
sds sdscpylen(sds s, const char *t, size_t len) {
|
270
|
+
struct sdshdr *sh = (void*) (s-sizeof *sh);;
|
147
271
|
size_t totlen = sh->free+sh->len;
|
148
272
|
|
149
273
|
if (totlen < len) {
|
150
274
|
s = sdsMakeRoomFor(s,len-sh->len);
|
151
275
|
if (s == NULL) return NULL;
|
152
|
-
sh = (void*) (s-
|
276
|
+
sh = (void*) (s-sizeof *sh);;
|
153
277
|
totlen = sh->free+sh->len;
|
154
278
|
}
|
155
279
|
memcpy(s, t, len);
|
@@ -159,10 +283,80 @@ sds sdscpylen(sds s, char *t, size_t len) {
|
|
159
283
|
return s;
|
160
284
|
}
|
161
285
|
|
162
|
-
|
286
|
+
/* Like sdscpylen() but 't' must be a null-termined string so that the length
|
287
|
+
* of the string is obtained with strlen(). */
|
288
|
+
sds sdscpy(sds s, const char *t) {
|
163
289
|
return sdscpylen(s, t, strlen(t));
|
164
290
|
}
|
165
291
|
|
292
|
+
/* Helper for sdscatlonglong() doing the actual number -> string
|
293
|
+
* conversion. 's' must point to a string with room for at least
|
294
|
+
* SDS_LLSTR_SIZE bytes.
|
295
|
+
*
|
296
|
+
* The function returns the lenght of the null-terminated string
|
297
|
+
* representation stored at 's'. */
|
298
|
+
#define SDS_LLSTR_SIZE 21
|
299
|
+
int sdsll2str(char *s, long long value) {
|
300
|
+
char *p, aux;
|
301
|
+
unsigned long long v;
|
302
|
+
size_t l;
|
303
|
+
|
304
|
+
/* Generate the string representation, this method produces
|
305
|
+
* an reversed string. */
|
306
|
+
v = (value < 0) ? -value : value;
|
307
|
+
p = s;
|
308
|
+
do {
|
309
|
+
*p++ = '0'+(v%10);
|
310
|
+
v /= 10;
|
311
|
+
} while(v);
|
312
|
+
if (value < 0) *p++ = '-';
|
313
|
+
|
314
|
+
/* Compute length and add null term. */
|
315
|
+
l = p-s;
|
316
|
+
*p = '\0';
|
317
|
+
|
318
|
+
/* Reverse the string. */
|
319
|
+
p--;
|
320
|
+
while(s < p) {
|
321
|
+
aux = *s;
|
322
|
+
*s = *p;
|
323
|
+
*p = aux;
|
324
|
+
s++;
|
325
|
+
p--;
|
326
|
+
}
|
327
|
+
return l;
|
328
|
+
}
|
329
|
+
|
330
|
+
/* Identical sdsll2str(), but for unsigned long long type. */
|
331
|
+
int sdsull2str(char *s, unsigned long long v) {
|
332
|
+
char *p, aux;
|
333
|
+
size_t l;
|
334
|
+
|
335
|
+
/* Generate the string representation, this method produces
|
336
|
+
* an reversed string. */
|
337
|
+
p = s;
|
338
|
+
do {
|
339
|
+
*p++ = '0'+(v%10);
|
340
|
+
v /= 10;
|
341
|
+
} while(v);
|
342
|
+
|
343
|
+
/* Compute length and add null term. */
|
344
|
+
l = p-s;
|
345
|
+
*p = '\0';
|
346
|
+
|
347
|
+
/* Reverse the string. */
|
348
|
+
p--;
|
349
|
+
while(s < p) {
|
350
|
+
aux = *s;
|
351
|
+
*s = *p;
|
352
|
+
*p = aux;
|
353
|
+
s++;
|
354
|
+
p--;
|
355
|
+
}
|
356
|
+
return l;
|
357
|
+
}
|
358
|
+
|
359
|
+
/* Like sdscatpritf() but gets va_list instead of being variadic. */
|
166
360
|
sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
|
167
361
|
va_list cpy;
|
168
362
|
char *buf, *t;
|
@@ -170,11 +364,7 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
|
|
170
364
|
|
171
365
|
while(1) {
|
172
366
|
buf = malloc(buflen);
|
173
|
-
#ifdef SDS_ABORT_ON_OOM
|
174
|
-
if (buf == NULL) sdsOomAbort();
|
175
|
-
#else
|
176
367
|
if (buf == NULL) return NULL;
|
177
|
-
#endif
|
178
368
|
buf[buflen-2] = '\0';
|
179
369
|
va_copy(cpy,ap);
|
180
370
|
vsnprintf(buf, buflen, fmt, cpy);
|
@@ -190,6 +380,22 @@ sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
|
|
190
380
|
return t;
|
191
381
|
}
|
192
382
|
|
383
|
+
/* Append to the sds string 's' a string obtained using printf-alike format
|
384
|
+
* specifier.
|
385
|
+
*
|
386
|
+
* After the call, the modified sds string is no longer valid and all the
|
387
|
+
* references must be substituted with the new pointer returned by the call.
|
388
|
+
*
|
389
|
+
* Example:
|
390
|
+
*
|
391
|
+
* s = sdsnew("Sum is: ");
|
392
|
+
* s = sdscatprintf(s,"%d+%d = %d",a,b,a+b);
|
393
|
+
*
|
394
|
+
* Often you need to create a string from scratch with the printf-alike
|
395
|
+
* format. When this is the need, just use sdsempty() as the target string:
|
396
|
+
*
|
397
|
+
* s = sdscatprintf(sdsempty(), "... your format ...", args);
|
398
|
+
*/
|
193
399
|
sds sdscatprintf(sds s, const char *fmt, ...) {
|
194
400
|
va_list ap;
|
195
401
|
char *t;
|
@@ -199,8 +405,143 @@ sds sdscatprintf(sds s, const char *fmt, ...) {
|
|
199
405
|
return t;
|
200
406
|
}
|
201
407
|
|
202
|
-
|
408
|
+
/* This function is similar to sdscatprintf, but much faster as it does
|
409
|
+
* not rely on sprintf() family functions implemented by the libc that
|
410
|
+
* are often very slow. Moreover directly handling the sds string as
|
411
|
+
* new data is concatenated provides a performance improvement.
|
412
|
+
*
|
413
|
+
* However this function only handles an incompatible subset of printf-alike
|
414
|
+
* format specifiers:
|
415
|
+
*
|
416
|
+
* %s - C String
|
417
|
+
* %S - SDS string
|
418
|
+
* %i - signed int
|
419
|
+
* %I - 64 bit signed integer (long long, int64_t)
|
420
|
+
* %u - unsigned int
|
421
|
+
* %U - 64 bit unsigned integer (unsigned long long, uint64_t)
|
422
|
+
* %T - A size_t variable.
|
423
|
+
* %% - Verbatim "%" character.
|
424
|
+
*/
|
425
|
+
sds sdscatfmt(sds s, char const *fmt, ...) {
|
203
426
|
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
427
|
+
size_t initlen = sdslen(s);
|
428
|
+
const char *f = fmt;
|
429
|
+
int i;
|
430
|
+
va_list ap;
|
431
|
+
|
432
|
+
va_start(ap,fmt);
|
433
|
+
f = fmt; /* Next format specifier byte to process. */
|
434
|
+
i = initlen; /* Position of the next byte to write to dest str. */
|
435
|
+
while(*f) {
|
436
|
+
char next, *str;
|
437
|
+
int l;
|
438
|
+
long long num;
|
439
|
+
unsigned long long unum;
|
440
|
+
|
441
|
+
/* Make sure there is always space for at least 1 char. */
|
442
|
+
if (sh->free == 0) {
|
443
|
+
s = sdsMakeRoomFor(s,1);
|
444
|
+
sh = (void*) (s-(sizeof(struct sdshdr)));
|
445
|
+
}
|
446
|
+
|
447
|
+
switch(*f) {
|
448
|
+
case '%':
|
449
|
+
next = *(f+1);
|
450
|
+
f++;
|
451
|
+
switch(next) {
|
452
|
+
case 's':
|
453
|
+
case 'S':
|
454
|
+
str = va_arg(ap,char*);
|
455
|
+
l = (next == 's') ? strlen(str) : sdslen(str);
|
456
|
+
if (sh->free < l) {
|
457
|
+
s = sdsMakeRoomFor(s,l);
|
458
|
+
sh = (void*) (s-(sizeof(struct sdshdr)));
|
459
|
+
}
|
460
|
+
memcpy(s+i,str,l);
|
461
|
+
sh->len += l;
|
462
|
+
sh->free -= l;
|
463
|
+
i += l;
|
464
|
+
break;
|
465
|
+
case 'i':
|
466
|
+
case 'I':
|
467
|
+
if (next == 'i')
|
468
|
+
num = va_arg(ap,int);
|
469
|
+
else
|
470
|
+
num = va_arg(ap,long long);
|
471
|
+
{
|
472
|
+
char buf[SDS_LLSTR_SIZE];
|
473
|
+
l = sdsll2str(buf,num);
|
474
|
+
if (sh->free < l) {
|
475
|
+
s = sdsMakeRoomFor(s,l);
|
476
|
+
sh = (void*) (s-(sizeof(struct sdshdr)));
|
477
|
+
}
|
478
|
+
memcpy(s+i,buf,l);
|
479
|
+
sh->len += l;
|
480
|
+
sh->free -= l;
|
481
|
+
i += l;
|
482
|
+
}
|
483
|
+
break;
|
484
|
+
case 'u':
|
485
|
+
case 'U':
|
486
|
+
case 'T':
|
487
|
+
if (next == 'u')
|
488
|
+
unum = va_arg(ap,unsigned int);
|
489
|
+
else if(next == 'U')
|
490
|
+
unum = va_arg(ap,unsigned long long);
|
491
|
+
else
|
492
|
+
unum = (unsigned long long)va_arg(ap,size_t);
|
493
|
+
{
|
494
|
+
char buf[SDS_LLSTR_SIZE];
|
495
|
+
l = sdsull2str(buf,unum);
|
496
|
+
if (sh->free < l) {
|
497
|
+
s = sdsMakeRoomFor(s,l);
|
498
|
+
sh = (void*) (s-(sizeof(struct sdshdr)));
|
499
|
+
}
|
500
|
+
memcpy(s+i,buf,l);
|
501
|
+
sh->len += l;
|
502
|
+
sh->free -= l;
|
503
|
+
i += l;
|
504
|
+
}
|
505
|
+
break;
|
506
|
+
default: /* Handle %% and generally %<unknown>. */
|
507
|
+
s[i++] = next;
|
508
|
+
sh->len += 1;
|
509
|
+
sh->free -= 1;
|
510
|
+
break;
|
511
|
+
}
|
512
|
+
break;
|
513
|
+
default:
|
514
|
+
s[i++] = *f;
|
515
|
+
sh->len += 1;
|
516
|
+
sh->free -= 1;
|
517
|
+
break;
|
518
|
+
}
|
519
|
+
f++;
|
520
|
+
}
|
521
|
+
va_end(ap);
|
522
|
+
|
523
|
+
/* Add null-term */
|
524
|
+
s[i] = '\0';
|
525
|
+
return s;
|
526
|
+
}
|
527
|
+
|
528
|
+
|
529
|
+
/* Remove the part of the string from left and from right composed just of
|
530
|
+
* contiguous characters found in 'cset', that is a null terminted C string.
|
531
|
+
*
|
532
|
+
* After the call, the modified sds string is no longer valid and all the
|
533
|
+
* references must be substituted with the new pointer returned by the call.
|
534
|
+
*
|
535
|
+
* Example:
|
536
|
+
*
|
537
|
+
* s = sdsnew("AA...AA.a.aa.aHelloWorld :::");
|
538
|
+
* s = sdstrim(s,"A. :");
|
539
|
+
* printf("%s\n", s);
|
540
|
+
*
|
541
|
+
* Output will be just "Hello World".
|
542
|
+
*/
|
543
|
+
void sdstrim(sds s, const char *cset) {
|
544
|
+
struct sdshdr *sh = (void*) (s-sizeof *sh);;
|
204
545
|
char *start, *end, *sp, *ep;
|
205
546
|
size_t len;
|
206
547
|
|
@@ -213,14 +554,29 @@ sds sdstrim(sds s, const char *cset) {
|
|
213
554
|
sh->buf[len] = '\0';
|
214
555
|
sh->free = sh->free+(sh->len-len);
|
215
556
|
sh->len = len;
|
216
|
-
return s;
|
217
557
|
}
|
218
558
|
|
219
|
-
|
220
|
-
|
559
|
+
/* Turn the string into a smaller (or equal) string containing only the
|
560
|
+
* substring specified by the 'start' and 'end' indexes.
|
561
|
+
*
|
562
|
+
* start and end can be negative, where -1 means the last character of the
|
563
|
+
* string, -2 the penultimate character, and so forth.
|
564
|
+
*
|
565
|
+
* The interval is inclusive, so the start and end characters will be part
|
566
|
+
* of the resulting string.
|
567
|
+
*
|
568
|
+
* The string is modified in-place.
|
569
|
+
*
|
570
|
+
* Example:
|
571
|
+
*
|
572
|
+
* s = sdsnew("Hello World");
|
573
|
+
* sdsrange(s,1,-1); => "ello World"
|
574
|
+
*/
|
575
|
+
void sdsrange(sds s, int start, int end) {
|
576
|
+
struct sdshdr *sh = (void*) (s-sizeof *sh);;
|
221
577
|
size_t newlen, len = sdslen(s);
|
222
578
|
|
223
|
-
if (len == 0) return
|
579
|
+
if (len == 0) return;
|
224
580
|
if (start < 0) {
|
225
581
|
start = len+start;
|
226
582
|
if (start < 0) start = 0;
|
@@ -244,22 +600,34 @@ sds sdsrange(sds s, int start, int end) {
|
|
244
600
|
sh->buf[newlen] = 0;
|
245
601
|
sh->free = sh->free+(sh->len-newlen);
|
246
602
|
sh->len = newlen;
|
247
|
-
return s;
|
248
603
|
}
|
249
604
|
|
605
|
+
/* Apply tolower() to every character of the sds string 's'. */
|
250
606
|
void sdstolower(sds s) {
|
251
607
|
int len = sdslen(s), j;
|
252
608
|
|
253
609
|
for (j = 0; j < len; j++) s[j] = tolower(s[j]);
|
254
610
|
}
|
255
611
|
|
612
|
+
/* Apply toupper() to every character of the sds string 's'. */
|
256
613
|
void sdstoupper(sds s) {
|
257
614
|
int len = sdslen(s), j;
|
258
615
|
|
259
616
|
for (j = 0; j < len; j++) s[j] = toupper(s[j]);
|
260
617
|
}
|
261
618
|
|
262
|
-
|
619
|
+
/* Compare two sds strings s1 and s2 with memcmp().
|
620
|
+
*
|
621
|
+
* Return value:
|
622
|
+
*
|
623
|
+
* 1 if s1 > s2.
|
624
|
+
* -1 if s1 < s2.
|
625
|
+
* 0 if s1 and s2 are exactly the same binary string.
|
626
|
+
*
|
627
|
+
* If two strings share exactly the same prefix, but one of the two has
|
628
|
+
* additional characters, the longer string is considered to be greater than
|
629
|
+
* the smaller one. */
|
630
|
+
int sdscmp(const sds s1, const sds s2) {
|
263
631
|
size_t l1, l2, minlen;
|
264
632
|
int cmp;
|
265
633
|
|
@@ -287,14 +655,15 @@ int sdscmp(sds s1, sds s2) {
|
|
287
655
|
* requires length arguments. sdssplit() is just the
|
288
656
|
* same function but for zero-terminated strings.
|
289
657
|
*/
|
290
|
-
sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
|
658
|
+
sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) {
|
291
659
|
int elements = 0, slots = 5, start = 0, j;
|
660
|
+
sds *tokens;
|
661
|
+
|
662
|
+
if (seplen < 1 || len < 0) return NULL;
|
663
|
+
|
664
|
+
tokens = malloc(sizeof(sds)*slots);
|
665
|
+
if (tokens == NULL) return NULL;
|
292
666
|
|
293
|
-
sds *tokens = malloc(sizeof(sds)*slots);
|
294
|
-
#ifdef SDS_ABORT_ON_OOM
|
295
|
-
if (tokens == NULL) sdsOomAbort();
|
296
|
-
#endif
|
297
|
-
if (seplen < 1 || len < 0 || tokens == NULL) return NULL;
|
298
667
|
if (len == 0) {
|
299
668
|
*count = 0;
|
300
669
|
return tokens;
|
@@ -306,25 +675,13 @@ sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
|
|
306
675
|
|
307
676
|
slots *= 2;
|
308
677
|
newtokens = realloc(tokens,sizeof(sds)*slots);
|
309
|
-
if (newtokens == NULL)
|
310
|
-
#ifdef SDS_ABORT_ON_OOM
|
311
|
-
sdsOomAbort();
|
312
|
-
#else
|
313
|
-
goto cleanup;
|
314
|
-
#endif
|
315
|
-
}
|
678
|
+
if (newtokens == NULL) goto cleanup;
|
316
679
|
tokens = newtokens;
|
317
680
|
}
|
318
681
|
/* search the separator */
|
319
682
|
if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
|
320
683
|
tokens[elements] = sdsnewlen(s+start,j-start);
|
321
|
-
if (tokens[elements] == NULL)
|
322
|
-
#ifdef SDS_ABORT_ON_OOM
|
323
|
-
sdsOomAbort();
|
324
|
-
#else
|
325
|
-
goto cleanup;
|
326
|
-
#endif
|
327
|
-
}
|
684
|
+
if (tokens[elements] == NULL) goto cleanup;
|
328
685
|
elements++;
|
329
686
|
start = j+seplen;
|
330
687
|
j = j+seplen-1; /* skip the separator */
|
@@ -332,28 +689,22 @@ sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
|
|
332
689
|
}
|
333
690
|
/* Add the final element. We are sure there is room in the tokens array. */
|
334
691
|
tokens[elements] = sdsnewlen(s+start,len-start);
|
335
|
-
if (tokens[elements] == NULL)
|
336
|
-
#ifdef SDS_ABORT_ON_OOM
|
337
|
-
sdsOomAbort();
|
338
|
-
#else
|
339
|
-
goto cleanup;
|
340
|
-
#endif
|
341
|
-
}
|
692
|
+
if (tokens[elements] == NULL) goto cleanup;
|
342
693
|
elements++;
|
343
694
|
*count = elements;
|
344
695
|
return tokens;
|
345
696
|
|
346
|
-
#ifndef SDS_ABORT_ON_OOM
|
347
697
|
cleanup:
|
348
698
|
{
|
349
699
|
int i;
|
350
700
|
for (i = 0; i < elements; i++) sdsfree(tokens[i]);
|
351
701
|
free(tokens);
|
702
|
+
*count = 0;
|
352
703
|
return NULL;
|
353
704
|
}
|
354
|
-
#endif
|
355
705
|
}
|
356
706
|
|
707
|
+
/* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */
|
357
708
|
void sdsfreesplitres(sds *tokens, int count) {
|
358
709
|
if (!tokens) return;
|
359
710
|
while(count--)
|
@@ -361,6 +712,10 @@ void sdsfreesplitres(sds *tokens, int count) {
|
|
361
712
|
free(tokens);
|
362
713
|
}
|
363
714
|
|
715
|
+
/* Create an sds string from a long long value. It is much faster than:
|
716
|
+
*
|
717
|
+
* sdscatprintf(sdsempty(),"%lld\n", value);
|
718
|
+
*/
|
364
719
|
sds sdsfromlonglong(long long value) {
|
365
720
|
char buf[32], *p;
|
366
721
|
unsigned long long v;
|
@@ -376,10 +731,14 @@ sds sdsfromlonglong(long long value) {
|
|
376
731
|
return sdsnewlen(p,32-(p-buf));
|
377
732
|
}
|
378
733
|
|
379
|
-
|
734
|
+
/* Append to the sds string "s" an escaped string representation where
|
735
|
+
* all the non-printable characters (tested with isprint()) are turned into
|
736
|
+
* escapes in the form "\n\r\a...." or "\x<hex-number>".
|
737
|
+
*
|
738
|
+
* After the call, the modified sds string is no longer valid and all the
|
739
|
+
* references must be substituted with the new pointer returned by the call. */
|
740
|
+
sds sdscatrepr(sds s, const char *p, size_t len) {
|
380
741
|
s = sdscatlen(s,"\"",1);
|
381
|
-
if (s == NULL) return NULL;
|
382
|
-
|
383
742
|
while(len--) {
|
384
743
|
switch(*p) {
|
385
744
|
case '\\':
|
@@ -399,27 +758,64 @@ sds sdscatrepr(sds s, char *p, size_t len) {
|
|
399
758
|
break;
|
400
759
|
}
|
401
760
|
p++;
|
402
|
-
if (s == NULL) return NULL;
|
403
761
|
}
|
404
762
|
return sdscatlen(s,"\"",1);
|
405
763
|
}
|
406
764
|
|
765
|
+
/* Helper function for sdssplitargs() that returns non zero if 'c'
|
766
|
+
* is a valid hex digit. */
|
767
|
+
int is_hex_digit(char c) {
|
768
|
+
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
|
769
|
+
(c >= 'A' && c <= 'F');
|
770
|
+
}
|
771
|
+
|
772
|
+
/* Helper function for sdssplitargs() that converts a hex digit into an
|
773
|
+
* integer from 0 to 15 */
|
774
|
+
int hex_digit_to_int(char c) {
|
775
|
+
switch(c) {
|
776
|
+
case '0': return 0;
|
777
|
+
case '1': return 1;
|
778
|
+
case '2': return 2;
|
779
|
+
case '3': return 3;
|
780
|
+
case '4': return 4;
|
781
|
+
case '5': return 5;
|
782
|
+
case '6': return 6;
|
783
|
+
case '7': return 7;
|
784
|
+
case '8': return 8;
|
785
|
+
case '9': return 9;
|
786
|
+
case 'a': case 'A': return 10;
|
787
|
+
case 'b': case 'B': return 11;
|
788
|
+
case 'c': case 'C': return 12;
|
789
|
+
case 'd': case 'D': return 13;
|
790
|
+
case 'e': case 'E': return 14;
|
791
|
+
case 'f': case 'F': return 15;
|
792
|
+
default: return 0;
|
793
|
+
}
|
794
|
+
}
|
795
|
+
|
407
796
|
/* Split a line into arguments, where every argument can be in the
|
408
797
|
* following programming-language REPL-alike form:
|
409
798
|
*
|
410
799
|
* foo bar "newline are supported\n" and "\xff\x00otherstuff"
|
411
800
|
*
|
412
801
|
* The number of arguments is stored into *argc, and an array
|
413
|
-
* of sds is returned.
|
414
|
-
*
|
802
|
+
* of sds is returned.
|
803
|
+
*
|
804
|
+
* The caller should free the resulting array of sds strings with
|
805
|
+
* sdsfreesplitres().
|
415
806
|
*
|
416
807
|
* Note that sdscatrepr() is able to convert back a string into
|
417
808
|
* a quoted string in the same format sdssplitargs() is able to parse.
|
809
|
+
*
|
810
|
+
* The function returns the allocated tokens on success, even when the
|
811
|
+
* input string is empty, or NULL if the input contains unbalanced
|
812
|
+
* quotes or closed quotes followed by non space characters
|
813
|
+
* as in: "foo"bar or "foo'
|
418
814
|
*/
|
419
|
-
sds *sdssplitargs(char *line, int *argc) {
|
420
|
-
char *p = line;
|
815
|
+
sds *sdssplitargs(const char *line, int *argc) {
|
816
|
+
const char *p = line;
|
421
817
|
char *current = NULL;
|
422
|
-
char **vector = NULL
|
818
|
+
char **vector = NULL;
|
423
819
|
|
424
820
|
*argc = 0;
|
425
821
|
while(1) {
|
@@ -427,17 +823,24 @@ sds *sdssplitargs(char *line, int *argc) {
|
|
427
823
|
while(*p && isspace(*p)) p++;
|
428
824
|
if (*p) {
|
429
825
|
/* get a token */
|
430
|
-
int inq=0;
|
826
|
+
int inq=0; /* set to 1 if we are in "quotes" */
|
827
|
+
int insq=0; /* set to 1 if we are in 'single quotes' */
|
431
828
|
int done=0;
|
432
829
|
|
433
|
-
if (current == NULL)
|
434
|
-
current = sdsempty();
|
435
|
-
if (current == NULL) goto err;
|
436
|
-
}
|
437
|
-
|
830
|
+
if (current == NULL) current = sdsempty();
|
438
831
|
while(!done) {
|
439
832
|
if (inq) {
|
440
|
-
if (*p == '\\' && *(p+1)
|
833
|
+
if (*p == '\\' && *(p+1) == 'x' &&
|
834
|
+
is_hex_digit(*(p+2)) &&
|
835
|
+
is_hex_digit(*(p+3)))
|
836
|
+
{
|
837
|
+
unsigned char byte;
|
838
|
+
|
839
|
+
byte = (hex_digit_to_int(*(p+2))*16)+
|
840
|
+
hex_digit_to_int(*(p+3));
|
841
|
+
current = sdscatlen(current,(char*)&byte,1);
|
842
|
+
p += 3;
|
843
|
+
} else if (*p == '\\' && *(p+1)) {
|
441
844
|
char c;
|
442
845
|
|
443
846
|
p++;
|
@@ -451,7 +854,23 @@ sds *sdssplitargs(char *line, int *argc) {
|
|
451
854
|
}
|
452
855
|
current = sdscatlen(current,&c,1);
|
453
856
|
} else if (*p == '"') {
|
454
|
-
/* closing quote must be followed by a space
|
857
|
+
/* closing quote must be followed by a space or
|
858
|
+
* nothing at all. */
|
859
|
+
if (*(p+1) && !isspace(*(p+1))) goto err;
|
860
|
+
done=1;
|
861
|
+
} else if (!*p) {
|
862
|
+
/* unterminated quotes */
|
863
|
+
goto err;
|
864
|
+
} else {
|
865
|
+
current = sdscatlen(current,p,1);
|
866
|
+
}
|
867
|
+
} else if (insq) {
|
868
|
+
if (*p == '\\' && *(p+1) == '\'') {
|
869
|
+
p++;
|
870
|
+
current = sdscatlen(current,"'",1);
|
871
|
+
} else if (*p == '\'') {
|
872
|
+
/* closing quote must be followed by a space or
|
873
|
+
* nothing at all. */
|
455
874
|
if (*(p+1) && !isspace(*(p+1))) goto err;
|
456
875
|
done=1;
|
457
876
|
} else if (!*p) {
|
@@ -472,23 +891,24 @@ sds *sdssplitargs(char *line, int *argc) {
|
|
472
891
|
case '"':
|
473
892
|
inq=1;
|
474
893
|
break;
|
894
|
+
case '\'':
|
895
|
+
insq=1;
|
896
|
+
break;
|
475
897
|
default:
|
476
898
|
current = sdscatlen(current,p,1);
|
477
899
|
break;
|
478
900
|
}
|
479
901
|
}
|
480
902
|
if (*p) p++;
|
481
|
-
if (current == NULL) goto err;
|
482
903
|
}
|
483
904
|
/* add the token to the vector */
|
484
|
-
|
485
|
-
if (_vector == NULL) goto err;
|
486
|
-
|
487
|
-
vector = _vector;
|
905
|
+
vector = realloc(vector,((*argc)+1)*sizeof(char*));
|
488
906
|
vector[*argc] = current;
|
489
907
|
(*argc)++;
|
490
908
|
current = NULL;
|
491
909
|
} else {
|
910
|
+
/* Even on empty input string return something not NULL. */
|
911
|
+
if (vector == NULL) vector = malloc(sizeof(void*));
|
492
912
|
return vector;
|
493
913
|
}
|
494
914
|
}
|
@@ -496,30 +916,67 @@ sds *sdssplitargs(char *line, int *argc) {
|
|
496
916
|
err:
|
497
917
|
while((*argc)--)
|
498
918
|
sdsfree(vector[*argc]);
|
499
|
-
|
500
|
-
if (current
|
919
|
+
free(vector);
|
920
|
+
if (current) sdsfree(current);
|
921
|
+
*argc = 0;
|
501
922
|
return NULL;
|
502
923
|
}
|
503
924
|
|
925
|
+
/* Modify the string substituting all the occurrences of the set of
|
926
|
+
* characters specified in the 'from' string to the corresponding character
|
927
|
+
* in the 'to' array.
|
928
|
+
*
|
929
|
+
* For instance: sdsmapchars(mystring, "ho", "01", 2)
|
930
|
+
* will have the effect of turning the string "hello" into "0ell1".
|
931
|
+
*
|
932
|
+
* The function returns the sds string pointer, that is always the same
|
933
|
+
* as the input pointer since no resize is needed. */
|
934
|
+
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {
|
935
|
+
size_t j, i, l = sdslen(s);
|
936
|
+
|
937
|
+
for (j = 0; j < l; j++) {
|
938
|
+
for (i = 0; i < setlen; i++) {
|
939
|
+
if (s[j] == from[i]) {
|
940
|
+
s[j] = to[i];
|
941
|
+
break;
|
942
|
+
}
|
943
|
+
}
|
944
|
+
}
|
945
|
+
return s;
|
946
|
+
}
|
947
|
+
|
948
|
+
/* Join an array of C strings using the specified separator (also a C string).
|
949
|
+
* Returns the result as an sds string. */
|
950
|
+
sds sdsjoin(char **argv, int argc, char *sep, size_t seplen) {
|
951
|
+
sds join = sdsempty();
|
952
|
+
int j;
|
953
|
+
|
954
|
+
for (j = 0; j < argc; j++) {
|
955
|
+
join = sdscat(join, argv[j]);
|
956
|
+
if (j != argc-1) join = sdscatlen(join,sep,seplen);
|
957
|
+
}
|
958
|
+
return join;
|
959
|
+
}
|
960
|
+
|
961
|
+
/* Like sdsjoin, but joins an array of SDS strings. */
|
962
|
+
sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {
|
963
|
+
sds join = sdsempty();
|
964
|
+
int j;
|
965
|
+
|
966
|
+
for (j = 0; j < argc; j++) {
|
967
|
+
join = sdscatsds(join, argv[j]);
|
968
|
+
if (j != argc-1) join = sdscatlen(join,sep,seplen);
|
969
|
+
}
|
970
|
+
return join;
|
971
|
+
}
|
972
|
+
|
504
973
|
#ifdef SDS_TEST_MAIN
|
505
974
|
#include <stdio.h>
|
506
|
-
|
507
|
-
int __failed_tests = 0;
|
508
|
-
int __test_num = 0;
|
509
|
-
#define test_cond(descr,_c) do { \
|
510
|
-
__test_num++; printf("%d - %s: ", __test_num, descr); \
|
511
|
-
if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \
|
512
|
-
} while(0);
|
513
|
-
#define test_report() do { \
|
514
|
-
printf("%d tests, %d passed, %d failed\n", __test_num, \
|
515
|
-
__test_num-__failed_tests, __failed_tests); \
|
516
|
-
if (__failed_tests) { \
|
517
|
-
printf("=== WARNING === We have failed tests here...\n"); \
|
518
|
-
} \
|
519
|
-
} while(0);
|
975
|
+
#include "testhelp.h"
|
520
976
|
|
521
977
|
int main(void) {
|
522
978
|
{
|
979
|
+
struct sdshdr *sh;
|
523
980
|
sds x = sdsnew("foo"), y;
|
524
981
|
|
525
982
|
test_cond("Create a string and obtain the length",
|
@@ -549,36 +1006,43 @@ int main(void) {
|
|
549
1006
|
sdslen(x) == 3 && memcmp(x,"123\0",4) ==0)
|
550
1007
|
|
551
1008
|
sdsfree(x);
|
552
|
-
x =
|
1009
|
+
x = sdsnew("xxciaoyyy");
|
1010
|
+
sdstrim(x,"xy");
|
553
1011
|
test_cond("sdstrim() correctly trims characters",
|
554
1012
|
sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)
|
555
1013
|
|
556
|
-
y =
|
1014
|
+
y = sdsdup(x);
|
1015
|
+
sdsrange(y,1,1);
|
557
1016
|
test_cond("sdsrange(...,1,1)",
|
558
1017
|
sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)
|
559
1018
|
|
560
1019
|
sdsfree(y);
|
561
|
-
y =
|
1020
|
+
y = sdsdup(x);
|
1021
|
+
sdsrange(y,1,-1);
|
562
1022
|
test_cond("sdsrange(...,1,-1)",
|
563
1023
|
sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
|
564
1024
|
|
565
1025
|
sdsfree(y);
|
566
|
-
y =
|
1026
|
+
y = sdsdup(x);
|
1027
|
+
sdsrange(y,-2,-1);
|
567
1028
|
test_cond("sdsrange(...,-2,-1)",
|
568
1029
|
sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)
|
569
1030
|
|
570
1031
|
sdsfree(y);
|
571
|
-
y =
|
1032
|
+
y = sdsdup(x);
|
1033
|
+
sdsrange(y,2,1);
|
572
1034
|
test_cond("sdsrange(...,2,1)",
|
573
1035
|
sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
|
574
1036
|
|
575
1037
|
sdsfree(y);
|
576
|
-
y =
|
1038
|
+
y = sdsdup(x);
|
1039
|
+
sdsrange(y,1,100);
|
577
1040
|
test_cond("sdsrange(...,1,100)",
|
578
1041
|
sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
|
579
1042
|
|
580
1043
|
sdsfree(y);
|
581
|
-
y =
|
1044
|
+
y = sdsdup(x);
|
1045
|
+
sdsrange(y,100,100);
|
582
1046
|
test_cond("sdsrange(...,100,100)",
|
583
1047
|
sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
|
584
1048
|
|
@@ -599,7 +1063,33 @@ int main(void) {
|
|
599
1063
|
x = sdsnew("aar");
|
600
1064
|
y = sdsnew("bar");
|
601
1065
|
test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
|
1066
|
+
|
1067
|
+
sdsfree(y);
|
1068
|
+
sdsfree(x);
|
1069
|
+
x = sdsnewlen("\a\n\0foo\r",7);
|
1070
|
+
y = sdscatrepr(sdsempty(),x,sdslen(x));
|
1071
|
+
test_cond("sdscatrepr(...data...)",
|
1072
|
+
memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0)
|
1073
|
+
|
1074
|
+
{
|
1075
|
+
int oldfree;
|
1076
|
+
|
1077
|
+
sdsfree(x);
|
1078
|
+
x = sdsnew("0");
|
1079
|
+
sh = (void*) (x-(sizeof(struct sdshdr)));
|
1080
|
+
test_cond("sdsnew() free/len buffers", sh->len == 1 && sh->free == 0);
|
1081
|
+
x = sdsMakeRoomFor(x,1);
|
1082
|
+
sh = (void*) (x-(sizeof(struct sdshdr)));
|
1083
|
+
test_cond("sdsMakeRoomFor()", sh->len == 1 && sh->free > 0);
|
1084
|
+
oldfree = sh->free;
|
1085
|
+
x[1] = '1';
|
1086
|
+
sdsIncrLen(x,1);
|
1087
|
+
test_cond("sdsIncrLen() -- content", x[0] == '0' && x[1] == '1');
|
1088
|
+
test_cond("sdsIncrLen() -- len", sh->len == 2);
|
1089
|
+
test_cond("sdsIncrLen() -- free", sh->free == oldfree-1);
|
1090
|
+
}
|
602
1091
|
}
|
603
1092
|
test_report()
|
1093
|
+
return 0;
|
604
1094
|
}
|
605
1095
|
#endif
|