cstring

Lightweight string library for C
git clone git://git.margiolis.net/cstring.git
Log | Files | Refs | README | LICENSE

cstring.c (8149B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <stdio.h>
      3 #include <stdlib.h>
      4 #include <string.h>
      5 
      6 #include "cstring.h"
      7 
      8 #define CSTRING_EXCEEDS_CAPACITY(len, cap) ((len) >= (cap))
      9 #define FIND_OCC(cs, s, f) do {			\
     10 	char *_found;				\
     11 	if ((_found = f(cs->str, (s))) != NULL) \
     12 		return (_found - cs->str);	\
     13 } while (0)
     14 
     15 static int cstring_is_one_of(char, const char *);
     16 static void *emalloc(size_t);
     17 static void csfree(cstring *);
     18 static inline int cstring_cmp_greater(const void *, const void *);
     19 static inline int cstring_cmp_less(const void *, const void *);
     20 static inline int cstring_cmp_char_greater(const void *, const void *);
     21 static inline int cstring_cmp_char_less(const void *, const void *);
     22 
     23 static int
     24 cstring_is_one_of(char c, const char *s)
     25 {
     26 	for (; *s; s++)
     27 		if (*s == c)
     28 			return (1);
     29 	return (0);
     30 }
     31 
     32 static void *
     33 emalloc(size_t nb)
     34 {
     35 	void *p;
     36 
     37 	if ((p= malloc(nb)) == NULL) {
     38 		perror("malloc");
     39 		exit(1);
     40 	}
     41 	return (p);
     42 }
     43 
     44 static void
     45 csfree(cstring *cs)
     46 {
     47 	if (!cstring_empty(cs))
     48 		free(cs->str);
     49 }
     50 
     51 static inline int
     52 cstring_cmp_greater(const void *lhs, const void *rhs)
     53 {
     54 	return (cstring_greater((cstring *)lhs, (cstring *)rhs));
     55 }
     56 
     57 static inline int
     58 cstring_cmp_less(const void *lhs, const void *rhs)
     59 {
     60 	return (cstring_less((cstring *)lhs, (cstring *)rhs));
     61 }
     62 
     63 static inline int
     64 cstring_cmp_char_greater(const void *lhs, const void *rhs)
     65 {
     66 	return ((*(char *)lhs - *(char *)rhs));
     67 }
     68 
     69 static inline int
     70 cstring_cmp_char_less(const void *lhs, const void *rhs)
     71 {
     72 	return (-(*(char *)lhs - *(char *)rhs));
     73 }
     74 
     75 cstring
     76 cstring_create(const char *s)
     77 {
     78 	cstring cs;
     79 	cs.len = strlen(s);
     80 	cs.str = cstring_copy(s);
     81 	cstring_resize(&cs, cs.len << 1);
     82 	return (cs);
     83 }
     84 
     85 void
     86 cstring_delete(cstring *cs)
     87 {
     88 	csfree(cs);
     89 	cs->str = NULL;
     90 	cs->len = 0;
     91 	cs->capacity = 0;
     92 }
     93 
     94 void
     95 cstring_assign(cstring *cs, const char *s)
     96 {
     97 	csfree(cs);
     98 	cs->str = cstring_copy(s);
     99 	cs->len = strlen(s);
    100 	if (CSTRING_EXCEEDS_CAPACITY(cs->len, cs->capacity))
    101 		cstring_resize(cs, cs->len << 1);
    102 }
    103 
    104 void
    105 cstring_insert(cstring *cs, const char *s, size_t i)
    106 {
    107 	size_t slen, newlen;
    108 	char *tmp;
    109 
    110 	if (!CSTRING_OUT_OF_BOUNDS(cs->len, i) && s != NULL) {
    111 		slen = strlen(s);
    112 		newlen = cs->len + slen;
    113 		tmp = emalloc(newlen + 1);
    114 		memcpy(tmp, cs->str, i);
    115 		memcpy(tmp + i, s, slen);
    116 		memcpy(tmp + i + slen, cs->str + i, newlen - slen - i + 1);
    117 		csfree(cs);
    118 		cs->len = newlen;
    119 		cs->str = tmp;
    120 		cs->str[cs->len] = '\0';
    121 		if (CSTRING_EXCEEDS_CAPACITY(newlen, cs->capacity))
    122 			cstring_resize(cs, newlen << 1);
    123 	}
    124 }
    125 
    126 void
    127 cstring_erase(cstring *cs, size_t pos, size_t len)
    128 {
    129 	size_t newlen;
    130 	char *tmp;
    131 
    132 	if (!cstring_empty(cs)
    133 	&& (!CSTRING_OUT_OF_BOUNDS(cs->len, pos)
    134 	||  !CSTRING_OUT_OF_BOUNDS(cs->len, len))) {
    135 		newlen = cs->len - len;
    136 		tmp = emalloc(newlen + 1);
    137 		memcpy(tmp, cs->str, pos);
    138 		memcpy(tmp + pos, cs->str + pos + len, cs->len - pos - len);
    139 		csfree(cs); /* Useless check but keep it for consistency */
    140 		cs->len = newlen;
    141 		cs->str = tmp;
    142 		cs->str[cs->len] = '\0';
    143 	}
    144 }
    145 
    146 void
    147 cstring_erase_matching(cstring *cs, const char *s)
    148 {
    149 	if (s != NULL)
    150 		cstring_erase(cs, cstring_find(cs, s), strlen(s));
    151 }
    152 
    153 void
    154 cstring_erase_all_matching(cstring *cs, const char *s)
    155 {
    156 	size_t len, i;
    157 
    158 	if (s != NULL) {
    159 		len = strlen(s);
    160 		i = cstring_find(cs, s);
    161 		for (; i != CSTRING_NPOS; i = cstring_find(cs, s))
    162 			cstring_erase(cs, i, len);
    163 	}
    164 }
    165 
    166 void
    167 cstring_trim(cstring *cs, const char *s)
    168 {
    169 	size_t i;
    170 	
    171 	i = cstring_find_first_of(cs, s);
    172 	for (; i != CSTRING_NPOS; i = cstring_find_first_of(cs, s))
    173 		cstring_erase(cs, i, 1);
    174 }
    175 
    176 void
    177 cstring_push_back(cstring *cs, char c)
    178 {
    179 	if (CSTRING_EXCEEDS_CAPACITY(cs->len, cs->capacity))
    180 		cstring_resize(cs, cs->len << 1);
    181 	cs->str[cs->len] = c;
    182 	cs->str[++cs->len] = '\0';
    183 }
    184 
    185 void
    186 cstring_pop_back(cstring *cs)
    187 {
    188 	if (cs->len > 0)
    189 		cs->str[--cs->len] = '\0';
    190 }
    191 
    192 void
    193 cstring_replace_char(cstring *cs, size_t i, char c)
    194 {
    195 	if (!CSTRING_OUT_OF_BOUNDS(cs->len, i))
    196 		cs->str[i] = c;
    197 }
    198 
    199 void
    200 cstring_replace_str(cstring *cs, const char *s, size_t pos, size_t olen)
    201 {
    202 	if (!CSTRING_OUT_OF_BOUNDS(cs->len, pos)
    203 	&&  !CSTRING_OUT_OF_BOUNDS(cs->len, olen)) {
    204 		cstring_erase(cs, pos, olen);
    205 		cstring_insert(cs, s, pos);
    206 	}
    207 }
    208 
    209 cstring
    210 cstring_substr(const cstring *cs, size_t pos, size_t len)
    211 {
    212 	if (CSTRING_OUT_OF_BOUNDS(cs->len, pos)
    213 	||  CSTRING_OUT_OF_BOUNDS(cs->len, len))
    214 		return (cstring_create(""));
    215 	cstring substr = cstring_create(&cs->str[pos]);
    216 	substr.len = len;
    217 	substr.str[len] = '\0';
    218 	cstring_shrink_to_fit(&substr);
    219 	return (substr);
    220 }
    221 
    222 void
    223 cstring_swap(cstring *lhs, cstring *rhs)
    224 {
    225 	cstring tmp = *lhs;
    226 	*lhs = *rhs;
    227 	*rhs = tmp;
    228 }
    229 
    230 void
    231 cstring_sort_partial(cstring *cs, size_t pos, size_t len, int flags,
    232 		     cstring_sort_callback cb)
    233 {
    234 	if (flags & CSTRING_SORT_REST ||  CSTRING_OUT_OF_BOUNDS(len, pos + len))
    235 		len -= pos;
    236 
    237 	if (flags & CSTRING_SORT_ASCENDING)
    238 		qsort(cs + pos, len, sizeof(cstring), cstring_cmp_greater);
    239 	else if (flags & CSTRING_SORT_DESCENDING)
    240 		qsort(cs + pos, len, sizeof(cstring), cstring_cmp_less);
    241 	else if (flags & CSTRING_SORT_CALLBACK)
    242 		qsort(cs + pos, len, sizeof(cstring), cb);
    243 }
    244 
    245 void
    246 cstring_sort_chars_partial(cstring *cs, size_t pos, size_t len, int flags,
    247 			   cstring_sort_callback cb)
    248 {
    249 	if (flags & CSTRING_SORT_REST ||  CSTRING_OUT_OF_BOUNDS(cs->len, pos + len))
    250 		len = cs->len - pos;
    251 
    252 	if (flags & CSTRING_SORT_ASCENDING)
    253 		qsort(cs->str + pos, len, sizeof(char), cstring_cmp_char_greater);
    254 	else if (flags & CSTRING_SORT_DESCENDING)
    255 		qsort(cs->str + pos, len, sizeof(char), cstring_cmp_char_less);
    256 	else if (flags & CSTRING_SORT_CALLBACK)
    257 		qsort(cs->str + pos, len, sizeof(char), cb);
    258 }
    259 
    260 void
    261 cstring_clear(cstring *cs)
    262 {
    263 	csfree(cs);
    264 	cs->str = emalloc(1);
    265 	cs->str[0] = '\0';
    266 	cs->len = 0;
    267 	cs->capacity = 0;
    268 }
    269 
    270 #define CSTRING_CHECK(cs, s)		\
    271 	if (cstring_empty(cs) || !*(s)) \
    272 		return (CSTRING_NPOS)
    273 
    274 size_t
    275 cstring_find(const cstring *cs, const char *s)
    276 {
    277 	CSTRING_CHECK(cs, s);
    278 	FIND_OCC(cs, s, strstr);
    279 	return (CSTRING_NPOS);
    280 }
    281 
    282 /*SIMPLIFY */
    283 size_t
    284 cstring_rfind(const cstring *cs, const char *s)
    285 {
    286 	size_t idx, slen, i, j;
    287 	int found;
    288 
    289 	CSTRING_CHECK(cs, s);
    290 	idx = -1;
    291 	slen = strlen(s);
    292 	for (i = 0; i <= cs->len - slen; i++) {
    293 		found = 1;
    294 		for (j = 0; j < slen; j++) {
    295 			if (cs->str[i + j] != s[j]) {
    296 				found = 0;
    297 				break;
    298 			}
    299 		}
    300 		if (found)
    301 			idx = i;
    302 	}
    303 	return (idx < 0 ? CSTRING_NPOS : idx);
    304 }
    305 
    306 size_t
    307 cstring_find_first_of(const cstring *cs, const char *s)
    308 {
    309 	CSTRING_CHECK(cs, s);
    310 	for (; *s; s++) {
    311 		FIND_OCC(cs, *s, strchr);
    312 	}
    313 	return (CSTRING_NPOS);
    314 }
    315 
    316 size_t
    317 cstring_find_first_not_of(const cstring *cs, const char *s)
    318 {
    319 	size_t i = 0;
    320 
    321 	CSTRING_CHECK(cs, s);
    322 	for (; i < cs->len; i++)
    323 		if (!cstring_is_one_of(cs->str[i], s))
    324 			return (i);
    325 	return (CSTRING_NPOS);
    326 }
    327 
    328 size_t
    329 cstring_find_last_of(const cstring *cs, const char *s)
    330 {
    331 	size_t i;
    332 
    333 	CSTRING_CHECK(cs, s);
    334 	i = *(s + strlen(s));
    335 	for (; i >= 0; i--) {
    336 		FIND_OCC(cs, s[i], strrchr);
    337 	}
    338 	return (CSTRING_NPOS);
    339 }
    340 
    341 size_t
    342 cstring_find_last_not_of(const cstring *cs, const char *s)
    343 {
    344 	size_t i = cs->len;
    345 
    346 	CSTRING_CHECK(cs, s);
    347 	for (; i >= 0; i--)
    348 		if (!cstring_is_one_of(cs->str[i], s))
    349 			return (i);
    350 	return (CSTRING_NPOS);
    351 }
    352 
    353 #undef CSTR_CHECK
    354 
    355 int
    356 cstring_ends_with_str(const cstring *cs, const char *s)
    357 {
    358 	/* TODO: avoid cstring_substr */
    359 	cstring sub;
    360 	size_t slen;
    361 	int found;
    362 
    363 	slen = strlen(s);
    364 	sub = cstring_substr(cs, cs->len - slen, slen);
    365 	found = !strcmp(sub.str, s);
    366 	cstring_delete(&sub);
    367 	return (found);
    368 }
    369 
    370 char *
    371 cstring_copy(const char *s)
    372 {
    373 	size_t len;
    374 	char *tmp;
    375 
    376 	len = strlen(s);
    377 	tmp = emalloc(len + 1);
    378 	memcpy(tmp, s, len + 1);
    379 	tmp[len] = '\0'; /* Add \0 in case `s` didn't have it */
    380 	return (tmp);
    381 }
    382 
    383 void
    384 cstring_resize(cstring *cs, size_t newcapacity)
    385 {
    386 	char *tmp;
    387 
    388 	tmp = emalloc(newcapacity + 1);
    389 	memcpy(tmp, cs->str, cs->len + 1);
    390 	csfree(cs);
    391 	cs->str = tmp;
    392 	cs->str[cs->len] = '\0';
    393 	cs->capacity = newcapacity;
    394 }
    395 
    396 cstring *
    397 cstring_getline(FILE *fd, cstring *cs, char delim)
    398 {
    399 	char c;
    400 
    401 	cstring_clear(cs);
    402 	while ((c = fgetc(fd)) != EOF && c != '\n') {
    403 		if (c == delim)
    404 			break;
    405 		else
    406 			cstring_push_back(cs, c);
    407 	}
    408 	return (c == EOF ? NULL : cs);
    409 }