src/string.c

Tue, 04 Oct 2022 18:55:20 +0200

author
Mike Becker <universe@uap-core.de>
date
Tue, 04 Oct 2022 18:55:20 +0200
changeset 590
02a56701a5cb
parent 583
0f3c9662f9b5
child 591
7df0bcaecffa
permissions
-rw-r--r--

fix missing zero-termination in strreplace

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@579 34 #include <stdint.h>
universe@581 35 #include <ctype.h>
universe@581 36
universe@581 37 #ifndef _WIN32
universe@581 38
universe@581 39 #include <strings.h> /* for strncasecmp() */
universe@581 40
universe@581 41 #endif /* _WIN32 */
universe@579 42
universe@579 43 cxmutstr cx_mutstr(char *cstring) {
universe@579 44 return (cxmutstr) {cstring, strlen(cstring)};
universe@579 45 }
universe@579 46
universe@579 47 cxmutstr cx_mutstrn(
universe@579 48 char *cstring,
universe@579 49 size_t length
universe@579 50 ) {
universe@579 51 return (cxmutstr) {cstring, length};
universe@579 52 }
universe@579 53
universe@579 54 cxstring cx_str(const char *cstring) {
universe@579 55 return (cxstring) {cstring, strlen(cstring)};
universe@579 56 }
universe@579 57
universe@579 58 cxstring cx_strn(
universe@579 59 const char *cstring,
universe@579 60 size_t length
universe@579 61 ) {
universe@579 62 return (cxstring) {cstring, length};
universe@579 63 }
universe@579 64
universe@579 65 cxstring cx_strcast(cxmutstr str) {
universe@579 66 return (cxstring) {str.ptr, str.length};
universe@579 67 }
universe@579 68
universe@579 69 void cx_strfree(cxmutstr *str) {
universe@579 70 free(str->ptr);
universe@579 71 str->ptr = NULL;
universe@579 72 str->length = 0;
universe@579 73 }
universe@579 74
universe@583 75 void cx_strfree_a(
universe@583 76 CxAllocator *alloc,
universe@583 77 cxmutstr *str
universe@583 78 ) {
universe@583 79 cxFree(alloc, str->ptr);
universe@583 80 str->ptr = NULL;
universe@583 81 str->length = 0;
universe@583 82 }
universe@583 83
universe@579 84 size_t cx_strlen(
universe@579 85 size_t count,
universe@579 86 ...
universe@579 87 ) {
universe@579 88 if (count == 0) return 0;
universe@579 89
universe@579 90 va_list ap;
universe@579 91 va_start(ap, count);
universe@579 92 size_t size = 0;
universe@579 93 cx_for_n(i, count) {
universe@579 94 cxstring str = va_arg(ap, cxstring);
universe@579 95 size += str.length;
universe@579 96 }
universe@579 97 va_end(ap);
universe@579 98
universe@579 99 return size;
universe@579 100 }
universe@579 101
universe@579 102 cxmutstr cx_strcat_a(
universe@579 103 CxAllocator *alloc,
universe@579 104 size_t count,
universe@579 105 ...
universe@579 106 ) {
universe@579 107 cxstring *strings = calloc(count, sizeof(cxstring));
universe@579 108 if (!strings) abort();
universe@579 109
universe@579 110 va_list ap;
universe@579 111 va_start(ap, count);
universe@579 112
universe@579 113 // get all args and overall length
universe@579 114 size_t slen = 0;
universe@579 115 cx_for_n(i, count) {
universe@579 116 cxstring s = va_arg (ap, cxstring);
universe@579 117 strings[i] = s;
universe@579 118 slen += s.length;
universe@579 119 }
universe@579 120
universe@579 121 // create new string
universe@579 122 cxmutstr result;
universe@579 123 result.ptr = cxMalloc(alloc, slen + 1);
universe@579 124 result.length = slen;
universe@579 125 if (result.ptr == NULL) abort();
universe@579 126
universe@579 127 // concatenate strings
universe@579 128 size_t pos = 0;
universe@579 129 cx_for_n(i, count) {
universe@579 130 cxstring s = strings[i];
universe@579 131 memcpy(result.ptr + pos, s.ptr, s.length);
universe@579 132 pos += s.length;
universe@579 133 }
universe@579 134
universe@579 135 // terminate string
universe@579 136 result.ptr[result.length] = '\0';
universe@579 137
universe@579 138 // free temporary array
universe@579 139 free(strings);
universe@579 140
universe@579 141 return result;
universe@579 142 }
universe@579 143
universe@580 144 cxstring cx_strsubs(
universe@580 145 cxstring string,
universe@580 146 size_t start
universe@580 147 ) {
universe@580 148 return cx_strsubsl(string, start, string.length - start);
universe@580 149 }
universe@579 150
universe@580 151 cxmutstr cx_strsubs_m(
universe@580 152 cxmutstr string,
universe@580 153 size_t start
universe@580 154 ) {
universe@580 155 return cx_strsubsl_m(string, start, string.length - start);
universe@580 156 }
universe@579 157
universe@580 158 cxstring cx_strsubsl(
universe@580 159 cxstring string,
universe@580 160 size_t start,
universe@580 161 size_t length
universe@580 162 ) {
universe@580 163 if (start > string.length) {
universe@580 164 return (cxstring) {NULL, 0};
universe@580 165 }
universe@580 166
universe@580 167 size_t rem_len = string.length - start;
universe@580 168 if (length > rem_len) {
universe@580 169 length = rem_len;
universe@580 170 }
universe@580 171
universe@580 172 return (cxstring) {string.ptr + start, length};
universe@580 173 }
universe@580 174
universe@580 175 cxmutstr cx_strsubsl_m(
universe@580 176 cxmutstr string,
universe@580 177 size_t start,
universe@580 178 size_t length
universe@580 179 ) {
universe@580 180 cxstring result = cx_strsubsl(cx_strcast(string), start, length);
universe@580 181 return (cxmutstr) {(char *) result.ptr, result.length};
universe@580 182 }
universe@580 183
universe@580 184 cxstring cx_strchr(
universe@580 185 cxstring string,
universe@580 186 int chr
universe@580 187 ) {
universe@580 188 chr = 0xFF & chr;
universe@580 189 // TODO: improve by comparing multiple bytes at once
universe@580 190 cx_for_n(i, string.length) {
universe@580 191 if (string.ptr[i] == chr) {
universe@580 192 return cx_strsubs(string, i);
universe@580 193 }
universe@580 194 }
universe@580 195 return (cxstring) {NULL, 0};
universe@580 196 }
universe@580 197
universe@580 198 cxmutstr cx_strchr_m(
universe@580 199 cxmutstr string,
universe@580 200 int chr
universe@580 201 ) {
universe@580 202 cxstring result = cx_strchr(cx_strcast(string), chr);
universe@580 203 return (cxmutstr) {(char *) result.ptr, result.length};
universe@580 204 }
universe@580 205
universe@580 206 cxstring cx_strrchr(
universe@580 207 cxstring string,
universe@580 208 int chr
universe@580 209 ) {
universe@580 210 chr = 0xFF & chr;
universe@580 211 size_t i = string.length;
universe@580 212 while (i > 0) {
universe@580 213 i--;
universe@580 214 // TODO: improve by comparing multiple bytes at once
universe@580 215 if (string.ptr[i] == chr) {
universe@580 216 return cx_strsubs(string, i);
universe@580 217 }
universe@580 218 }
universe@580 219 return (cxstring) {NULL, 0};
universe@580 220 }
universe@580 221
universe@580 222 cxmutstr cx_strrchr_m(
universe@580 223 cxmutstr string,
universe@580 224 int chr
universe@580 225 ) {
universe@580 226 cxstring result = cx_strrchr(cx_strcast(string), chr);
universe@580 227 return (cxmutstr) {(char *) result.ptr, result.length};
universe@580 228 }
universe@580 229
universe@580 230 #define ptable_r(dest, useheap, ptable, index) (dest = useheap ? \
universe@580 231 ((size_t*)ptable)[index] : (size_t) ((uint8_t*)ptable)[index])
universe@580 232
universe@580 233 #define ptable_w(useheap, ptable, index, src) do {\
universe@580 234 if (!useheap) ((uint8_t*)ptable)[index] = (uint8_t) src;\
universe@580 235 else ((size_t*)ptable)[index] = src;\
universe@580 236 } while (0)
universe@580 237
universe@580 238
universe@580 239 cxstring cx_strstr(
universe@580 240 cxstring haystack,
universe@580 241 cxstring needle
universe@580 242 ) {
universe@580 243 if (needle.length == 0) {
universe@580 244 return haystack;
universe@580 245 }
universe@580 246
universe@583 247 /* optimize for single-char needles */
universe@583 248 if (needle.length == 1) {
universe@583 249 return cx_strchr(haystack, *needle.ptr);
universe@583 250 }
universe@583 251
universe@580 252 /*
universe@580 253 * IMPORTANT:
universe@580 254 * Our prefix table contains the prefix length PLUS ONE
universe@580 255 * this is our decision, because we want to use the full range of size_t.
universe@580 256 * The original algorithm needs a (-1) at one single place,
universe@580 257 * and we want to avoid that.
universe@580 258 */
universe@580 259
universe@580 260 /* static prefix table */
universe@580 261 static uint8_t s_prefix_table[512];
universe@580 262
universe@580 263 /* check pattern length and use appropriate prefix table */
universe@580 264 /* if the pattern exceeds static prefix table, allocate on the heap */
universe@580 265 register int useheap = needle.length >= 512;
universe@580 266 register void *ptable = useheap ? calloc(needle.length + 1,
universe@580 267 sizeof(size_t)) : s_prefix_table;
universe@580 268
universe@580 269 /* keep counter in registers */
universe@580 270 register size_t i, j;
universe@580 271
universe@580 272 /* fill prefix table */
universe@580 273 i = 0;
universe@580 274 j = 0;
universe@580 275 ptable_w(useheap, ptable, i, j);
universe@580 276 while (i < needle.length) {
universe@580 277 while (j >= 1 && needle.ptr[j - 1] != needle.ptr[i]) {
universe@580 278 ptable_r(j, useheap, ptable, j - 1);
universe@580 279 }
universe@580 280 i++;
universe@580 281 j++;
universe@580 282 ptable_w(useheap, ptable, i, j);
universe@580 283 }
universe@580 284
universe@580 285 /* search */
universe@580 286 cxstring result = {NULL, 0};
universe@580 287 i = 0;
universe@580 288 j = 1;
universe@580 289 while (i < haystack.length) {
universe@580 290 while (j >= 1 && haystack.ptr[i] != needle.ptr[j - 1]) {
universe@580 291 ptable_r(j, useheap, ptable, j - 1);
universe@580 292 }
universe@580 293 i++;
universe@580 294 j++;
universe@580 295 if (j - 1 == needle.length) {
universe@580 296 size_t start = i - needle.length;
universe@580 297 result.ptr = haystack.ptr + start;
universe@580 298 result.length = haystack.length - start;
universe@580 299 break;
universe@580 300 }
universe@580 301 }
universe@580 302
universe@580 303 /* if prefix table was allocated on the heap, free it */
universe@580 304 if (ptable != s_prefix_table) {
universe@580 305 free(ptable);
universe@580 306 }
universe@580 307
universe@580 308 return result;
universe@580 309 }
universe@580 310
universe@580 311 cxmutstr cx_strstr_m(
universe@580 312 cxmutstr haystack,
universe@580 313 cxstring needle
universe@580 314 ) {
universe@580 315 cxstring result = cx_strstr(cx_strcast(haystack), needle);
universe@580 316 return (cxmutstr) {(char *) result.ptr, result.length};
universe@580 317 }
universe@580 318
universe@580 319 size_t cx_strsplit(
universe@580 320 cxstring string,
universe@580 321 cxstring delim,
universe@580 322 size_t limit,
universe@580 323 cxstring *output
universe@580 324 ) {
universe@583 325 /* special case: output limit is zero */
universe@583 326 if (limit == 0) return 0;
universe@583 327
universe@583 328 /* special case: delimiter is empty */
universe@583 329 if (delim.length == 0) {
universe@583 330 output[0] = string;
universe@583 331 return 1;
universe@583 332 }
universe@583 333
universe@583 334 /* special cases: delimiter is at least as large as the string */
universe@583 335 if (delim.length >= string.length) {
universe@583 336 /* exact match */
universe@583 337 if (cx_strcmp(string, delim) == 0) {
universe@583 338 output[0] = cx_strn(string.ptr, 0);
universe@583 339 output[1] = cx_strn(string.ptr + string.length, 0);
universe@583 340 return 2;
universe@583 341 } else /* no match possible */ {
universe@583 342 output[0] = string;
universe@583 343 return 1;
universe@583 344 }
universe@583 345 }
universe@583 346
universe@583 347 size_t n = 0;
universe@583 348 cxstring curpos = string;
universe@583 349 while (1) {
universe@583 350 ++n;
universe@583 351 cxstring match = cx_strstr(curpos, delim);
universe@583 352 if (match.length > 0) {
universe@583 353 /* is the limit reached? */
universe@583 354 if (n < limit) {
universe@583 355 /* copy the current string to the array */
universe@583 356 cxstring item = cx_strn(curpos.ptr, match.ptr - curpos.ptr);
universe@583 357 output[n - 1] = item;
universe@583 358 size_t processed = item.length + delim.length;
universe@583 359 curpos.ptr += processed;
universe@583 360 curpos.length -= processed;
universe@583 361 } else {
universe@583 362 /* limit reached, copy the _full_ remaining string */
universe@583 363 output[n - 1] = curpos;
universe@583 364 break;
universe@583 365 }
universe@583 366 } else {
universe@583 367 /* no more matches, copy last string */
universe@583 368 output[n - 1] = curpos;
universe@583 369 break;
universe@583 370 }
universe@583 371 }
universe@583 372
universe@583 373 return n;
universe@580 374 }
universe@580 375
universe@580 376 size_t cx_strsplit_a(
universe@580 377 CxAllocator *allocator,
universe@580 378 cxstring string,
universe@580 379 cxstring delim,
universe@580 380 size_t limit,
universe@580 381 cxstring **output
universe@580 382 ) {
universe@583 383 /* find out how many splits we're going to make and allocate memory */
universe@583 384 size_t n = 0;
universe@583 385 cxstring curpos = string;
universe@583 386 while (1) {
universe@583 387 ++n;
universe@583 388 cxstring match = cx_strstr(curpos, delim);
universe@583 389 if (match.length > 0) {
universe@583 390 /* is the limit reached? */
universe@583 391 if (n < limit) {
universe@583 392 size_t processed = match.ptr - curpos.ptr + delim.length;
universe@583 393 curpos.ptr += processed;
universe@583 394 curpos.length -= processed;
universe@583 395 } else {
universe@583 396 /* limit reached */
universe@583 397 break;
universe@583 398 }
universe@583 399 } else {
universe@583 400 /* no more matches */
universe@583 401 break;
universe@583 402 }
universe@583 403 }
universe@583 404 *output = cxCalloc(allocator, n, sizeof(cxstring));
universe@583 405 return cx_strsplit(string, delim, n, *output);
universe@580 406 }
universe@580 407
universe@580 408 size_t cx_strsplit_m(
universe@580 409 cxmutstr string,
universe@580 410 cxstring delim,
universe@580 411 size_t limit,
universe@580 412 cxmutstr *output
universe@580 413 ) {
universe@580 414 return cx_strsplit(cx_strcast(string),
universe@580 415 delim, limit, (cxstring *) output);
universe@580 416 }
universe@580 417
universe@580 418 size_t cx_strsplit_ma(
universe@580 419 CxAllocator *allocator,
universe@580 420 cxmutstr string,
universe@580 421 cxstring delim,
universe@580 422 size_t limit,
universe@580 423 cxmutstr **output
universe@580 424 ) {
universe@580 425 return cx_strsplit_a(allocator, cx_strcast(string),
universe@580 426 delim, limit, (cxstring **) output);
universe@580 427 }
universe@581 428
universe@583 429 int cx_strcmp(
universe@583 430 cxstring s1,
universe@583 431 cxstring s2
universe@583 432 ) {
universe@581 433 if (s1.length == s2.length) {
universe@581 434 return memcmp(s1.ptr, s2.ptr, s1.length);
universe@581 435 } else if (s1.length > s2.length) {
universe@581 436 return 1;
universe@581 437 } else {
universe@581 438 return -1;
universe@581 439 }
universe@581 440 }
universe@581 441
universe@583 442 int cx_strcasecmp(
universe@583 443 cxstring s1,
universe@583 444 cxstring s2
universe@583 445 ) {
universe@581 446 if (s1.length == s2.length) {
universe@581 447 #ifdef _WIN32
universe@581 448 return _strnicmp(s1.ptr, s2.ptr, s1.length);
universe@581 449 #else
universe@581 450 return strncasecmp(s1.ptr, s2.ptr, s1.length);
universe@581 451 #endif
universe@581 452 } else if (s1.length > s2.length) {
universe@581 453 return 1;
universe@581 454 } else {
universe@581 455 return -1;
universe@581 456 }
universe@581 457 }
universe@581 458
universe@583 459 cxmutstr cx_strdup_a(
universe@583 460 CxAllocator *allocator,
universe@583 461 cxstring string
universe@583 462 ) {
universe@581 463 cxmutstr result = {
universe@581 464 cxMalloc(allocator, string.length + 1),
universe@581 465 string.length
universe@581 466 };
universe@581 467 if (result.ptr == NULL) {
universe@581 468 result.length = 0;
universe@581 469 return result;
universe@581 470 }
universe@581 471 memcpy(result.ptr, string.ptr, string.length);
universe@581 472 result.ptr[string.length] = '\0';
universe@581 473 return result;
universe@581 474 }
universe@581 475
universe@581 476 cxstring cx_strtrim(cxstring string) {
universe@581 477 cxstring result = string;
universe@581 478 // TODO: optimize by comparing multiple bytes at once
universe@581 479 while (result.length > 0 && isspace(*result.ptr)) {
universe@581 480 result.ptr++;
universe@581 481 result.length--;
universe@581 482 }
universe@581 483 while (result.length > 0 && isspace(result.ptr[result.length - 1])) {
universe@581 484 result.length--;
universe@581 485 }
universe@581 486 return result;
universe@581 487 }
universe@581 488
universe@581 489 cxmutstr cx_strtrim_m(cxmutstr string) {
universe@581 490 cxstring result = cx_strtrim(cx_strcast(string));
universe@581 491 return (cxmutstr) {(char *) result.ptr, result.length};
universe@581 492 }
universe@581 493
universe@583 494 bool cx_strprefix(
universe@583 495 cxstring string,
universe@583 496 cxstring prefix
universe@583 497 ) {
universe@581 498 if (string.length < prefix.length) return false;
universe@581 499 return memcmp(string.ptr, prefix.ptr, prefix.length) == 0;
universe@581 500 }
universe@581 501
universe@583 502 bool cx_strsuffix(
universe@583 503 cxstring string,
universe@583 504 cxstring suffix
universe@583 505 ) {
universe@581 506 if (string.length < suffix.length) return false;
universe@581 507 return memcmp(string.ptr + string.length - suffix.length,
universe@581 508 suffix.ptr, suffix.length) == 0;
universe@581 509 }
universe@581 510
universe@583 511 bool cx_strcaseprefix(
universe@583 512 cxstring string,
universe@583 513 cxstring prefix
universe@583 514 ) {
universe@581 515 if (string.length < prefix.length) return false;
universe@581 516 #ifdef _WIN32
universe@581 517 return _strnicmp(string.ptr, prefix.ptr, prefix.length) == 0;
universe@581 518 #else
universe@581 519 return strncasecmp(string.ptr, prefix.ptr, prefix.length) == 0;
universe@581 520 #endif
universe@581 521 }
universe@581 522
universe@583 523 bool cx_strcasesuffix(
universe@583 524 cxstring string,
universe@583 525 cxstring suffix
universe@583 526 ) {
universe@581 527 if (string.length < suffix.length) return false;
universe@581 528 #ifdef _WIN32
universe@581 529 return _strnicmp(string.ptr+string.length-suffix.length,
universe@581 530 suffix.ptr, suffix.length) == 0;
universe@581 531 #else
universe@581 532 return strncasecmp(string.ptr + string.length - suffix.length,
universe@581 533 suffix.ptr, suffix.length) == 0;
universe@581 534 #endif
universe@581 535 }
universe@582 536
universe@582 537 void cx_strlower(cxmutstr string) {
universe@582 538 cx_for_n(i, string.length) {
universe@582 539 string.ptr[i] = tolower(string.ptr[i]);
universe@582 540 }
universe@582 541 }
universe@582 542
universe@582 543 void cx_strupper(cxmutstr string) {
universe@582 544 cx_for_n(i, string.length) {
universe@582 545 string.ptr[i] = toupper(string.ptr[i]);
universe@582 546 }
universe@582 547 }
universe@583 548
universe@583 549 #define REPLACE_INDEX_BUFFER_MAX 100
universe@583 550
universe@583 551 struct cx_strreplace_ibuf {
universe@583 552 size_t *buf;
universe@583 553 struct cx_strreplace_ibuf *next;
universe@590 554 unsigned int len;
universe@583 555 };
universe@583 556
universe@583 557 static void cx_strrepl_free_ibuf(struct cx_strreplace_ibuf *buf) {
universe@583 558 while (buf) {
universe@583 559 struct cx_strreplace_ibuf *next = buf->next;
universe@583 560 free(buf->buf);
universe@583 561 free(buf);
universe@583 562 buf = next;
universe@583 563 }
universe@583 564 }
universe@583 565
universe@583 566 cxmutstr cx_strreplacen_a(
universe@583 567 CxAllocator *allocator,
universe@583 568 cxstring str,
universe@583 569 cxstring pattern,
universe@583 570 cxstring replacement,
universe@583 571 size_t replmax
universe@583 572 ) {
universe@583 573
universe@583 574 if (pattern.length == 0 || pattern.length > str.length || replmax == 0)
universe@583 575 return cx_strdup_a(allocator, str);
universe@583 576
universe@583 577 /* Compute expected buffer length */
universe@583 578 size_t ibufmax = str.length / pattern.length;
universe@583 579 size_t ibuflen = replmax < ibufmax ? replmax : ibufmax;
universe@583 580 if (ibuflen > REPLACE_INDEX_BUFFER_MAX) {
universe@583 581 ibuflen = REPLACE_INDEX_BUFFER_MAX;
universe@583 582 }
universe@583 583
universe@583 584 /* Allocate first index buffer */
universe@583 585 struct cx_strreplace_ibuf *firstbuf, *curbuf;
universe@583 586 firstbuf = curbuf = calloc(1, sizeof(struct cx_strreplace_ibuf));
universe@583 587 if (!firstbuf) return cx_mutstrn(NULL, 0);
universe@583 588 firstbuf->buf = calloc(ibuflen, sizeof(size_t));
universe@583 589 if (!firstbuf->buf) {
universe@583 590 free(firstbuf);
universe@583 591 return cx_mutstrn(NULL, 0);
universe@583 592 }
universe@583 593
universe@583 594 /* Search occurrences */
universe@583 595 cxstring searchstr = str;
universe@583 596 size_t found = 0;
universe@583 597 do {
universe@583 598 cxstring match = cx_strstr(searchstr, pattern);
universe@583 599 if (match.length > 0) {
universe@583 600 /* Allocate next buffer in chain, if required */
universe@583 601 if (curbuf->len == ibuflen) {
universe@583 602 struct cx_strreplace_ibuf *nextbuf =
universe@583 603 calloc(1, sizeof(struct cx_strreplace_ibuf));
universe@583 604 if (!nextbuf) {
universe@583 605 cx_strrepl_free_ibuf(firstbuf);
universe@583 606 return cx_mutstrn(NULL, 0);
universe@583 607 }
universe@583 608 nextbuf->buf = calloc(ibuflen, sizeof(size_t));
universe@583 609 if (!nextbuf->buf) {
universe@583 610 free(nextbuf);
universe@583 611 cx_strrepl_free_ibuf(firstbuf);
universe@583 612 return cx_mutstrn(NULL, 0);
universe@583 613 }
universe@583 614 curbuf->next = nextbuf;
universe@583 615 curbuf = nextbuf;
universe@583 616 }
universe@583 617
universe@583 618 /* Record match index */
universe@583 619 found++;
universe@583 620 size_t idx = match.ptr - str.ptr;
universe@583 621 curbuf->buf[curbuf->len++] = idx;
universe@583 622 searchstr.ptr = match.ptr + pattern.length;
universe@583 623 searchstr.length = str.length - idx - pattern.length;
universe@583 624 } else {
universe@583 625 break;
universe@583 626 }
universe@583 627 } while (searchstr.length > 0 && found < replmax);
universe@583 628
universe@583 629 /* Allocate result string */
universe@583 630 cxmutstr result;
universe@583 631 {
universe@583 632 ssize_t adjlen = (ssize_t) replacement.length - (ssize_t) pattern.length;
universe@583 633 size_t rcount = 0;
universe@583 634 curbuf = firstbuf;
universe@583 635 do {
universe@583 636 rcount += curbuf->len;
universe@583 637 curbuf = curbuf->next;
universe@583 638 } while (curbuf);
universe@583 639 result.length = str.length + rcount * adjlen;
universe@590 640 result.ptr = cxMalloc(allocator, result.length + 1);
universe@583 641 if (!result.ptr) {
universe@583 642 cx_strrepl_free_ibuf(firstbuf);
universe@583 643 return cx_mutstrn(NULL, 0);
universe@583 644 }
universe@583 645 }
universe@583 646
universe@583 647 /* Build result string */
universe@583 648 curbuf = firstbuf;
universe@583 649 size_t srcidx = 0;
universe@583 650 char *destptr = result.ptr;
universe@583 651 do {
universe@583 652 for (size_t i = 0; i < curbuf->len; i++) {
universe@583 653 /* Copy source part up to next match*/
universe@583 654 size_t idx = curbuf->buf[i];
universe@583 655 size_t srclen = idx - srcidx;
universe@583 656 if (srclen > 0) {
universe@583 657 memcpy(destptr, str.ptr + srcidx, srclen);
universe@583 658 destptr += srclen;
universe@583 659 srcidx += srclen;
universe@583 660 }
universe@583 661
universe@583 662 /* Copy the replacement and skip the source pattern */
universe@583 663 srcidx += pattern.length;
universe@583 664 memcpy(destptr, replacement.ptr, replacement.length);
universe@583 665 destptr += replacement.length;
universe@583 666 }
universe@583 667 curbuf = curbuf->next;
universe@583 668 } while (curbuf);
universe@583 669 memcpy(destptr, str.ptr + srcidx, str.length - srcidx);
universe@583 670
universe@590 671 /* Result is guaranteed to be zero-terminated */
universe@590 672 result.ptr[result.length] = '\0';
universe@590 673
universe@583 674 /* Free index buffer */
universe@583 675 cx_strrepl_free_ibuf(firstbuf);
universe@583 676
universe@583 677 return result;
universe@583 678 }
universe@583 679
universe@583 680

mercurial