1.1 --- a/core/sm_stringutil.cpp Tue May 22 15:42:18 2007 +0000
1.2 +++ b/core/sm_stringutil.cpp Tue May 22 20:50:48 2007 +0000
1.3 @@ -910,3 +910,165 @@
1.4 strcpy(ptr, str);
1.5 return ptr;
1.6 }
1.7 +
1.8 +unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace)
1.9 +{
1.10 + size_t searchLen = strlen(search);
1.11 + size_t replaceLen = strlen(replace);
1.12 +
1.13 + char *ptr = subject;
1.14 + unsigned int total = 0;
1.15 + while ((ptr = UTIL_ReplaceEx(ptr, maxlength, search, searchLen, replace, replaceLen)) != NULL)
1.16 + {
1.17 + total++;
1.18 + if (*ptr == '\0')
1.19 + {
1.20 + break;
1.21 + }
1.22 + }
1.23 +
1.24 + return total;
1.25 +}
1.26 +
1.27 +/**
1.28 + * NOTE: Do not edit this for the love of god unless you have
1.29 + * read the test cases and understand the code behind each one.
1.30 + * While I don't guarantee there aren't mistakes, I do guarantee
1.31 + * that plugins will end up relying on tiny idiosyncracies of this
1.32 + * function, just like they did with AMX Mod X.
1.33 + *
1.34 + * There are explicitly more cases than the AMX Mod X version because
1.35 + * we're not doing a blind copy. Each case is specifically optimized
1.36 + * for what needs to be done. Even better, we don't have to error on
1.37 + * bad buffer sizes. Instead, this function will smartly cut off the
1.38 + * string in a way that pushes old data out.
1.39 + */
1.40 +char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen)
1.41 +{
1.42 + char *ptr = subject;
1.43 + size_t browsed = 0;
1.44 + size_t textLen = strlen(subject);
1.45 +
1.46 + /* It's not possible to search or replace */
1.47 + if (searchLen > textLen || maxLen <= 1)
1.48 + {
1.49 + return NULL;
1.50 + }
1.51 +
1.52 + /* Subtract one off the maxlength so we can include the null terminator */
1.53 + maxLen--;
1.54 +
1.55 + while (*ptr != '\0' && (browsed <= textLen - searchLen))
1.56 + {
1.57 + /* See if we get a comparison */
1.58 + if (strncmp(ptr, search, searchLen) == 0)
1.59 + {
1.60 + char *retPtr = NULL;
1.61 + if (replaceLen > searchLen)
1.62 + {
1.63 + /* First, see if we have enough space to do this operation */
1.64 + if (maxLen - textLen < replaceLen - searchLen)
1.65 + {
1.66 + /* First, see if the replacement length goes out of bounds. */
1.67 + if (browsed + replaceLen >= maxLen)
1.68 + {
1.69 + /* EXAMPLE CASE:
1.70 + * Subject: AABBBCCC
1.71 + * Buffer : 12 bytes
1.72 + * Search : BBB
1.73 + * Replace: DDDDDDDDDD
1.74 + * OUTPUT : AADDDDDDDDD
1.75 + * POSITION: ^
1.76 + */
1.77 + /* If it does, we'll just bound the length and do a strcpy. */
1.78 + replaceLen = maxLen - browsed;
1.79 + /* Note, we add one to the final result for the null terminator */
1.80 + strncopy(ptr, replace, replaceLen+1);
1.81 + } else {
1.82 + /* EXAMPLE CASE:
1.83 + * Subject: AABBBCCC
1.84 + * Buffer : 12 bytes
1.85 + * Search : BBB
1.86 + * Replace: DDDDDDD
1.87 + * OUTPUT : AADDDDDDDCC
1.88 + * POSITION: ^
1.89 + */
1.90 + /* We're going to have some bytes left over... */
1.91 + size_t origBytesToCopy = (textLen - (browsed + searchLen)) + 1;
1.92 + size_t realBytesToCopy = (maxLen - (browsed + replaceLen)) + 1;
1.93 + char *moveFrom = ptr + searchLen + (origBytesToCopy - realBytesToCopy);
1.94 + char *moveTo = ptr + replaceLen;
1.95 +
1.96 + /* First, move our old data out of the way. */
1.97 + memmove(moveTo, moveFrom, realBytesToCopy);
1.98 +
1.99 + /* Now, do our replacement. */
1.100 + memcpy(ptr, replace, replaceLen);
1.101 + }
1.102 + } else {
1.103 + /* EXAMPLE CASE:
1.104 + * Subject: AABBBCCC
1.105 + * Buffer : 12 bytes
1.106 + * Search : BBB
1.107 + * Replace: DDDD
1.108 + * OUTPUT : AADDDDCCC
1.109 + * POSITION: ^
1.110 + */
1.111 + /* Yes, we have enough space. Do a normal move operation. */
1.112 + char *moveFrom = ptr + searchLen;
1.113 + char *moveTo = ptr + replaceLen;
1.114 +
1.115 + /* First move our old data out of the way. */
1.116 + size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
1.117 + memmove(moveTo, moveFrom, bytesToCopy);
1.118 +
1.119 + /* Now do our replacement. */
1.120 + memcpy(ptr, replace, replaceLen);
1.121 + }
1.122 + } else if (replaceLen < searchLen) {
1.123 + /* EXAMPLE CASE:
1.124 + * Subject: AABBBCCC
1.125 + * Buffer : 12 bytes
1.126 + * Search : BBB
1.127 + * Replace: D
1.128 + * OUTPUT : AADCCC
1.129 + * POSOTION: ^
1.130 + */
1.131 + /* If the replacement does not grow the string length, we do not
1.132 + * need to do any fancy checking at all. Yay!
1.133 + */
1.134 + char *moveFrom = ptr + searchLen; /* Start after the search pointer */
1.135 + char *moveTo = ptr + replaceLen; /* Copy to where the replacement ends */
1.136 +
1.137 + /* Copy our replacement in, if any */
1.138 + if (replaceLen)
1.139 + {
1.140 + memcpy(ptr, replace, replaceLen);
1.141 + }
1.142 +
1.143 + /* Figure out how many bytes to move down, including null terminator */
1.144 + size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
1.145 +
1.146 + /* Move the rest of the string down */
1.147 + memmove(moveTo, moveFrom, bytesToCopy);
1.148 + } else {
1.149 + /* EXAMPLE CASE:
1.150 + * Subject: AABBBCCC
1.151 + * Buffer : 12 bytes
1.152 + * Search : BBB
1.153 + * Replace: DDD
1.154 + * OUTPUT : AADDDCCC
1.155 + * POSITION: ^
1.156 + */
1.157 + /* We don't have to move anything around, just do a straight copy */
1.158 + memcpy(ptr, replace, replaceLen);
1.159 + }
1.160 +
1.161 + return ptr + replaceLen;
1.162 + }
1.163 + ptr++;
1.164 + browsed++;
1.165 + }
1.166 +
1.167 + return NULL;
1.168 +}