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 }