Fix OTS warning about `maxp.maxSizeOfInstructions`.
[ttfautohint.git] / lib / sds.c
blob8293a61f7ed9cba7c0bf729edde1c01be67b08b8
1 /* SDS (Simple Dynamic Strings), A C dynamic strings library.
3 * Copyright (c) 2006-2014, Salvatore Sanfilippo <antirez at gmail dot com>
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
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.
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.
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <assert.h>
37 #include "sds.h"
39 typedef struct sdshdr_ {
40 size_t len;
41 size_t free;
42 char buf[];
43 } sdshdr;
45 static INLINE sdshdr *sds_start(const sds s)
47 return (sdshdr*) (s-(int)offsetof(sdshdr, buf));
50 /* Create a new sds string with the content specified by the 'init' pointer
51 * and 'initlen'.
52 * If NULL is used for 'init' the string is initialized with zero bytes.
54 * The string is always null-termined (all the sds strings are, always) so
55 * even if you create an sds string with:
57 * mystring = sdsnewlen("abc",3");
59 * You can print the string with printf() as there is an implicit \0 at the
60 * end of the string. However the string is binary safe and can contain
61 * \0 characters in the middle, as the length is stored in the sds header. */
62 sds sdsnewlen(const void *init, size_t initlen) {
63 sdshdr *sh;
65 if (init) {
66 sh = (sdshdr*) malloc(sizeof *sh+initlen+1);
67 } else {
68 sh = (sdshdr*) calloc(sizeof *sh+initlen+1,1);
70 if (sh == NULL) return NULL;
71 sh->len = initlen;
72 sh->free = 0;
73 if (initlen && init)
74 memcpy(sh->buf, init, initlen);
75 sh->buf[initlen] = '\0';
76 return (char*)sh->buf;
79 /* Create an empty (zero length) sds string. Even in this case the string
80 * always has an implicit null term. */
81 sds sdsempty(void) {
82 return sdsnewlen("",0);
85 /* Create a new sds string starting from a null termined C string. */
86 sds sdsnew(const char *init) {
87 size_t initlen = (init == NULL) ? 0 : strlen(init);
88 return sdsnewlen(init, initlen);
91 /* Duplicate an sds string. */
92 sds sdsdup(const sds s) {
93 return sdsnewlen(s, sdslen(s));
96 /* Free an sds string. No operation is performed if 's' is NULL. */
97 void sdsfree(sds s) {
98 if (s == NULL) return;
100 free(sds_start(s));
103 /* Set the sds string length to the length as obtained with strlen(), so
104 * considering as content only up to the first null term character.
106 * This function is useful when the sds string is hacked manually in some
107 * way, like in the following example:
109 * s = sdsnew("foobar");
110 * s[2] = '\0';
111 * sdsupdatelen(s);
112 * printf("%d\n", sdslen(s));
114 * The output will be "2", but if we comment out the call to sdsupdatelen()
115 * the output will be "6" as the string was modified but the logical length
116 * remains 6 bytes. */
117 void sdsupdatelen(sds s) {
118 if (s == NULL) return;
120 sdshdr *sh = sds_start(s);
121 size_t reallen = strlen(s);
122 sh->free += (sh->len-reallen);
123 sh->len = reallen;
126 /* Modify an sds string on-place to make it empty (zero length).
127 * However all the existing buffer is not discarded but set as free space
128 * so that next append operations will not require allocations up to the
129 * number of bytes previously available. */
130 void sdsclear(sds s) {
131 if (s == NULL) return;
133 sdshdr *sh = sds_start(s);
134 sh->free += sh->len;
135 sh->len = 0;
136 sh->buf[0] = '\0';
139 /* Enlarge the free space at the end of the sds string so that the caller
140 * is sure that after calling this function can overwrite up to addlen
141 * bytes after the end of the string, plus one more byte for nul term.
143 * Note: this does not change the *length* of the sds string as returned
144 * by sdslen(), but only the free buffer space we have. */
145 sds sdsMakeRoomFor(sds s, size_t addlen) {
146 if (s == NULL) return NULL;
148 sdshdr *sh, *newsh;
149 size_t free = sdsavail(s);
150 size_t len, newlen;
152 if (free >= addlen) return s;
153 len = sdslen(s);
154 sh = sds_start(s);
155 newlen = (len+addlen);
156 if (newlen < SDS_MAX_PREALLOC)
157 newlen *= 2;
158 else
159 newlen += SDS_MAX_PREALLOC;
160 newsh = (sdshdr*) realloc(sh, sizeof *newsh+newlen+1);
161 if (newsh == NULL) return NULL;
163 newsh->free = newlen - len;
164 return newsh->buf;
167 /* Reallocate the sds string so that it has no free space at the end. The
168 * contained string remains not altered, but next concatenation operations
169 * will require a reallocation.
171 * After the call, the passed sds string is no longer valid and all the
172 * references must be substituted with the new pointer returned by the call. */
173 sds sdsRemoveFreeSpace(sds s) {
174 if (s == NULL) return NULL;
176 sdshdr *sh, *newsh;
178 sh = sds_start(s);
179 newsh = (sdshdr*) realloc(sh, sizeof *sh+sh->len+1);
180 if (newsh == NULL) return NULL;
182 newsh->free = 0;
183 return newsh->buf;
186 /* Return the total size of the allocation of the specifed sds string,
187 * including:
188 * 1) The sds header before the pointer.
189 * 2) The string.
190 * 3) The free buffer at the end if any.
191 * 4) The implicit null term.
193 size_t sdsAllocSize(sds s) {
194 if (s == NULL) return 0;
196 sdshdr *sh = sds_start(s);
198 return sizeof(*sh)+sh->len+sh->free+1;
201 /* Increment the sds length and decrements the left free space at the
202 * end of the string according to 'incr'. Also set the null term
203 * in the new end of the string.
205 * This function is used in order to fix the string length after the
206 * user calls sdsMakeRoomFor(), writes something after the end of
207 * the current string, and finally needs to set the new length.
209 * Note: it is possible to use a negative increment in order to
210 * right-trim the string.
212 * Usage example:
214 * Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the
215 * following schema, to cat bytes coming from the kernel to the end of an
216 * sds string without copying into an intermediate buffer:
218 * oldlen = sdslen(s);
219 * s = sdsMakeRoomFor(s, BUFFER_SIZE);
220 * nread = read(fd, s+oldlen, BUFFER_SIZE);
221 * ... check for nread <= 0 and handle it ...
222 * sdsIncrLen(s, nread);
224 void sdsIncrLen(sds s, int incr) {
225 if (s == NULL) return;
227 sdshdr *sh = sds_start(s);
229 if (incr >= 0) {
230 size_t tmp = (size_t)(incr);
231 assert(sh->free >= tmp);
232 sh->len += tmp;
233 sh->free -= tmp;
235 else {
236 size_t tmp = (size_t)(-incr);
237 assert(sh->len >= tmp);
238 sh->len -= tmp;
239 sh->free += tmp;
241 s[sh->len] = '\0';
244 /* Grow the sds to have the specified length. Bytes that were not part of
245 * the original length of the sds will be set to zero.
247 * if the specified length is smaller than the current length, no operation
248 * is performed. */
249 sds sdsgrowzero(sds s, size_t len) {
250 if (s == NULL) return NULL;
252 sdshdr *sh = sds_start(s);
253 size_t totlen, curlen = sh->len;
255 if (len <= curlen) return s;
256 s = sdsMakeRoomFor(s,len-curlen);
257 if (s == NULL) return NULL;
259 /* Make sure added region doesn't contain garbage */
260 sh = sds_start(s);
261 memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
262 totlen = sh->len+sh->free;
263 sh->len = len;
264 sh->free = totlen-sh->len;
265 return s;
268 /* Append the specified binary-safe string pointed by 't' of 'len' bytes to the
269 * end of the specified sds string 's'.
271 * After the call, the passed sds string is no longer valid and all the
272 * references must be substituted with the new pointer returned by the call. */
273 sds sdscatlen(sds s, const void *t, size_t len) {
274 sdshdr *sh;
275 size_t curlen = sdslen(s);
277 s = sdsMakeRoomFor(s,len);
278 if (s == NULL) return NULL;
279 sh = sds_start(s);
280 memcpy(s+curlen, t, len);
281 sh->len = curlen+len;
282 sh->free = sh->free-len;
283 s[curlen+len] = '\0';
284 return s;
287 /* Append the specified null termianted C string to the sds string 's'.
289 * After the call, the passed sds string is no longer valid and all the
290 * references must be substituted with the new pointer returned by the call. */
291 sds sdscat(sds s, const char *t) {
292 return sdscatlen(s, t, strlen(t));
295 /* Append the specified sds 't' to the existing sds 's'.
297 * After the call, the modified sds string is no longer valid and all the
298 * references must be substituted with the new pointer returned by the call. */
299 sds sdscatsds(sds s, const sds t) {
300 return sdscatlen(s, t, sdslen(t));
303 /* Destructively modify the sds string 's' to hold the specified binary
304 * safe string pointed by 't' of length 'len' bytes. */
305 sds sdscpylen(sds s, const char *t, size_t len) {
306 if (s == NULL) return NULL;
308 sdshdr *sh = sds_start(s);
309 size_t totlen = sh->free+sh->len;
311 if (totlen < len) {
312 s = sdsMakeRoomFor(s,len-sh->len);
313 if (s == NULL) return NULL;
314 sh = sds_start(s);
315 totlen = sh->free+sh->len;
317 memcpy(s, t, len);
318 s[len] = '\0';
319 sh->len = len;
320 sh->free = totlen-len;
321 return s;
324 /* Like sdscpylen() but 't' must be a null-termined string so that the length
325 * of the string is obtained with strlen(). */
326 sds sdscpy(sds s, const char *t) {
327 return sdscpylen(s, t, strlen(t));
330 /* Like sdscatpritf() but gets va_list instead of being variadic. */
331 sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
332 va_list cpy;
333 char *buf, *t;
334 size_t buflen = 16;
336 while(1) {
337 buf = (char*) malloc(buflen);
338 if (buf == NULL) return NULL;
339 buf[buflen-2] = '\0';
340 va_copy(cpy,ap);
341 vsnprintf(buf, buflen, fmt, cpy);
342 va_end(cpy);
343 if (buf[buflen-2] != '\0') {
344 free(buf);
345 buflen *= 2;
346 continue;
348 break;
350 t = sdscat(s, buf);
351 free(buf);
352 return t;
355 /* Append to the sds string 's' a string obtained using printf-alike format
356 * specifier.
358 * After the call, the modified sds string is no longer valid and all the
359 * references must be substituted with the new pointer returned by the call.
361 * Example:
363 * s = sdsempty("Sum is: ");
364 * s = sdscatprintf(s,"%d+%d = %d",a,b,a+b).
366 * Often you need to create a string from scratch with the printf-alike
367 * format. When this is the need, just use sdsempty() as the target string:
369 * s = sdscatprintf(sdsempty(), "... your format ...", args);
371 sds sdscatprintf(sds s, const char *fmt, ...) {
372 va_list ap;
373 char *t;
374 va_start(ap, fmt);
375 t = sdscatvprintf(s,fmt,ap);
376 va_end(ap);
377 return t;
380 /* Remove the part of the string from left and from right composed just of
381 * contiguous characters found in 'cset', that is a null terminted C string.
383 * After the call, the modified sds string is no longer valid and all the
384 * references must be substituted with the new pointer returned by the call.
386 * Example:
388 * s = sdsnew("AA...AA.a.aa.aHello World :::");
389 * sdstrim(s,"aA. :");
390 * printf("%s\n", s);
392 * Output will be just "Hello World".
394 void sdstrim(sds s, const char *cset) {
395 if (s == NULL) return;
397 sdshdr *sh = sds_start(s);
398 char *start, *end, *sp, *ep;
399 size_t len;
401 sp = start = s;
402 ep = end = s+sdslen(s)-1;
403 while(sp <= end && strchr(cset, *sp)) sp++;
404 while(ep > start && strchr(cset, *ep)) ep--;
405 len = (sp > ep) ? 0 : (size_t)((ep-sp)+1);
406 if (sh->buf != sp) memmove(sh->buf, sp, len);
407 sh->buf[len] = '\0';
408 sh->free = sh->free+(sh->len-len);
409 sh->len = len;
412 /* Turn the string into a smaller (or equal) string containing only the
413 * substring specified by the 'start' and 'end' indexes.
415 * start and end can be negative, where -1 means the last character of the
416 * string, -2 the penultimate character, and so forth.
418 * The interval is inclusive, so the start and end characters will be part
419 * of the resulting string.
421 * The string is modified in-place.
423 * Example:
425 * s = sdsnew("Hello World");
426 * sdsrange(s,1,-1); => "ello World"
428 void sdsrange(sds s, int start, int end) {
429 sdshdr *sh = sds_start(s);
430 size_t newlen, len = sdslen(s);
432 if (len == 0) return;
433 if (start < 0) {
434 start = (int)len+start;
435 if (start < 0) start = 0;
437 if (end < 0) {
438 end = (int)len+end;
439 if (end < -1) end = -1;
441 newlen = (start > end) ? 0 : (size_t)((end-start)+1);
442 if (newlen != 0) {
443 if (start >= (signed)len) {
444 newlen = 0;
445 } else if (end >= (signed)len) {
446 end = (int)len-1;
447 newlen = (start > end) ? 0 : (size_t)((end-start)+1);
449 } else {
450 start = 0;
452 if (start && newlen) memmove(sh->buf, sh->buf+start, newlen);
453 sh->buf[newlen] = 0;
454 sh->free = sh->free+(sh->len-newlen);
455 sh->len = newlen;
458 /* Apply tolower() to every character of the sds string 's'. */
459 void sdstolower(sds s) {
460 size_t len = sdslen(s), j;
462 for (j = 0; j < len; j++) s[j] = (char)tolower((unsigned char)s[j]);
465 /* Apply toupper() to every character of the sds string 's'. */
466 void sdstoupper(sds s) {
467 size_t len = sdslen(s), j;
469 for (j = 0; j < len; j++) s[j] = (char)toupper((unsigned char)s[j]);
472 /* Compare two sds strings s1 and s2 with memcmp().
474 * Return value:
476 * 1 if s1 > s2.
477 * -1 if s1 < s2.
478 * 0 if s1 and s2 are exactly the same binary string.
480 * If two strings share exactly the same prefix, but one of the two has
481 * additional characters, the longer string is considered to be greater than
482 * the smaller one. */
483 int sdscmp(const sds s1, const sds s2) {
484 size_t l1, l2, minlen;
485 int cmp;
487 l1 = sdslen(s1);
488 l2 = sdslen(s2);
489 minlen = (l1 < l2) ? l1 : l2;
490 cmp = memcmp(s1,s2,minlen);
491 if (cmp == 0) {
492 int diff = (int)l1 - (int)l2;
493 /* https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign */
494 return (diff > 0) - (diff < 0);
496 return cmp;
499 /* Split 's' with separator in 'sep'. An array
500 * of sds strings is returned. *count will be set
501 * by reference to the number of tokens returned.
503 * On out of memory, zero length string, zero length
504 * separator, NULL is returned.
506 * Note that 'sep' is able to split a string using
507 * a multi-character separator. For example
508 * sdssplit("foo_-_bar","_-_"); will return two
509 * elements "foo" and "bar".
511 * This version of the function is binary-safe but
512 * requires length arguments. sdssplit() is just the
513 * same function but for zero-terminated strings.
515 sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count) {
516 size_t elements = 0, slots = 5, start = 0, j;
517 size_t len_, seplen_;
518 sds *tokens;
520 if (seplen < 1 || len < 0) return NULL;
522 len_ = (size_t)len;
523 seplen_ = (size_t)seplen;
525 tokens = (sds*) malloc(sizeof(sds)*slots);
526 if (tokens == NULL) return NULL;
528 if (len_ == 0) {
529 *count = 0;
530 return tokens;
532 for (j = 0; j < (len_-(seplen_-1)); j++) {
533 /* make sure there is room for the next element and the final one */
534 if (slots < elements+2) {
535 sds *newtokens;
537 slots *= 2;
538 newtokens = (sds*) realloc(tokens,sizeof(sds)*slots);
539 if (newtokens == NULL) goto cleanup;
540 tokens = newtokens;
542 /* search the separator */
543 if ((seplen_ == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen_) == 0)) {
544 tokens[elements] = sdsnewlen(s+start,j-start);
545 if (tokens[elements] == NULL) goto cleanup;
546 elements++;
547 start = j+seplen_;
548 j = j+seplen_-1; /* skip the separator */
551 /* Add the final element. We are sure there is room in the tokens array. */
552 tokens[elements] = sdsnewlen(s+start,len_-start);
553 if (tokens[elements] == NULL) goto cleanup;
554 elements++;
555 *count = (int)elements;
556 return tokens;
558 cleanup:
560 size_t i;
561 for (i = 0; i < elements; i++) sdsfree(tokens[i]);
562 free(tokens);
563 *count = 0;
564 return NULL;
568 /* Free the result returned by sdssplitlen(), or do nothing if 'tokens' is NULL. */
569 void sdsfreesplitres(sds *tokens, int count) {
570 if (!tokens) return;
571 while(count--)
572 sdsfree(tokens[count]);
573 free(tokens);
576 /* Create an sds string from a long long value. It is much faster than:
578 * sdscatprintf(sdsempty(),"%lld\n", value);
580 sds sdsfromlonglong(long long value) {
581 char buf[32], *p;
582 unsigned long long v;
584 v = (unsigned long long)((value < 0) ? (-value) : value);
585 p = buf+31; /* point to the last character */
586 do {
587 *p-- = '0'+(v%10);
588 v /= 10;
589 } while(v);
590 if (value < 0) *p-- = '-';
591 p++;
592 return sdsnewlen(p,(size_t)(32-(p-buf)));
595 /* Append to the sds string "s" an escaped string representation where
596 * all the non-printable characters (tested with isprint()) are turned into
597 * escapes in the form "\n\r\a...." or "\x<hex-number>".
599 * After the call, the modified sds string is no longer valid and all the
600 * references must be substituted with the new pointer returned by the call. */
601 sds sdscatrepr(sds s, const char *p, size_t len) {
602 s = sdscatlen(s,"\"",1);
603 while(len--) {
604 switch(*p) {
605 case '\\':
606 case '"':
607 s = sdscatprintf(s,"\\%c",*p);
608 break;
609 case '\n': s = sdscatlen(s,"\\n",2); break;
610 case '\r': s = sdscatlen(s,"\\r",2); break;
611 case '\t': s = sdscatlen(s,"\\t",2); break;
612 case '\a': s = sdscatlen(s,"\\a",2); break;
613 case '\b': s = sdscatlen(s,"\\b",2); break;
614 default:
615 if (isprint((unsigned char)*p))
616 s = sdscatprintf(s,"%c",*p);
617 else
618 s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
619 break;
621 p++;
623 return sdscatlen(s,"\"",1);
626 /* Helper function for sdssplitargs() that returns non zero if 'c'
627 * is a valid hex digit. */
628 static int is_hex_digit(char c) {
629 return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
630 (c >= 'A' && c <= 'F');
633 /* Helper function for sdssplitargs() that converts a hex digit into an
634 * integer from 0 to 15 */
635 static int hex_digit_to_int(char c) {
636 switch(c) {
637 case '0': return 0;
638 case '1': return 1;
639 case '2': return 2;
640 case '3': return 3;
641 case '4': return 4;
642 case '5': return 5;
643 case '6': return 6;
644 case '7': return 7;
645 case '8': return 8;
646 case '9': return 9;
647 case 'a': case 'A': return 10;
648 case 'b': case 'B': return 11;
649 case 'c': case 'C': return 12;
650 case 'd': case 'D': return 13;
651 case 'e': case 'E': return 14;
652 case 'f': case 'F': return 15;
653 default: return 0;
657 /* Split a line into arguments, where every argument can be in the
658 * following programming-language REPL-alike form:
660 * foo bar "newline are supported\n" and "\xff\x00otherstuff"
662 * The number of arguments is stored into *argc, and an array
663 * of sds is returned.
665 * The caller should free the resulting array of sds strings with
666 * sdsfreesplitres().
668 * Note that sdscatrepr() is able to convert back a string into
669 * a quoted string in the same format sdssplitargs() is able to parse.
671 * The function returns the allocated tokens on success, even when the
672 * input string is empty, or NULL if the input contains unbalanced
673 * quotes or closed quotes followed by non space characters
674 * as in: "foo"bar or "foo'
676 sds *sdssplitargs(const char *line, int *argc) {
677 const char *p = line;
678 char *current = NULL;
679 char **vector = NULL;
681 *argc = 0;
682 while(1) {
683 /* skip blanks */
684 while(*p && isspace((unsigned char)*p)) p++;
685 if (*p) {
686 /* get a token */
687 int inq=0; /* set to 1 if we are in "quotes" */
688 int insq=0; /* set to 1 if we are in 'single quotes' */
689 int done=0;
691 if (current == NULL) current = sdsempty();
692 while(!done) {
693 if (inq) {
694 if (*p == '\\' && *(p+1) == 'x' &&
695 is_hex_digit(*(p+2)) &&
696 is_hex_digit(*(p+3)))
698 unsigned char byte;
700 byte = (unsigned char)((hex_digit_to_int(*(p+2))*16)+
701 hex_digit_to_int(*(p+3)));
702 current = sdscatlen(current,(char*)&byte,1);
703 p += 3;
704 } else if (*p == '\\' && *(p+1)) {
705 char c;
707 p++;
708 switch(*p) {
709 case 'n': c = '\n'; break;
710 case 'r': c = '\r'; break;
711 case 't': c = '\t'; break;
712 case 'b': c = '\b'; break;
713 case 'a': c = '\a'; break;
714 default: c = *p; break;
716 current = sdscatlen(current,&c,1);
717 } else if (*p == '"') {
718 /* closing quote must be followed by a space or
719 * nothing at all. */
720 if (*(p+1) && !isspace((unsigned char)*(p+1))) goto err;
721 done=1;
722 } else if (!*p) {
723 /* unterminated quotes */
724 goto err;
725 } else {
726 current = sdscatlen(current,p,1);
728 } else if (insq) {
729 if (*p == '\\' && *(p+1) == '\'') {
730 p++;
731 current = sdscatlen(current,"'",1);
732 } else if (*p == '\'') {
733 /* closing quote must be followed by a space or
734 * nothing at all. */
735 if (*(p+1) && !isspace((unsigned char)*(p+1))) goto err;
736 done=1;
737 } else if (!*p) {
738 /* unterminated quotes */
739 goto err;
740 } else {
741 current = sdscatlen(current,p,1);
743 } else {
744 switch(*p) {
745 case ' ':
746 case '\n':
747 case '\r':
748 case '\t':
749 case '\0':
750 done=1;
751 break;
752 case '"':
753 inq=1;
754 break;
755 case '\'':
756 insq=1;
757 break;
758 default:
759 current = sdscatlen(current,p,1);
760 break;
763 if (*p) p++;
765 /* add the token to the vector */
766 vector = (char**) realloc(vector,(size_t)((*argc)+1)*sizeof(char*));
767 vector[*argc] = current;
768 (*argc)++;
769 current = NULL;
770 } else {
771 /* Even on empty input string return something not NULL. */
772 if (vector == NULL) vector = (char**) malloc(sizeof(void*));
773 return vector;
777 err:
778 while((*argc)--)
779 sdsfree(vector[*argc]);
780 free(vector);
781 if (current) sdsfree(current);
782 *argc = 0;
783 return NULL;
786 /* Modify the string substituting all the occurrences of the set of
787 * characters specified in the 'from' string to the corresponding character
788 * in the 'to' array.
790 * For instance: sdsmapchars(mystring, "ho", "01", 2)
791 * will have the effect of turning the string "hello" into "0ell1".
793 * The function returns the sds string pointer, that is always the same
794 * as the input pointer since no resize is needed. */
795 sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {
796 size_t j, i, l = sdslen(s);
798 for (j = 0; j < l; j++) {
799 for (i = 0; i < setlen; i++) {
800 if (s[j] == from[i]) {
801 s[j] = to[i];
802 break;
806 return s;
809 /* Join an array of C strings using the specified separator (also a C string).
810 * Returns the result as an sds string. */
811 sds sdsjoin(char **argv, int argc, char *sep, size_t seplen) {
812 sds join = sdsempty();
813 int j;
815 for (j = 0; j < argc; j++) {
816 join = sdscat(join, argv[j]);
817 if (j != argc-1) join = sdscatlen(join,sep,seplen);
819 return join;
822 /* Like sdsjoin, but joins an array of SDS strings. */
823 sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen) {
824 sds join = sdsempty();
825 int j;
827 for (j = 0; j < argc; j++) {
828 join = sdscatsds(join, argv[j]);
829 if (j != argc-1) join = sdscatlen(join,sep,seplen);
831 return join;
834 #ifdef SDS_TEST_MAIN
835 #include <stdio.h>
836 #include "testhelp.h"
838 int main(void) {
840 sdshdr *sh;
841 sds x = sdsnew("foo"), y;
843 test_cond("Create a string and obtain the length",
844 sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0)
846 sdsfree(x);
847 x = sdsnewlen("foo",2);
848 test_cond("Create a string with specified length",
849 sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0)
851 x = sdscat(x,"bar");
852 test_cond("Strings concatenation",
853 sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0);
855 x = sdscpy(x,"a");
856 test_cond("sdscpy() against an originally longer string",
857 sdslen(x) == 1 && memcmp(x,"a\0",2) == 0)
859 x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk");
860 test_cond("sdscpy() against an originally shorter string",
861 sdslen(x) == 33 &&
862 memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0)
864 sdsfree(x);
865 x = sdscatprintf(sdsempty(),"%d",123);
866 test_cond("sdscatprintf() seems working in the base case",
867 sdslen(x) == 3 && memcmp(x,"123\0",4) ==0)
869 sdsfree(x);
870 x = sdsnew("xxciaoyyy");
871 sdstrim(x,"xy");
872 test_cond("sdstrim() correctly trims characters",
873 sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)
875 y = sdsdup(x);
876 sdsrange(y,1,1);
877 test_cond("sdsrange(...,1,1)",
878 sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)
880 sdsfree(y);
881 y = sdsdup(x);
882 sdsrange(y,1,-1);
883 test_cond("sdsrange(...,1,-1)",
884 sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
886 sdsfree(y);
887 y = sdsdup(x);
888 sdsrange(y,-2,-1);
889 test_cond("sdsrange(...,-2,-1)",
890 sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)
892 sdsfree(y);
893 y = sdsdup(x);
894 sdsrange(y,2,1);
895 test_cond("sdsrange(...,2,1)",
896 sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
898 sdsfree(y);
899 y = sdsdup(x);
900 sdsrange(y,1,100);
901 test_cond("sdsrange(...,1,100)",
902 sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
904 sdsfree(y);
905 y = sdsdup(x);
906 sdsrange(y,100,100);
907 test_cond("sdsrange(...,100,100)",
908 sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
910 sdsfree(y);
911 sdsfree(x);
912 x = sdsnew("foo");
913 y = sdsnew("foa");
914 test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0)
916 sdsfree(y);
917 sdsfree(x);
918 x = sdsnew("bar");
919 y = sdsnew("bar");
920 test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0)
922 sdsfree(y);
923 sdsfree(x);
924 x = sdsnew("aar");
925 y = sdsnew("bar");
926 test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
928 sdsfree(y);
929 sdsfree(x);
930 x = sdsnewlen("\a\n\0foo\r",7);
931 y = sdscatrepr(sdsempty(),x,sdslen(x));
932 test_cond("sdscatrepr(...data...)",
933 memcmp(y,"\"\\a\\n\\x00foo\\r\"",15) == 0)
934 sdsfree(y);
937 size_t oldfree;
939 sdsfree(x);
940 x = sdsnew("0");
941 sh = sds_start(x);
942 test_cond("sdsnew() free/len buffers", sh->len == 1 && sh->free == 0);
943 x = sdsMakeRoomFor(x,1);
944 sh = sds_start(x);
945 test_cond("sdsMakeRoomFor()", sh->len == 1 && sh->free > 0);
946 oldfree = sh->free;
947 x[1] = '1';
948 sdsIncrLen(x,1);
949 test_cond("sdsIncrLen() -- content", x[0] == '0' && x[1] == '1');
950 test_cond("sdsIncrLen() -- len", sh->len == 2);
951 test_cond("sdsIncrLen() -- free", sh->free == oldfree-1);
952 sdsfree(x);
955 x = sdsnew("0FoO1bar\n");
956 sdstolower(x);
957 test_cond("sdstolower(...)",
958 memcmp(x,"0foo1bar\n\0",10) == 0)
960 sdsfree(x);
962 x = sdsnew("0FoO1bar\n");
963 sdstoupper(x);
964 test_cond("sdstoupper(...)",
965 memcmp(x,"0FOO1BAR\n\0",10) == 0)
967 sdsfree(x);
969 test_report()
970 return 0;
972 #endif