core/sm_stringutil.cpp
changeset 833 732bedfcbe3e
parent 732 13f3a417a340
child 835 4c9cf82967bd
     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 +}