1/* 2 * An implementation of replacing substrings in a string. 3 * 4 * Source: http://creativeandcritical.net/str-replace-c 5 * Author: Laird Shaw 6 * License: Public domain 7 * 8 * Brought on 2016-02-24 9 */ 10 11#include <string.h> 12#include <stdlib.h> 13#include <stddef.h> 14 15#include "repl_str.h" 16 17 18/* An optimised string replacement function that caches string positions so that 19 * strstr() doesn't need to be called twice for each position. 20 * 21 * This code is entirely by me, and released into the public domain, so have no 22 * doubts about your right to use it. 23 */ 24char *replace_substr(const char *str, const char *old, const char *new) { 25 26 /* Adjust each of the below values to suit your needs. */ 27 28 /* Increment positions cache size initially by this number. */ 29 size_t cache_sz_inc = 16; 30 /* Thereafter, each time capacity needs to be increased, 31 * multiply the increment by this factor. */ 32 const size_t cache_sz_inc_factor = 3; 33 /* But never increment capacity by more than this number. */ 34 const size_t cache_sz_inc_max = 1048576; 35 36 char *pret, *ret = NULL; 37 const char *pstr2, *pstr = str; 38 size_t i, count = 0; 39 ptrdiff_t *pos_cache = NULL; 40 size_t cache_sz = 0; 41 size_t cpylen, orglen, retlen, newlen, oldlen = strlen(old); 42 43 /* Find all matches and cache their positions. */ 44 while ((pstr2 = strstr(pstr, old)) != NULL) { 45 count++; 46 47 /* Increase the cache size when necessary. */ 48 if (cache_sz < count) { 49 cache_sz += cache_sz_inc; 50 pos_cache = realloc(pos_cache, sizeof(*pos_cache) * cache_sz); 51 if (pos_cache == NULL) { 52 goto end_repl_str; 53 } 54 cache_sz_inc *= cache_sz_inc_factor; 55 if (cache_sz_inc > cache_sz_inc_max) { 56 cache_sz_inc = cache_sz_inc_max; 57 } 58 } 59 60 pos_cache[count-1] = pstr2 - str; 61 pstr = pstr2 + oldlen; 62 } 63 64 orglen = pstr - str + strlen(pstr); 65 66 /* Allocate memory for the post-replacement string. */ 67 if (count > 0) { 68 newlen = strlen(new); 69 retlen = orglen + (newlen - oldlen) * count; 70 } else retlen = orglen; 71 ret = malloc(retlen + 1); 72 if (ret == NULL) { 73 goto end_repl_str; 74 } 75 76 if (count == 0) { 77 /* If no matches, then just duplicate the string. */ 78 strcpy(ret, str); 79 } else { 80 /* Otherwise, duplicate the string whilst performing 81 * the replacements using the position cache. */ 82 pret = ret; 83 memcpy(pret, str, pos_cache[0]); 84 pret += pos_cache[0]; 85 for (i = 0; i < count; i++) { 86 memcpy(pret, new, newlen); 87 pret += newlen; 88 pstr = str + pos_cache[i] + oldlen; 89 cpylen = (i == count-1 ? orglen : pos_cache[i+1]) - pos_cache[i] - oldlen; 90 memcpy(pret, pstr, cpylen); 91 pret += cpylen; 92 } 93 ret[retlen] = '\0'; 94 } 95 96end_repl_str: 97 /* Free the cache and return the post-replacement string, 98 * which will be NULL in the event of an error. */ 99 free(pos_cache); 100 return ret; 101} 102