added ReplaceString and ReplaceStringEx (phew!)

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40839
This commit is contained in:
David Anderson 2007-05-22 20:50:48 +00:00
parent 3f68917040
commit 0ef8935839
4 changed files with 238 additions and 5 deletions

View File

@ -910,3 +910,165 @@ char *sm_strdup(const char *str)
strcpy(ptr, str);
return ptr;
}
unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace)
{
size_t searchLen = strlen(search);
size_t replaceLen = strlen(replace);
char *ptr = subject;
unsigned int total = 0;
while ((ptr = UTIL_ReplaceEx(ptr, maxlength, search, searchLen, replace, replaceLen)) != NULL)
{
total++;
if (*ptr == '\0')
{
break;
}
}
return total;
}
/**
* NOTE: Do not edit this for the love of god unless you have
* read the test cases and understand the code behind each one.
* While I don't guarantee there aren't mistakes, I do guarantee
* that plugins will end up relying on tiny idiosyncracies of this
* function, just like they did with AMX Mod X.
*
* There are explicitly more cases than the AMX Mod X version because
* we're not doing a blind copy. Each case is specifically optimized
* for what needs to be done. Even better, we don't have to error on
* bad buffer sizes. Instead, this function will smartly cut off the
* string in a way that pushes old data out.
*/
char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen)
{
char *ptr = subject;
size_t browsed = 0;
size_t textLen = strlen(subject);
/* It's not possible to search or replace */
if (searchLen > textLen || maxLen <= 1)
{
return NULL;
}
/* Subtract one off the maxlength so we can include the null terminator */
maxLen--;
while (*ptr != '\0' && (browsed <= textLen - searchLen))
{
/* See if we get a comparison */
if (strncmp(ptr, search, searchLen) == 0)
{
char *retPtr = NULL;
if (replaceLen > searchLen)
{
/* First, see if we have enough space to do this operation */
if (maxLen - textLen < replaceLen - searchLen)
{
/* First, see if the replacement length goes out of bounds. */
if (browsed + replaceLen >= maxLen)
{
/* EXAMPLE CASE:
* Subject: AABBBCCC
* Buffer : 12 bytes
* Search : BBB
* Replace: DDDDDDDDDD
* OUTPUT : AADDDDDDDDD
* POSITION: ^
*/
/* If it does, we'll just bound the length and do a strcpy. */
replaceLen = maxLen - browsed;
/* Note, we add one to the final result for the null terminator */
strncopy(ptr, replace, replaceLen+1);
} else {
/* EXAMPLE CASE:
* Subject: AABBBCCC
* Buffer : 12 bytes
* Search : BBB
* Replace: DDDDDDD
* OUTPUT : AADDDDDDDCC
* POSITION: ^
*/
/* We're going to have some bytes left over... */
size_t origBytesToCopy = (textLen - (browsed + searchLen)) + 1;
size_t realBytesToCopy = (maxLen - (browsed + replaceLen)) + 1;
char *moveFrom = ptr + searchLen + (origBytesToCopy - realBytesToCopy);
char *moveTo = ptr + replaceLen;
/* First, move our old data out of the way. */
memmove(moveTo, moveFrom, realBytesToCopy);
/* Now, do our replacement. */
memcpy(ptr, replace, replaceLen);
}
} else {
/* EXAMPLE CASE:
* Subject: AABBBCCC
* Buffer : 12 bytes
* Search : BBB
* Replace: DDDD
* OUTPUT : AADDDDCCC
* POSITION: ^
*/
/* Yes, we have enough space. Do a normal move operation. */
char *moveFrom = ptr + searchLen;
char *moveTo = ptr + replaceLen;
/* First move our old data out of the way. */
size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
memmove(moveTo, moveFrom, bytesToCopy);
/* Now do our replacement. */
memcpy(ptr, replace, replaceLen);
}
} else if (replaceLen < searchLen) {
/* EXAMPLE CASE:
* Subject: AABBBCCC
* Buffer : 12 bytes
* Search : BBB
* Replace: D
* OUTPUT : AADCCC
* POSOTION: ^
*/
/* If the replacement does not grow the string length, we do not
* need to do any fancy checking at all. Yay!
*/
char *moveFrom = ptr + searchLen; /* Start after the search pointer */
char *moveTo = ptr + replaceLen; /* Copy to where the replacement ends */
/* Copy our replacement in, if any */
if (replaceLen)
{
memcpy(ptr, replace, replaceLen);
}
/* Figure out how many bytes to move down, including null terminator */
size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
/* Move the rest of the string down */
memmove(moveTo, moveFrom, bytesToCopy);
} else {
/* EXAMPLE CASE:
* Subject: AABBBCCC
* Buffer : 12 bytes
* Search : BBB
* Replace: DDD
* OUTPUT : AADDDCCC
* POSITION: ^
*/
/* We don't have to move anything around, just do a straight copy */
memcpy(ptr, replace, replaceLen);
}
return ptr + replaceLen;
}
ptr++;
browsed++;
}
return NULL;
}

View File

@ -33,5 +33,7 @@ size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...);
size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap);
char *sm_strdup(const char *str);
size_t CorePlayerTranslate(int client, char *buffer, size_t maxlength, const char *phrase, void **params);
unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace);
char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen);
#endif // _INCLUDE_SOURCEMOD_STRINGUTIL_H_

View File

@ -407,6 +407,43 @@ static cell_t IsCharLower(IPluginContext *pContext, const cell_t *params)
return islower(chr);
}
static cell_t ReplaceString(IPluginContext *pContext, const cell_t *params)
{
char *text, *search, *replace;
size_t maxlength;
pContext->LocalToString(params[1], &text);
pContext->LocalToString(params[3], &search);
pContext->LocalToString(params[4], &replace);
maxlength = (size_t)params[2];
return UTIL_ReplaceAll(text, maxlength, search, replace);
}
static cell_t ReplaceStringEx(IPluginContext *pContext, const cell_t *params)
{
char *text, *search, *replace;
size_t maxlength;
pContext->LocalToString(params[1], &text);
pContext->LocalToString(params[3], &search);
pContext->LocalToString(params[4], &replace);
maxlength = (size_t)params[2];
size_t searchLen = (params[5] == -1) ? strlen(search) : (size_t)params[5];
size_t replaceLen = (params[6] == -1) ? strlen(replace) : (size_t)params[6];
char *ptr = UTIL_ReplaceEx(text, maxlength, search, searchLen, replace, replaceLen);
if (ptr == NULL)
{
return -1;
}
return ptr - text;
}
static cell_t TrimString(IPluginContext *pContext, const cell_t *params)
{
char *str;
@ -444,6 +481,10 @@ static cell_t TrimString(IPluginContext *pContext, const cell_t *params)
REGISTER_NATIVES(basicStrings)
{
{"BreakString", BreakString},
{"FloatToString", sm_floattostr},
{"Format", sm_format},
{"FormatEx", sm_formatex},
{"GetCharBytes", GetCharBytes},
{"IntToString", sm_numtostr},
{"IsCharAlpha", IsCharAlpha},
@ -452,9 +493,10 @@ REGISTER_NATIVES(basicStrings)
{"IsCharNumeric", IsCharNumeric},
{"IsCharSpace", IsCharSpace},
{"IsCharUpper", IsCharUpper},
{"ReplaceString", ReplaceString},
{"ReplaceStringEx", ReplaceStringEx},
{"strlen", sm_strlen},
{"StrBreak", BreakString}, /* Backwards compat shim */
{"BreakString", BreakString},
{"StrContains", sm_contain},
{"strcmp", sm_strcmp},
{"StrCompare", sm_strcmp}, /* Backwards compat shim */
@ -463,10 +505,7 @@ REGISTER_NATIVES(basicStrings)
{"StrCopy", sm_strcopy}, /* Backwards compat shim */
{"StringToInt", sm_strconvint},
{"StringToFloat", sm_strtofloat},
{"FloatToString", sm_floattostr},
{"Format", sm_format},
{"FormatEx", sm_formatex},
{"VFormat", sm_vformat},
{"TrimString", TrimString},
{"VFormat", sm_vformat},
{NULL, NULL},
};

View File

@ -216,6 +216,36 @@ stock StrBreak(const String:source[], String:arg[], argLen)
return BreakString(source, arg, argLen);
}
/**
* Given a string, replaces all occurrences of a search string with a
* replacement string.
*
* @param text String to perform search and replacements on.
* @param maxlength Maximum length of the string buffer.
* @param search String to search for.
* @param replace String to replace the search string with.
* @return Number of replacements that were performed.
*/
native ReplaceString(String:text[], maxlength, const String:search[], const String:replace[]);
/**
* Given a string, replaces the first occurrence of a search string with a
* replacement string.
*
* @param text String to perform search and replacements on.
* @param maxlength Maximum length of the string buffer.
* @param search String to search for.
* @param replace String to replace the search string with.
* @param searchLen If higher than -1, its value will be used instead of
* a strlen() call on the search parameter.
* @param replaceLen If higher than -1, its value will be used instead of
* a strlen() call on the replace parameter.
* @return Index into the buffer (relative to the start) from where
* the last replacement ended, or -1 if no replacements were
* made.
*/
native ReplaceStringEx(String:text[], maxlength, const String:search[], const String:replace[], searchLen=-1, replaceLen=-1);
/**
* Returns the number of bytes a character is using. This is
* for multi-byte characters (UTF-8). For normal ASCII characters,