]> granicus.if.org Git - multimarkdown/commitdiff
UPDATED: Add test suite to d_string
authorFletcher T. Penney <fletcher@fletcherpenney.net>
Mon, 15 Jan 2018 16:44:11 +0000 (11:44 -0500)
committerFletcher T. Penney <fletcher@fletcherpenney.net>
Mon, 15 Jan 2018 16:44:11 +0000 (11:44 -0500)
Sources/libMultiMarkdown/d_string.c

index 697fab242649d2b5037b16b3dd28409bf450fb49..b6896e17c284819d523403588cd153cbc3089e5e 100644 (file)
 
 #include "d_string.h"
 
+#ifdef TEST
+       #include "CuTest.h"
+#endif
+
 
 /*
  * The following section came from:
@@ -145,6 +149,31 @@ DString* d_string_new(const char * startingString) {
 }
 
 
+#ifdef TEST
+void Test_d_string_new(CuTest* tc) {
+       char * test = "foo";
+
+       DString * result = d_string_new(test);
+
+       CuAssertIntEquals(tc, 3, result->currentStringLength);
+       CuAssertIntEquals(tc, kStringBufferStartingSize, result->currentStringBufferSize);
+       CuAssertStrEquals(tc, test, result->str);
+       CuAssertIntEquals(tc, '\0', result->str[strlen(test)]);
+
+       d_string_free(result, true);
+
+       result = d_string_new(NULL);
+
+       CuAssertIntEquals(tc, 0, result->currentStringLength);
+       CuAssertIntEquals(tc, kStringBufferStartingSize, result->currentStringBufferSize);
+       CuAssertStrEquals(tc, "", result->str);
+       CuAssertIntEquals(tc, '\0', 0);
+
+       d_string_free(result, true);
+}
+#endif
+
+
 /// Free dynamic string
 char* d_string_free(DString * ripString, bool freeCharacterData) {
        if (ripString == NULL) {
@@ -169,234 +198,584 @@ char* d_string_free(DString * ripString, bool freeCharacterData) {
 
 /// Ensure that dynamic string has specified capacity
 static void ensureStringBufferCanHold(DString * baseString, size_t newStringSize) {
-       size_t newBufferSizeNeeded = newStringSize + 1;
+       if (baseString) {
+               size_t newBufferSizeNeeded = newStringSize + 1;
+
+               if (newBufferSizeNeeded > baseString->currentStringBufferSize) {
+                       size_t newBufferSize = baseString->currentStringBufferSize;
+
+                       while (newBufferSizeNeeded > newBufferSize) {
+                               if (newBufferSize > kStringBufferMaxIncrement) {
+                                       newBufferSize += kStringBufferMaxIncrement;
+                               } else {
+                                       newBufferSize *= kStringBufferGrowthMultiplier;
+                               }
+                       }
+
+                       char *temp;
+                       temp = realloc(baseString->str, newBufferSize);
 
-       if (newBufferSizeNeeded > baseString->currentStringBufferSize) {
-               size_t newBufferSize = baseString->currentStringBufferSize;
+                       if (temp == NULL) {
+                               /* realloc failed */
+                               fprintf(stderr, "Error reallocating memory for d_string. Current buffer size %lu.\n", baseString->currentStringBufferSize);
 
-               while (newBufferSizeNeeded > newBufferSize) {
-                       if (newBufferSize > kStringBufferMaxIncrement) {
-                               newBufferSize += kStringBufferMaxIncrement;
-                       } else {
-                               newBufferSize *= kStringBufferGrowthMultiplier;
+                               exit(1);
                        }
+
+                       baseString->str = temp;
+                       baseString->currentStringBufferSize = newBufferSize;
                }
+       }
+}
 
-               char *temp;
-               temp = realloc(baseString->str, newBufferSize);
 
-               if (temp == NULL) {
-                       /* realloc failed */
-                       fprintf(stderr, "Error reallocating memory for d_string. Current buffer size %lu.\n", baseString->currentStringBufferSize);
+#ifdef TEST
+void Test_ensureStringBufferCanHold(CuTest* tc) {
+       char * test = "foo";
 
-                       exit(1);
-               }
+       DString * result = d_string_new(test);
 
-               baseString->str = temp;
-               baseString->currentStringBufferSize = newBufferSize;
-       }
+       ensureStringBufferCanHold(result, 1024);
+       CuAssertIntEquals(tc, 2048, result->currentStringBufferSize);
+
+       ensureStringBufferCanHold(result, 1024);
+       CuAssertIntEquals(tc, 2048, result->currentStringBufferSize);
+
+       /* This becomes 0 after we add 1 for the '\0' */
+       ensureStringBufferCanHold(result, -1);
+       CuAssertIntEquals(tc, 2048, result->currentStringBufferSize);
+
+       ensureStringBufferCanHold(result, 1024 * 1024 - 1);
+       CuAssertIntEquals(tc, 1024 * 1024, result->currentStringBufferSize);
+
+       ensureStringBufferCanHold(result, 1024 * 1024 - 1);
+       CuAssertIntEquals(tc, 1024 * 1024, result->currentStringBufferSize);
+
+       ensureStringBufferCanHold(NULL, 1024);
+
+       d_string_free(result, true);
 }
+#endif
 
 
 /// Append null-terminated string to end of dynamic string
 void d_string_append(DString * baseString, const char * appendedString) {
-       size_t appendedStringLength = strlen(appendedString);
+       if (baseString && appendedString) {
+               size_t appendedStringLength = strlen(appendedString);
 
-       if ((appendedString != NULL) && (appendedStringLength > 0)) {
-               size_t newStringLength = baseString->currentStringLength + appendedStringLength;
-               ensureStringBufferCanHold(baseString, newStringLength);
+               if (appendedStringLength > 0) {
+                       size_t newStringLength = baseString->currentStringLength + appendedStringLength;
+                       ensureStringBufferCanHold(baseString, newStringLength);
 
-               /* We already know where the current string ends, so pass that as the starting address for strncat */
-               strncat(baseString->str + baseString->currentStringLength, appendedString, appendedStringLength);
-               baseString->currentStringLength = newStringLength;
+                       /* We already know where the current string ends, so pass that as the starting address for strncat */
+                       strncat(baseString->str + baseString->currentStringLength, appendedString, appendedStringLength);
+                       baseString->currentStringLength = newStringLength;
+               }
        }
 }
 
 
+#ifdef TEST
+void Test_d_string_append(CuTest* tc) {
+       char * test = "foo";
+
+       DString * result = d_string_new(test);
+
+       d_string_append(result, "bar");
+       CuAssertStrEquals(tc, "foobar", result->str);
+
+       d_string_append(result, "");
+       CuAssertStrEquals(tc, "foobar", result->str);
+
+       d_string_append(result, NULL);
+       CuAssertStrEquals(tc, "foobar", result->str);
+
+       d_string_append(NULL, "foo");
+
+       d_string_free(result, true);
+}
+#endif
+
+
 /// Append single character to end of dynamic string
 void d_string_append_c(DString * baseString, char appendedCharacter) {
-       size_t newSizeNeeded = baseString->currentStringLength + 1;
-       ensureStringBufferCanHold(baseString, newSizeNeeded);
+       if (baseString && appendedCharacter) {
+               size_t newSizeNeeded = baseString->currentStringLength + 1;
+               ensureStringBufferCanHold(baseString, newSizeNeeded);
+
+               baseString->str[baseString->currentStringLength] = appendedCharacter;
+               baseString->currentStringLength++;
+               baseString->str[baseString->currentStringLength] = '\0';
+       }
+}
+
+
+#ifdef TEST
+void Test_d_string_append_c(CuTest* tc) {
+       char * test = "foo";
 
-       baseString->str[baseString->currentStringLength] = appendedCharacter;
-       baseString->currentStringLength++;
-       baseString->str[baseString->currentStringLength] = '\0';
+       DString * result = d_string_new(test);
+
+       d_string_append_c(result, 'z');
+       CuAssertStrEquals(tc, "fooz", result->str);
+       CuAssertIntEquals(tc, 4, result->currentStringLength);
+
+       d_string_append_c(result, 0);
+       CuAssertStrEquals(tc, "fooz", result->str);
+       CuAssertIntEquals(tc, 4, result->currentStringLength);
+
+       d_string_append_c(NULL, 'f');
+
+       d_string_free(result, true);
 }
+#endif
 
 
 /// Append array of characters to end of dynamic string
 void d_string_append_c_array(DString * baseString, const char * appendedChars, size_t bytes) {
-       size_t newSizeNeeded = baseString->currentStringLength + bytes;
-       ensureStringBufferCanHold(baseString, newSizeNeeded);
+       if (baseString && appendedChars) {
+               if (bytes == -1) {
+                       // This is the same as regular append
+                       d_string_append(baseString, appendedChars);
+               } else {
+                       if (appendedChars) {
+                               size_t newSizeNeeded = baseString->currentStringLength + bytes;
+                               ensureStringBufferCanHold(baseString, newSizeNeeded);
+
+                               memcpy(baseString->str + baseString->currentStringLength, appendedChars, bytes);
+
+                               baseString->currentStringLength = newSizeNeeded;
+                               baseString->str[baseString->currentStringLength] = '\0';
+                       }
+               }
+       }
+}
+
+
+#ifdef TEST
+void Test_d_string_append_c_array(CuTest* tc) {
+       char * test = "foo";
 
-       memcpy(baseString->str + baseString->currentStringLength, appendedChars, bytes);
+       DString * result = d_string_new(test);
 
-       baseString->currentStringLength = newSizeNeeded;
-       baseString->str[baseString->currentStringLength] = '\0';
+       d_string_append_c_array(result, "bar", 3);
+       CuAssertStrEquals(tc, "foobar", result->str);
+       CuAssertIntEquals(tc, 6, result->currentStringLength);
+
+       d_string_append_c_array(result, "baz", -1);
+       CuAssertStrEquals(tc, "foobarbaz", result->str);
+       CuAssertIntEquals(tc, 9, result->currentStringLength);
+
+       d_string_append_c_array(result, NULL, 0);
+       CuAssertStrEquals(tc, "foobarbaz", result->str);
+       CuAssertIntEquals(tc, 9, result->currentStringLength);
+
+       d_string_append_c_array(result, NULL, -1);
+       CuAssertStrEquals(tc, "foobarbaz", result->str);
+       CuAssertIntEquals(tc, 9, result->currentStringLength);
+
+       d_string_append_c_array(NULL, "foo", -1);
+
+       d_string_free(result, true);
 }
+#endif
 
 
 /// Append to end of dynamic string using format specifier
 void d_string_append_printf(DString * baseString, const char * format, ...) {
-       va_list args;
-       va_start(args, format);
+       if (baseString && format) {
+               va_list args;
+               va_start(args, format);
 
-       char* formattedString = NULL;
-       vasprintf(&formattedString, format, args);
+               char* formattedString = NULL;
+               vasprintf(&formattedString, format, args);
 
-       if (formattedString != NULL) {
-               d_string_append(baseString, formattedString);
-               free(formattedString);
+               if (formattedString != NULL) {
+                       d_string_append(baseString, formattedString);
+                       free(formattedString);
+               }
+
+               va_end(args);
        }
+}
+
+
+#ifdef TEST
+void Test_d_string_append_printf(CuTest* tc) {
+       char * test = "foo";
 
-       va_end(args);
+       DString * result = d_string_new(test);
+
+       d_string_append_printf(result, "%dbar%d", 5, 7);
+       CuAssertStrEquals(tc, "foo5bar7", result->str);
+       CuAssertIntEquals(tc, 8, result->currentStringLength);
+
+       d_string_append_printf(result, NULL);
+       CuAssertStrEquals(tc, "foo5bar7", result->str);
+       CuAssertIntEquals(tc, 8, result->currentStringLength);
+
+       d_string_append_printf(result, NULL, 5, 7);
+       CuAssertStrEquals(tc, "foo5bar7", result->str);
+       CuAssertIntEquals(tc, 8, result->currentStringLength);
+
+       d_string_append_printf(NULL, "foo");
+
+       d_string_free(result, true);
 }
+#endif
 
 
 /// Prepend null-terminated string to end of dynamic string
 void d_string_prepend(DString * baseString, const char * prependedString) {
-       size_t prependedStringLength = strlen(prependedString);
+       if (baseString && prependedString) {
+               size_t prependedStringLength = strlen(prependedString);
 
-       if ((prependedString != NULL) && (prependedStringLength > 0)) {
-               size_t newStringLength = baseString->currentStringLength + prependedStringLength;
-               ensureStringBufferCanHold(baseString, newStringLength);
+               if (prependedStringLength > 0) {
+                       size_t newStringLength = baseString->currentStringLength + prependedStringLength;
+                       ensureStringBufferCanHold(baseString, newStringLength);
 
-               memmove(baseString->str + prependedStringLength, baseString->str, baseString->currentStringLength);
-               strncpy(baseString->str, prependedString, prependedStringLength);
-               baseString->currentStringLength = newStringLength;
-               baseString->str[baseString->currentStringLength] = '\0';
+                       memmove(baseString->str + prependedStringLength, baseString->str, baseString->currentStringLength);
+                       strncpy(baseString->str, prependedString, prependedStringLength);
+                       baseString->currentStringLength = newStringLength;
+                       baseString->str[baseString->currentStringLength] = '\0';
+               }
        }
 }
 
 
+#ifdef TEST
+void Test_d_string_prepend(CuTest* tc) {
+       char * test = "foo";
+
+       DString * result = d_string_new(test);
+
+       d_string_prepend(result, "bar");
+       CuAssertStrEquals(tc, "barfoo", result->str);
+       CuAssertIntEquals(tc, 6, result->currentStringLength);
+
+       d_string_prepend(result, NULL);
+       CuAssertStrEquals(tc, "barfoo", result->str);
+       CuAssertIntEquals(tc, 6, result->currentStringLength);
+
+       d_string_prepend(NULL, "bar");
+
+       d_string_free(result, true);
+}
+#endif
+
+
 /// Insert null-terminated string inside dynamic string
 void d_string_insert(DString * baseString, size_t pos, const char * insertedString) {
-       size_t insertedStringLength = strlen(insertedString);
+       if (baseString && insertedString) {
+               size_t insertedStringLength = strlen(insertedString);
+
+               if (insertedStringLength > 0) {
+                       if (pos > baseString->currentStringLength) {
+                               pos = baseString->currentStringLength;
+                       }
+
+                       size_t newStringLength = baseString->currentStringLength + insertedStringLength;
+                       ensureStringBufferCanHold(baseString, newStringLength);
+
+                       /* Shift following string to 'right' */
+                       memmove(baseString->str + pos + insertedStringLength, baseString->str + pos, baseString->currentStringLength - pos);
+                       strncpy(baseString->str + pos, insertedString, insertedStringLength);
+                       baseString->currentStringLength = newStringLength;
+                       baseString->str[baseString->currentStringLength] = '\0';
+               }
+       }
+}
+
+
+#ifdef TEST
+void Test_d_string_insert(CuTest* tc) {
+       char * test = "foo";
+
+       DString * result = d_string_new(test);
+
+       d_string_insert(result, 2, "bar");
+       CuAssertStrEquals(tc, "fobaro", result->str);
+       CuAssertIntEquals(tc, 6, result->currentStringLength);
+
+       d_string_insert(result, -1, "bar");
+       CuAssertStrEquals(tc, "fobarobar", result->str);
+       CuAssertIntEquals(tc, 9, result->currentStringLength);
+
+       d_string_insert(result, -1, NULL);
+       CuAssertStrEquals(tc, "fobarobar", result->str);
+       CuAssertIntEquals(tc, 9, result->currentStringLength);
+
+       d_string_insert(NULL, 0, NULL);
 
-       if ((insertedString != NULL) && (insertedStringLength > 0)) {
+       d_string_free(result, true);
+}
+#endif
+
+
+/// Insert single character inside dynamic string
+void d_string_insert_c(DString * baseString, size_t pos, char insertedCharacter) {
+       if (baseString && insertedCharacter) {
                if (pos > baseString->currentStringLength) {
                        pos = baseString->currentStringLength;
                }
 
-               size_t newStringLength = baseString->currentStringLength + insertedStringLength;
-               ensureStringBufferCanHold(baseString, newStringLength);
+               size_t newSizeNeeded = baseString->currentStringLength + 1;
+               ensureStringBufferCanHold(baseString, newSizeNeeded);
 
                /* Shift following string to 'right' */
-               memmove(baseString->str + pos + insertedStringLength, baseString->str + pos, baseString->currentStringLength - pos);
-               strncpy(baseString->str + pos, insertedString, insertedStringLength);
-               baseString->currentStringLength = newStringLength;
+               memmove(baseString->str + pos + 1, baseString->str + pos, baseString->currentStringLength - pos);
+
+               baseString->str[pos] = insertedCharacter;
+               baseString->currentStringLength++;
                baseString->str[baseString->currentStringLength] = '\0';
        }
 }
 
 
-/// Insert single character inside dynamic string
-void d_string_insert_c(DString * baseString, size_t pos, char insertedCharacter) {
-       if (pos > baseString->currentStringLength) {
-               pos = baseString->currentStringLength;
-       }
+#ifdef TEST
+void Test_d_string_insert_c(CuTest* tc) {
+       char * test = "foo";
+
+       DString * result = d_string_new(test);
 
-       size_t newSizeNeeded = baseString->currentStringLength + 1;
-       ensureStringBufferCanHold(baseString, newSizeNeeded);
+       d_string_insert_c(result, 2, 'b');
+       CuAssertStrEquals(tc, "fobo", result->str);
+       CuAssertIntEquals(tc, 4, result->currentStringLength);
 
-       /* Shift following string to 'right' */
-       memmove(baseString->str + pos + 1, baseString->str + pos, baseString->currentStringLength - pos);
+       d_string_insert_c(result, -1, 'z');
+       CuAssertStrEquals(tc, "foboz", result->str);
+       CuAssertIntEquals(tc, 5, result->currentStringLength);
 
-       baseString->str[pos] = insertedCharacter;
-       baseString->currentStringLength++;
-       baseString->str[baseString->currentStringLength] = '\0';
+       d_string_insert_c(result, 3, 0);
+       CuAssertStrEquals(tc, "foboz", result->str);
+       CuAssertIntEquals(tc, 5, result->currentStringLength);
+
+       d_string_insert_c(NULL, 0, 0);
+
+       d_string_free(result, true);
 }
+#endif
 
 
 /// Insert inside dynamic string using format specifier
 void d_string_insert_printf(DString * baseString, size_t pos, const char * format, ...) {
-       va_list args;
-       va_start(args, format);
+       if (baseString && format) {
+               va_list args;
+               va_start(args, format);
 
-       char* formattedString = NULL;
-       vasprintf(&formattedString, format, args);
+               char* formattedString = NULL;
+               vasprintf(&formattedString, format, args);
 
-       if (formattedString != NULL) {
-               d_string_insert(baseString, pos, formattedString);
-               free(formattedString);
+               if (formattedString != NULL) {
+                       d_string_insert(baseString, pos, formattedString);
+                       free(formattedString);
+               }
+
+               va_end(args);
        }
+}
+
+
+#ifdef TEST
+void Test_d_string_insert_printf(CuTest* tc) {
+       char * test = "foo";
+
+       DString * result = d_string_new(test);
+
+       d_string_insert_printf(result, 2, "%dbar%d", 5, 7);
+       CuAssertStrEquals(tc, "fo5bar7o", result->str);
+       CuAssertIntEquals(tc, 8, result->currentStringLength);
+
+       d_string_insert_printf(result, -1, "z", 5, 7);
+       CuAssertStrEquals(tc, "fo5bar7oz", result->str);
+       CuAssertIntEquals(tc, 9, result->currentStringLength);
 
-       va_end(args);
+       d_string_insert_printf(NULL, 0, NULL);
+
+       d_string_free(result, true);
 }
+#endif
 
 
 /// Erase portion of dynamic string
 void d_string_erase(DString * baseString, size_t pos, size_t len) {
-       if ((pos > baseString->currentStringLength) || (len <= 0)) {
-               return;
-       }
+       if (baseString) {
+               if ((pos > baseString->currentStringLength) || (len <= 0)) {
+                       return;
+               }
 
-       if ((pos + len) >= baseString->currentStringLength) {
-               len = -1;
-       }
+               if ((pos + len) >= baseString->currentStringLength) {
+                       len = -1;
+               }
 
-       if (len == -1) {
-               baseString->currentStringLength = pos;
-       } else {
-               memmove(baseString->str + pos, baseString->str + pos + len, baseString->currentStringLength - pos - len);
-               baseString->currentStringLength -= len;
+               if (len == -1) {
+                       baseString->currentStringLength = pos;
+               } else {
+                       memmove(baseString->str + pos, baseString->str + pos + len, baseString->currentStringLength - pos - len);
+                       baseString->currentStringLength -= len;
+               }
+
+               baseString->str[baseString->currentStringLength] = '\0';
        }
+}
+
+
+#ifdef TEST
+void Test_d_string_erase(CuTest* tc) {
+       char * test = "foobar";
+
+       DString * result = d_string_new(test);
+
+       d_string_erase(result, 2, 1);
+       CuAssertStrEquals(tc, "fobar", result->str);
+       CuAssertIntEquals(tc, 5, result->currentStringLength);
 
-       baseString->str[baseString->currentStringLength] = '\0';
+       d_string_erase(result, -1, -1);
+       CuAssertStrEquals(tc, "fobar", result->str);
+       CuAssertIntEquals(tc, 5, result->currentStringLength);
+
+       d_string_erase(result, 2, -1);
+       CuAssertStrEquals(tc, "fo", result->str);
+       CuAssertIntEquals(tc, 2, result->currentStringLength);
+
+       d_string_erase(NULL, 0, 0);
+
+       d_string_free(result, true);
 }
+#endif
+
 
 /// Copy a portion of dynamic string
 char * d_string_copy_substring(DString * d, size_t start, size_t len) {
-       char * result;
+       if (d) {
+               char * result;
+
+               if ((len == -1) && (start < d->currentStringLength)) {
+                       len = d->currentStringLength - start;
+               } else {
+                       if (start + len > d->currentStringLength) {
+                               fprintf(stderr, "d_string: Asked to copy invalid substring range.\n");
+                               fprintf(stderr, "start: %lu  len: %lu  string: %lu\n", start, len,
+                                               d->currentStringLength);
+                               return NULL;
+                       }
+               }
+
+               result = malloc(len + 1);
+               strncpy(result, &d->str[start], len);
+               result[len] = '\0';
 
-       if (len == -1) {
-               len = d->currentStringLength - start;
+               return result;
        } else {
-               if (start + len > d->currentStringLength) {
-                       fprintf(stderr, "d_string: Asked to copy invalid substring range.\n");
-                       fprintf(stderr, "start: %lu  len: %lu  string: %lu\n", start, len,
-                                       d->currentStringLength);
-                       return NULL;
-               }
+               return NULL;
        }
+}
+
+
+#ifdef TEST
+void Test_d_string_copy_substring(CuTest* tc) {
+       char * test = "foobar";
+
+       DString * result = d_string_new(test);
+
+       test = d_string_copy_substring(result, 0, -1);
+       CuAssertStrEquals(tc, "foobar", test);
+       free(test);
+
+       test = d_string_copy_substring(result, 2, 3);
+       CuAssertStrEquals(tc, "oba", test);
+       free(test);
 
-       result = malloc(len + 1);
-       strncpy(result, &d->str[start], len);
-       result[len] = '\0';
+       test = d_string_copy_substring(result, 8, 2);
+       CuAssertStrEquals(tc, NULL, test);
+       free(test);
 
-       return result;
+       test = d_string_copy_substring(result, -1, -1);
+       CuAssertStrEquals(tc, NULL, test);
+       free(test);
+
+       test = d_string_copy_substring(NULL, -1, -1);
+       CuAssertStrEquals(tc, NULL, test);
+
+       d_string_free(result, true);
 }
+#endif
 
 
 /// Replace occurences of "original" with "replace" inside the specified range
 /// Returns the change in overall length
 long d_string_replace_text_in_range(DString * d, size_t pos, size_t len, const char * original, const char * replace) {
-       long delta = 0;         // Overall change in length
+       if (d && original && replace) {
+               long delta = 0;         // Overall change in length
+
+               long len_o = strlen(original);
+               long len_r = strlen(replace);
+               long change = len_r - len_o;    // Change in length for each replacement
+
+               size_t stop;
+
+               if (len == -1) {
+                       stop = d->currentStringLength;
+               } else {
+                       stop = pos + len;
 
-       long len_o = strlen(original);
-       long len_r = strlen(replace);
-       long change = len_r - len_o;    // Change in length for each replacement
+                       if (stop > d->currentStringLength) {
+                               stop = d->currentStringLength;
+                       }
+               }
+
+               char * match = strstr(&(d->str[pos]), original);
 
-       size_t stop;
+               while (match && (match - d->str < stop)) {
+                       pos = match - d->str;
+                       d_string_erase(d, match - d->str, len_o);
+                       d_string_insert(d, match - d->str, replace);
+
+                       delta += change;
+                       stop += change;
+                       match = strstr(d->str + pos + len_r, original);
+               }
 
-       if (len == -1) {
-               stop = d->currentStringLength;
+               return delta;
        } else {
-               stop = pos + len;
+               return 0;
        }
+}
 
-       char * match = strstr(&(d->str[pos]), original);
 
-       while (match && (match - d->str < stop)) {
-               pos = match - d->str;
-               d_string_erase(d, match - d->str, len_o);
-               d_string_insert(d, match - d->str, replace);
+#ifdef TEST
+void Test_d_string_replace_text_in_range(CuTest* tc) {
+       char * test = "foobarfoobarfoo";
+       long delta = 0;
 
-               delta += change;
-               stop += change;
-               match = strstr(d->str + pos + len_r, original);
-       }
+       DString * result = d_string_new(test);
 
-       return delta;
-}
+       delta = d_string_replace_text_in_range(result, 100, 3, "foo", "zapz");
+       CuAssertIntEquals(tc, 15, result->currentStringLength);
+       CuAssertStrEquals(tc, "foobarfoobarfoo", result->str);
+       CuAssertIntEquals(tc, delta, 0);
+
+       delta = d_string_replace_text_in_range(result, 0, 3, "foo", "zapz");
+       CuAssertIntEquals(tc, 16, result->currentStringLength);
+       CuAssertStrEquals(tc, "zapzbarfoobarfoo", result->str);
+       CuAssertIntEquals(tc, delta, 1);
+
+       delta = d_string_replace_text_in_range(result, 0, 100, "foo", "zapz");
+       CuAssertIntEquals(tc, 18, result->currentStringLength);
+       CuAssertStrEquals(tc, "zapzbarzapzbarzapz", result->str);
+       CuAssertIntEquals(tc, delta, 2);
 
+       delta = d_string_replace_text_in_range(result, 0, -1, NULL, "zap");
+       CuAssertIntEquals(tc, 18, result->currentStringLength);
+       CuAssertStrEquals(tc, "zapzbarzapzbarzapz", result->str);
+       CuAssertIntEquals(tc, delta, 0);
+
+       d_string_replace_text_in_range(result, 0, -1, "foo", NULL);
+       CuAssertIntEquals(tc, 18, result->currentStringLength);
+       CuAssertStrEquals(tc, "zapzbarzapzbarzapz", result->str);
+
+       d_string_replace_text_in_range(NULL, 0, -1, "foo", NULL);
+
+       d_string_free(result, true);
+}
+#endif