repl_str.c
1 #include <string.h> 2 #include <stdlib.h> 3 #include <stddef.h> 4 5 #if (__STDC_VERSION__ >= 199901L) 6 #include <stdint.h> 7 #endif 8 9 char *repl_str(const char *str, const char *from, const char *to) { 10 11 /* Adjust each of the below values to suit your needs. */ 12 13 /* Increment positions cache size initially by this number. */ 14 size_t cache_sz_inc = 16; 15 /* Thereafter, each time capacity needs to be increased, 16 * multiply the increment by this factor. */ 17 const size_t cache_sz_inc_factor = 3; 18 /* But never increment capacity by more than this number. */ 19 const size_t cache_sz_inc_max = 1048576; 20 21 char *pret, *ret = NULL; 22 const char *pstr2, *pstr = str; 23 size_t i, count = 0; 24 #if (__STDC_VERSION__ >= 199901L) 25 uintptr_t *pos_cache_tmp, *pos_cache = NULL; 26 #else 27 ptrdiff_t *pos_cache_tmp, *pos_cache = NULL; 28 #endif 29 size_t cache_sz = 0; 30 size_t cpylen, orglen, retlen, tolen, fromlen = strlen(from); 31 32 /* Find all matches and cache their positions. */ 33 while ((pstr2 = strstr(pstr, from)) != NULL) { 34 count++; 35 36 /* Increase the cache size when necessary. */ 37 if (cache_sz < count) { 38 cache_sz += cache_sz_inc; 39 pos_cache_tmp = realloc(pos_cache, sizeof(*pos_cache) * cache_sz); 40 if (pos_cache_tmp == NULL) { 41 goto end_repl_str; 42 } else pos_cache = pos_cache_tmp; 43 cache_sz_inc *= cache_sz_inc_factor; 44 if (cache_sz_inc > cache_sz_inc_max) { 45 cache_sz_inc = cache_sz_inc_max; 46 } 47 } 48 49 pos_cache[count-1] = (uintptr_t)(pstr2 - str); 50 pstr = pstr2 + fromlen; 51 } 52 53 orglen = (size_t)(pstr - str) + strlen(pstr); 54 55 /* Allocate memory for the post-replacement string. */ 56 if (count > 0) { 57 tolen = strlen(to); 58 retlen = orglen + (tolen - fromlen) * count; 59 } else retlen = orglen; 60 ret = malloc(retlen + 1); 61 if (ret == NULL) { 62 goto end_repl_str; 63 } 64 65 if (count == 0) { 66 /* If no matches, then just duplicate the string. */ 67 strcpy(ret, str); 68 } else { 69 /* Otherwise, duplicate the string whilst performing 70 * the replacements using the position cache. */ 71 pret = ret; 72 memcpy(pret, str, pos_cache[0]); 73 pret += pos_cache[0]; 74 for (i = 0; i < count; i++) { 75 memcpy(pret, to, tolen); 76 pret += tolen; 77 pstr = str + pos_cache[i] + fromlen; 78 cpylen = (i == count-1 ? orglen : pos_cache[i+1]) - pos_cache[i] - fromlen; 79 memcpy(pret, pstr, cpylen); 80 pret += cpylen; 81 } 82 ret[retlen] = '\0'; 83 } 84 85 end_repl_str: 86 /* Free the cache and return the post-replacement string, 87 * which will be NULL in the event of an error. */ 88 free(pos_cache); 89 return ret; 90 }