src/string.c

Tue, 07 Feb 2023 20:08:45 +0100

author
Mike Becker <universe@uap-core.de>
date
Tue, 07 Feb 2023 20:08:45 +0100
changeset 650
77021e06b1a8
parent 645
ec50abb285ad
child 657
3eeadf666d6b
permissions
-rw-r--r--

fix code not compiling under windows+mingw

universe@576 1 /*
universe@576 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
universe@576 3 *
universe@576 4 * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
universe@576 5 *
universe@576 6 * Redistribution and use in source and binary forms, with or without
universe@576 7 * modification, are permitted provided that the following conditions are met:
universe@576 8 *
universe@576 9 * 1. Redistributions of source code must retain the above copyright
universe@576 10 * notice, this list of conditions and the following disclaimer.
universe@576 11 *
universe@576 12 * 2. Redistributions in binary form must reproduce the above copyright
universe@576 13 * notice, this list of conditions and the following disclaimer in the
universe@576 14 * documentation and/or other materials provided with the distribution.
universe@576 15 *
universe@576 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
universe@576 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
universe@576 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
universe@576 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
universe@576 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
universe@576 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
universe@576 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
universe@576 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
universe@576 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
universe@576 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
universe@576 26 * POSSIBILITY OF SUCH DAMAGE.
universe@576 27 */
universe@576 28
universe@576 29 #include "cx/string.h"
universe@579 30 #include "cx/utils.h"
universe@579 31
universe@579 32 #include <string.h>
universe@579 33 #include <stdarg.h>
universe@581 34 #include <ctype.h>
universe@581 35
universe@581 36 #ifndef _WIN32
universe@581 37
universe@628 38 #include <strings.h> // for strncasecmp()
universe@581 39
universe@628 40 #endif // _WIN32
universe@579 41
universe@579 42 cxmutstr cx_mutstr(char *cstring) {
universe@579 43 return (cxmutstr) {cstring, strlen(cstring)};
universe@579 44 }
universe@579 45
universe@579 46 cxmutstr cx_mutstrn(
universe@579 47 char *cstring,
universe@579 48 size_t length
universe@579 49 ) {
universe@579 50 return (cxmutstr) {cstring, length};
universe@579 51 }
universe@579 52
universe@579 53 cxstring cx_str(const char *cstring) {
universe@579 54 return (cxstring) {cstring, strlen(cstring)};
universe@579 55 }
universe@579 56
universe@579 57 cxstring cx_strn(
universe@579 58 const char *cstring,
universe@579 59 size_t length
universe@579 60 ) {
universe@579 61 return (cxstring) {cstring, length};
universe@579 62 }
universe@579 63
universe@579 64 cxstring cx_strcast(cxmutstr str) {
universe@579 65 return (cxstring) {str.ptr, str.length};
universe@579 66 }
universe@579 67
universe@579 68 void cx_strfree(cxmutstr *str) {
universe@579 69 free(str->ptr);
universe@579 70 str->ptr = NULL;
universe@579 71 str->length = 0;
universe@579 72 }
universe@579 73
universe@583 74 void cx_strfree_a(
universe@583 75 CxAllocator *alloc,
universe@583 76 cxmutstr *str
universe@583 77 ) {
universe@583 78 cxFree(alloc, str->ptr);
universe@583 79 str->ptr = NULL;
universe@583 80 str->length = 0;
universe@583 81 }
universe@583 82
universe@579 83 size_t cx_strlen(
universe@579 84 size_t count,
universe@579 85 ...
universe@579 86 ) {
universe@579 87 if (count == 0) return 0;
universe@579 88
universe@579 89 va_list ap;
universe@579 90 va_start(ap, count);
universe@579 91 size_t size = 0;
universe@579 92 cx_for_n(i, count) {
universe@579 93 cxstring str = va_arg(ap, cxstring);
universe@579 94 size += str.length;
universe@579 95 }
universe@579 96 va_end(ap);
universe@579 97
universe@579 98 return size;
universe@579 99 }
universe@579 100
universe@579 101 cxmutstr cx_strcat_a(
universe@579 102 CxAllocator *alloc,
universe@579 103 size_t count,
universe@579 104 ...
universe@579 105 ) {
universe@579 106 cxstring *strings = calloc(count, sizeof(cxstring));
universe@579 107 if (!strings) abort();
universe@579 108
universe@579 109 va_list ap;
universe@579 110 va_start(ap, count);
universe@579 111
universe@579 112 // get all args and overall length
universe@579 113 size_t slen = 0;
universe@579 114 cx_for_n(i, count) {
universe@579 115 cxstring s = va_arg (ap, cxstring);
universe@579 116 strings[i] = s;
universe@579 117 slen += s.length;
universe@579 118 }
universe@579 119
universe@579 120 // create new string
universe@579 121 cxmutstr result;
universe@579 122 result.ptr = cxMalloc(alloc, slen + 1);
universe@579 123 result.length = slen;
universe@579 124 if (result.ptr == NULL) abort();
universe@579 125
universe@579 126 // concatenate strings
universe@579 127 size_t pos = 0;
universe@579 128 cx_for_n(i, count) {
universe@579 129 cxstring s = strings[i];
universe@579 130 memcpy(result.ptr + pos, s.ptr, s.length);
universe@579 131 pos += s.length;
universe@579 132 }
universe@579 133
universe@579 134 // terminate string
universe@579 135 result.ptr[result.length] = '\0';
universe@579 136
universe@579 137 // free temporary array
universe@579 138 free(strings);
universe@579 139
universe@579 140 return result;
universe@579 141 }
universe@579 142
universe@580 143 cxstring cx_strsubs(
universe@580 144 cxstring string,
universe@580 145 size_t start
universe@580 146 ) {
universe@580 147 return cx_strsubsl(string, start, string.length - start);
universe@580 148 }
universe@579 149
universe@580 150 cxmutstr cx_strsubs_m(
universe@580 151 cxmutstr string,
universe@580 152 size_t start
universe@580 153 ) {
universe@580 154 return cx_strsubsl_m(string, start, string.length - start);
universe@580 155 }
universe@579 156
universe@580 157 cxstring cx_strsubsl(
universe@580 158 cxstring string,
universe@580 159 size_t start,
universe@580 160 size_t length
universe@580 161 ) {
universe@580 162 if (start > string.length) {
universe@580 163 return (cxstring) {NULL, 0};
universe@580 164 }
universe@580 165
universe@580 166 size_t rem_len = string.length - start;
universe@580 167 if (length > rem_len) {
universe@580 168 length = rem_len;
universe@580 169 }
universe@580 170
universe@580 171 return (cxstring) {string.ptr + start, length};
universe@580 172 }
universe@580 173
universe@580 174 cxmutstr cx_strsubsl_m(
universe@580 175 cxmutstr string,
universe@580 176 size_t start,
universe@580 177 size_t length
universe@580 178 ) {
universe@580 179 cxstring result = cx_strsubsl(cx_strcast(string), start, length);
universe@580 180 return (cxmutstr) {(char *) result.ptr, result.length};
universe@580 181 }
universe@580 182
universe@580 183 cxstring cx_strchr(
universe@580 184 cxstring string,
universe@580 185 int chr
universe@580 186 ) {
universe@580 187 chr = 0xFF & chr;
universe@580 188 // TODO: improve by comparing multiple bytes at once
universe@580 189 cx_for_n(i, string.length) {
universe@580 190 if (string.ptr[i] == chr) {
universe@580 191 return cx_strsubs(string, i);
universe@580 192 }
universe@580 193 }
universe@580 194 return (cxstring) {NULL, 0};
universe@580 195 }
universe@580 196
universe@580 197 cxmutstr cx_strchr_m(
universe@580 198 cxmutstr string,
universe@580 199 int chr
universe@580 200 ) {
universe@580 201 cxstring result = cx_strchr(cx_strcast(string), chr);
universe@580 202 return (cxmutstr) {(char *) result.ptr, result.length};
universe@580 203 }
universe@580 204
universe@580 205 cxstring cx_strrchr(
universe@580 206 cxstring string,
universe@580 207 int chr
universe@580 208 ) {
universe@580 209 chr = 0xFF & chr;
universe@580 210 size_t i = string.length;
universe@580 211 while (i > 0) {
universe@580 212 i--;
universe@580 213 // TODO: improve by comparing multiple bytes at once
universe@580 214 if (string.ptr[i] == chr) {
universe@580 215 return cx_strsubs(string, i);
universe@580 216 }
universe@580 217 }
universe@580 218 return (cxstring) {NULL, 0};
universe@580 219 }
universe@580 220
universe@580 221 cxmutstr cx_strrchr_m(
universe@580 222 cxmutstr string,
universe@580 223 int chr
universe@580 224 ) {
universe@580 225 cxstring result = cx_strrchr(cx_strcast(string), chr);
universe@580 226 return (cxmutstr) {(char *) result.ptr, result.length};
universe@580 227 }
universe@580 228
universe@643 229 #ifndef CX_STRSTR_SBO_SIZE
universe@643 230 #define CX_STRSTR_SBO_SIZE 512
universe@643 231 #endif
universe@580 232
universe@580 233 cxstring cx_strstr(
universe@580 234 cxstring haystack,
universe@580 235 cxstring needle
universe@580 236 ) {
universe@580 237 if (needle.length == 0) {
universe@580 238 return haystack;
universe@580 239 }
universe@580 240
universe@628 241 // optimize for single-char needles
universe@583 242 if (needle.length == 1) {
universe@583 243 return cx_strchr(haystack, *needle.ptr);
universe@583 244 }
universe@583 245
universe@580 246 /*
universe@580 247 * IMPORTANT:
universe@580 248 * Our prefix table contains the prefix length PLUS ONE
universe@580 249 * this is our decision, because we want to use the full range of size_t.
universe@580 250 * The original algorithm needs a (-1) at one single place,
universe@580 251 * and we want to avoid that.
universe@580 252 */
universe@580 253
universe@628 254 // local prefix table
universe@643 255 size_t s_prefix_table[CX_STRSTR_SBO_SIZE];
universe@580 256
universe@628 257 // check needle length and use appropriate prefix table
universe@628 258 // if the pattern exceeds static prefix table, allocate on the heap
universe@643 259 bool useheap = needle.length >= CX_STRSTR_SBO_SIZE;
universe@591 260 register size_t *ptable = useheap ? calloc(needle.length + 1,
universe@591 261 sizeof(size_t)) : s_prefix_table;
universe@580 262
universe@628 263 // keep counter in registers
universe@580 264 register size_t i, j;
universe@580 265
universe@628 266 // fill prefix table
universe@580 267 i = 0;
universe@580 268 j = 0;
universe@591 269 ptable[i] = j;
universe@580 270 while (i < needle.length) {
universe@580 271 while (j >= 1 && needle.ptr[j - 1] != needle.ptr[i]) {
universe@591 272 j = ptable[j - 1];
universe@580 273 }
universe@580 274 i++;
universe@580 275 j++;
universe@591 276 ptable[i] = j;
universe@580 277 }
universe@580 278
universe@628 279 // search
universe@580 280 cxstring result = {NULL, 0};
universe@580 281 i = 0;
universe@580 282 j = 1;
universe@580 283 while (i < haystack.length) {
universe@580 284 while (j >= 1 && haystack.ptr[i] != needle.ptr[j - 1]) {
universe@591 285 j = ptable[j - 1];
universe@580 286 }
universe@580 287 i++;
universe@580 288 j++;
universe@580 289 if (j - 1 == needle.length) {
universe@580 290 size_t start = i - needle.length;
universe@580 291 result.ptr = haystack.ptr + start;
universe@580 292 result.length = haystack.length - start;
universe@580 293 break;
universe@580 294 }
universe@580 295 }
universe@580 296
universe@628 297 // if prefix table was allocated on the heap, free it
universe@580 298 if (ptable != s_prefix_table) {
universe@580 299 free(ptable);
universe@580 300 }
universe@580 301
universe@580 302 return result;
universe@580 303 }
universe@580 304
universe@580 305 cxmutstr cx_strstr_m(
universe@580 306 cxmutstr haystack,
universe@580 307 cxstring needle
universe@580 308 ) {
universe@580 309 cxstring result = cx_strstr(cx_strcast(haystack), needle);
universe@580 310 return (cxmutstr) {(char *) result.ptr, result.length};
universe@580 311 }
universe@580 312
universe@580 313 size_t cx_strsplit(
universe@580 314 cxstring string,
universe@580 315 cxstring delim,
universe@580 316 size_t limit,
universe@580 317 cxstring *output
universe@580 318 ) {
universe@628 319 // special case: output limit is zero
universe@583 320 if (limit == 0) return 0;
universe@583 321
universe@628 322 // special case: delimiter is empty
universe@583 323 if (delim.length == 0) {
universe@583 324 output[0] = string;
universe@583 325 return 1;
universe@583 326 }
universe@583 327
universe@628 328 // special cases: delimiter is at least as large as the string
universe@583 329 if (delim.length >= string.length) {
universe@628 330 // exact match
universe@583 331 if (cx_strcmp(string, delim) == 0) {
universe@583 332 output[0] = cx_strn(string.ptr, 0);
universe@583 333 output[1] = cx_strn(string.ptr + string.length, 0);
universe@583 334 return 2;
universe@628 335 } else {
universe@628 336 // no match possible
universe@583 337 output[0] = string;
universe@583 338 return 1;
universe@583 339 }
universe@583 340 }
universe@583 341
universe@583 342 size_t n = 0;
universe@583 343 cxstring curpos = string;
universe@583 344 while (1) {
universe@583 345 ++n;
universe@583 346 cxstring match = cx_strstr(curpos, delim);
universe@583 347 if (match.length > 0) {
universe@628 348 // is the limit reached?
universe@583 349 if (n < limit) {
universe@628 350 // copy the current string to the array
universe@583 351 cxstring item = cx_strn(curpos.ptr, match.ptr - curpos.ptr);
universe@583 352 output[n - 1] = item;
universe@583 353 size_t processed = item.length + delim.length;
universe@583 354 curpos.ptr += processed;
universe@583 355 curpos.length -= processed;
universe@583 356 } else {
universe@628 357 // limit reached, copy the _full_ remaining string
universe@583 358 output[n - 1] = curpos;
universe@583 359 break;
universe@583 360 }
universe@583 361 } else {
universe@628 362 // no more matches, copy last string
universe@583 363 output[n - 1] = curpos;
universe@583 364 break;
universe@583 365 }
universe@583 366 }
universe@583 367
universe@583 368 return n;
universe@580 369 }
universe@580 370
universe@580 371 size_t cx_strsplit_a(
universe@580 372 CxAllocator *allocator,
universe@580 373 cxstring string,
universe@580 374 cxstring delim,
universe@580 375 size_t limit,
universe@580 376 cxstring **output
universe@580 377 ) {
universe@628 378 // find out how many splits we're going to make and allocate memory
universe@583 379 size_t n = 0;
universe@583 380 cxstring curpos = string;
universe@583 381 while (1) {
universe@583 382 ++n;
universe@583 383 cxstring match = cx_strstr(curpos, delim);
universe@583 384 if (match.length > 0) {
universe@628 385 // is the limit reached?
universe@583 386 if (n < limit) {
universe@583 387 size_t processed = match.ptr - curpos.ptr + delim.length;
universe@583 388 curpos.ptr += processed;
universe@583 389 curpos.length -= processed;
universe@583 390 } else {
universe@628 391 // limit reached
universe@583 392 break;
universe@583 393 }
universe@583 394 } else {
universe@628 395 // no more matches
universe@583 396 break;
universe@583 397 }
universe@583 398 }
universe@583 399 *output = cxCalloc(allocator, n, sizeof(cxstring));
universe@583 400 return cx_strsplit(string, delim, n, *output);
universe@580 401 }
universe@580 402
universe@580 403 size_t cx_strsplit_m(
universe@580 404 cxmutstr string,
universe@580 405 cxstring delim,
universe@580 406 size_t limit,
universe@580 407 cxmutstr *output
universe@580 408 ) {
universe@580 409 return cx_strsplit(cx_strcast(string),
universe@580 410 delim, limit, (cxstring *) output);
universe@580 411 }
universe@580 412
universe@580 413 size_t cx_strsplit_ma(
universe@580 414 CxAllocator *allocator,
universe@580 415 cxmutstr string,
universe@580 416 cxstring delim,
universe@580 417 size_t limit,
universe@580 418 cxmutstr **output
universe@580 419 ) {
universe@580 420 return cx_strsplit_a(allocator, cx_strcast(string),
universe@580 421 delim, limit, (cxstring **) output);
universe@580 422 }
universe@581 423
universe@583 424 int cx_strcmp(
universe@583 425 cxstring s1,
universe@583 426 cxstring s2
universe@583 427 ) {
universe@581 428 if (s1.length == s2.length) {
universe@581 429 return memcmp(s1.ptr, s2.ptr, s1.length);
universe@581 430 } else if (s1.length > s2.length) {
universe@581 431 return 1;
universe@581 432 } else {
universe@581 433 return -1;
universe@581 434 }
universe@581 435 }
universe@581 436
universe@583 437 int cx_strcasecmp(
universe@583 438 cxstring s1,
universe@583 439 cxstring s2
universe@583 440 ) {
universe@581 441 if (s1.length == s2.length) {
universe@581 442 #ifdef _WIN32
universe@581 443 return _strnicmp(s1.ptr, s2.ptr, s1.length);
universe@581 444 #else
universe@581 445 return strncasecmp(s1.ptr, s2.ptr, s1.length);
universe@581 446 #endif
universe@581 447 } else if (s1.length > s2.length) {
universe@581 448 return 1;
universe@581 449 } else {
universe@581 450 return -1;
universe@581 451 }
universe@581 452 }
universe@581 453
universe@583 454 cxmutstr cx_strdup_a(
universe@583 455 CxAllocator *allocator,
universe@583 456 cxstring string
universe@583 457 ) {
universe@581 458 cxmutstr result = {
universe@581 459 cxMalloc(allocator, string.length + 1),
universe@581 460 string.length
universe@581 461 };
universe@581 462 if (result.ptr == NULL) {
universe@581 463 result.length = 0;
universe@581 464 return result;
universe@581 465 }
universe@581 466 memcpy(result.ptr, string.ptr, string.length);
universe@581 467 result.ptr[string.length] = '\0';
universe@581 468 return result;
universe@581 469 }
universe@581 470
universe@581 471 cxstring cx_strtrim(cxstring string) {
universe@581 472 cxstring result = string;
universe@581 473 // TODO: optimize by comparing multiple bytes at once
universe@581 474 while (result.length > 0 && isspace(*result.ptr)) {
universe@581 475 result.ptr++;
universe@581 476 result.length--;
universe@581 477 }
universe@581 478 while (result.length > 0 && isspace(result.ptr[result.length - 1])) {
universe@581 479 result.length--;
universe@581 480 }
universe@581 481 return result;
universe@581 482 }
universe@581 483
universe@581 484 cxmutstr cx_strtrim_m(cxmutstr string) {
universe@581 485 cxstring result = cx_strtrim(cx_strcast(string));
universe@581 486 return (cxmutstr) {(char *) result.ptr, result.length};
universe@581 487 }
universe@581 488
universe@583 489 bool cx_strprefix(
universe@583 490 cxstring string,
universe@583 491 cxstring prefix
universe@583 492 ) {
universe@581 493 if (string.length < prefix.length) return false;
universe@581 494 return memcmp(string.ptr, prefix.ptr, prefix.length) == 0;
universe@581 495 }
universe@581 496
universe@583 497 bool cx_strsuffix(
universe@583 498 cxstring string,
universe@583 499 cxstring suffix
universe@583 500 ) {
universe@581 501 if (string.length < suffix.length) return false;
universe@581 502 return memcmp(string.ptr + string.length - suffix.length,
universe@581 503 suffix.ptr, suffix.length) == 0;
universe@581 504 }
universe@581 505
universe@583 506 bool cx_strcaseprefix(
universe@583 507 cxstring string,
universe@583 508 cxstring prefix
universe@583 509 ) {
universe@581 510 if (string.length < prefix.length) return false;
universe@581 511 #ifdef _WIN32
universe@581 512 return _strnicmp(string.ptr, prefix.ptr, prefix.length) == 0;
universe@581 513 #else
universe@581 514 return strncasecmp(string.ptr, prefix.ptr, prefix.length) == 0;
universe@581 515 #endif
universe@581 516 }
universe@581 517
universe@583 518 bool cx_strcasesuffix(
universe@583 519 cxstring string,
universe@583 520 cxstring suffix
universe@583 521 ) {
universe@581 522 if (string.length < suffix.length) return false;
universe@581 523 #ifdef _WIN32
universe@581 524 return _strnicmp(string.ptr+string.length-suffix.length,
universe@581 525 suffix.ptr, suffix.length) == 0;
universe@581 526 #else
universe@581 527 return strncasecmp(string.ptr + string.length - suffix.length,
universe@581 528 suffix.ptr, suffix.length) == 0;
universe@581 529 #endif
universe@581 530 }
universe@582 531
universe@582 532 void cx_strlower(cxmutstr string) {
universe@582 533 cx_for_n(i, string.length) {
universe@593 534 string.ptr[i] = (char) tolower(string.ptr[i]);
universe@582 535 }
universe@582 536 }
universe@582 537
universe@582 538 void cx_strupper(cxmutstr string) {
universe@582 539 cx_for_n(i, string.length) {
universe@593 540 string.ptr[i] = (char) toupper(string.ptr[i]);
universe@582 541 }
universe@582 542 }
universe@583 543
universe@643 544 #ifndef CX_STRREPLACE_INDEX_BUFFER_SIZE
universe@643 545 #define CX_STRREPLACE_INDEX_BUFFER_SIZE 64
universe@643 546 #endif
universe@583 547
universe@583 548 struct cx_strreplace_ibuf {
universe@583 549 size_t *buf;
universe@583 550 struct cx_strreplace_ibuf *next;
universe@590 551 unsigned int len;
universe@583 552 };
universe@583 553
universe@583 554 static void cx_strrepl_free_ibuf(struct cx_strreplace_ibuf *buf) {
universe@583 555 while (buf) {
universe@583 556 struct cx_strreplace_ibuf *next = buf->next;
universe@583 557 free(buf->buf);
universe@583 558 free(buf);
universe@583 559 buf = next;
universe@583 560 }
universe@583 561 }
universe@583 562
universe@583 563 cxmutstr cx_strreplacen_a(
universe@583 564 CxAllocator *allocator,
universe@583 565 cxstring str,
universe@583 566 cxstring pattern,
universe@583 567 cxstring replacement,
universe@583 568 size_t replmax
universe@583 569 ) {
universe@583 570
universe@583 571 if (pattern.length == 0 || pattern.length > str.length || replmax == 0)
universe@583 572 return cx_strdup_a(allocator, str);
universe@583 573
universe@628 574 // Compute expected buffer length
universe@583 575 size_t ibufmax = str.length / pattern.length;
universe@583 576 size_t ibuflen = replmax < ibufmax ? replmax : ibufmax;
universe@643 577 if (ibuflen > CX_STRREPLACE_INDEX_BUFFER_SIZE) {
universe@643 578 ibuflen = CX_STRREPLACE_INDEX_BUFFER_SIZE;
universe@583 579 }
universe@583 580
universe@628 581 // Allocate first index buffer
universe@583 582 struct cx_strreplace_ibuf *firstbuf, *curbuf;
universe@583 583 firstbuf = curbuf = calloc(1, sizeof(struct cx_strreplace_ibuf));
universe@583 584 if (!firstbuf) return cx_mutstrn(NULL, 0);
universe@583 585 firstbuf->buf = calloc(ibuflen, sizeof(size_t));
universe@583 586 if (!firstbuf->buf) {
universe@583 587 free(firstbuf);
universe@583 588 return cx_mutstrn(NULL, 0);
universe@583 589 }
universe@583 590
universe@628 591 // Search occurrences
universe@583 592 cxstring searchstr = str;
universe@583 593 size_t found = 0;
universe@583 594 do {
universe@583 595 cxstring match = cx_strstr(searchstr, pattern);
universe@583 596 if (match.length > 0) {
universe@628 597 // Allocate next buffer in chain, if required
universe@583 598 if (curbuf->len == ibuflen) {
universe@583 599 struct cx_strreplace_ibuf *nextbuf =
universe@583 600 calloc(1, sizeof(struct cx_strreplace_ibuf));
universe@583 601 if (!nextbuf) {
universe@583 602 cx_strrepl_free_ibuf(firstbuf);
universe@583 603 return cx_mutstrn(NULL, 0);
universe@583 604 }
universe@583 605 nextbuf->buf = calloc(ibuflen, sizeof(size_t));
universe@583 606 if (!nextbuf->buf) {
universe@583 607 free(nextbuf);
universe@583 608 cx_strrepl_free_ibuf(firstbuf);
universe@583 609 return cx_mutstrn(NULL, 0);
universe@583 610 }
universe@583 611 curbuf->next = nextbuf;
universe@583 612 curbuf = nextbuf;
universe@583 613 }
universe@583 614
universe@628 615 // Record match index
universe@583 616 found++;
universe@583 617 size_t idx = match.ptr - str.ptr;
universe@583 618 curbuf->buf[curbuf->len++] = idx;
universe@583 619 searchstr.ptr = match.ptr + pattern.length;
universe@583 620 searchstr.length = str.length - idx - pattern.length;
universe@583 621 } else {
universe@583 622 break;
universe@583 623 }
universe@583 624 } while (searchstr.length > 0 && found < replmax);
universe@583 625
universe@628 626 // Allocate result string
universe@583 627 cxmutstr result;
universe@583 628 {
universe@583 629 ssize_t adjlen = (ssize_t) replacement.length - (ssize_t) pattern.length;
universe@583 630 size_t rcount = 0;
universe@583 631 curbuf = firstbuf;
universe@583 632 do {
universe@583 633 rcount += curbuf->len;
universe@583 634 curbuf = curbuf->next;
universe@583 635 } while (curbuf);
universe@583 636 result.length = str.length + rcount * adjlen;
universe@590 637 result.ptr = cxMalloc(allocator, result.length + 1);
universe@583 638 if (!result.ptr) {
universe@583 639 cx_strrepl_free_ibuf(firstbuf);
universe@583 640 return cx_mutstrn(NULL, 0);
universe@583 641 }
universe@583 642 }
universe@583 643
universe@628 644 // Build result string
universe@583 645 curbuf = firstbuf;
universe@583 646 size_t srcidx = 0;
universe@583 647 char *destptr = result.ptr;
universe@583 648 do {
universe@583 649 for (size_t i = 0; i < curbuf->len; i++) {
universe@628 650 // Copy source part up to next match
universe@583 651 size_t idx = curbuf->buf[i];
universe@583 652 size_t srclen = idx - srcidx;
universe@583 653 if (srclen > 0) {
universe@583 654 memcpy(destptr, str.ptr + srcidx, srclen);
universe@583 655 destptr += srclen;
universe@583 656 srcidx += srclen;
universe@583 657 }
universe@583 658
universe@628 659 // Copy the replacement and skip the source pattern
universe@583 660 srcidx += pattern.length;
universe@583 661 memcpy(destptr, replacement.ptr, replacement.length);
universe@583 662 destptr += replacement.length;
universe@583 663 }
universe@583 664 curbuf = curbuf->next;
universe@583 665 } while (curbuf);
universe@583 666 memcpy(destptr, str.ptr + srcidx, str.length - srcidx);
universe@583 667
universe@628 668 // Result is guaranteed to be zero-terminated
universe@590 669 result.ptr[result.length] = '\0';
universe@590 670
universe@628 671 // Free index buffer
universe@583 672 cx_strrepl_free_ibuf(firstbuf);
universe@583 673
universe@583 674 return result;
universe@583 675 }
universe@583 676
universe@645 677 CxStrtokCtx cx_strtok(
universe@645 678 cxstring str,
universe@645 679 cxstring delim,
universe@645 680 size_t limit
universe@645 681 ) {
universe@645 682 CxStrtokCtx ctx;
universe@645 683 ctx.str = str;
universe@645 684 ctx.delim = delim;
universe@645 685 ctx.limit = limit;
universe@645 686 ctx.pos = 0;
universe@645 687 ctx.next_pos = 0;
universe@645 688 ctx.delim_pos = 0;
universe@645 689 ctx.found = 0;
universe@645 690 ctx.delim_more = NULL;
universe@645 691 ctx.delim_more_count = 0;
universe@645 692 return ctx;
universe@645 693 }
universe@583 694
universe@645 695 CxStrtokCtx cx_strtok_m(
universe@645 696 cxmutstr str,
universe@645 697 cxstring delim,
universe@645 698 size_t limit
universe@645 699 ) {
universe@645 700 return cx_strtok(cx_strcast(str), delim, limit);
universe@645 701 }
universe@645 702
universe@645 703 bool cx_strtok_next(
universe@645 704 CxStrtokCtx *ctx,
universe@645 705 cxstring *token
universe@645 706 ) {
universe@645 707 // abortion criteria
universe@645 708 if (ctx->found >= ctx->limit || ctx->delim_pos >= ctx->str.length) {
universe@645 709 return false;
universe@645 710 }
universe@645 711
universe@645 712 // determine the search start
universe@645 713 cxstring haystack = cx_strsubs(ctx->str, ctx->next_pos);
universe@645 714
universe@645 715 // search the next delimiter
universe@645 716 cxstring delim = cx_strstr(haystack, ctx->delim);
universe@645 717
universe@645 718 // if found, make delim capture exactly the delimiter
universe@645 719 if (delim.length > 0) {
universe@645 720 delim.length = ctx->delim.length;
universe@645 721 }
universe@645 722
universe@645 723 // if more delimiters are specified, check them now
universe@645 724 if (ctx->delim_more_count > 0) {
universe@645 725 cx_for_n(i, ctx->delim_more_count) {
universe@645 726 cxstring d = cx_strstr(haystack, ctx->delim_more[i]);
universe@645 727 if (d.length > 0 && (delim.length == 0 || d.ptr < delim.ptr)) {
universe@645 728 delim.ptr = d.ptr;
universe@645 729 delim.length = ctx->delim_more[i].length;
universe@645 730 }
universe@645 731 }
universe@645 732 }
universe@645 733
universe@645 734 // store the token information and adjust the context
universe@645 735 ctx->found++;
universe@645 736 ctx->pos = ctx->next_pos;
universe@645 737 token->ptr = &ctx->str.ptr[ctx->pos];
universe@645 738 ctx->delim_pos = delim.length == 0 ?
universe@645 739 ctx->str.length : (size_t) (delim.ptr - ctx->str.ptr);
universe@645 740 token->length = ctx->delim_pos - ctx->pos;
universe@645 741 ctx->next_pos = ctx->delim_pos + delim.length;
universe@645 742
universe@645 743 return true;
universe@645 744 }
universe@645 745
universe@645 746 bool cx_strtok_next_m(
universe@645 747 CxStrtokCtx *ctx,
universe@645 748 cxmutstr *token
universe@645 749 ) {
universe@645 750 return cx_strtok_next(ctx, (cxstring *) token);
universe@645 751 }
universe@645 752
universe@645 753 void cx_strtok_delim(
universe@645 754 CxStrtokCtx *ctx,
universe@645 755 cxstring const *delim,
universe@645 756 size_t count
universe@645 757 ) {
universe@645 758 ctx->delim_more = delim;
universe@645 759 ctx->delim_more_count = count;
universe@645 760 }

mercurial