cstring

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

cstring.c (8049B)


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