added ReplaceString and ReplaceStringEx (phew!)
--HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40839
This commit is contained in:
parent
3f68917040
commit
0ef8935839
@ -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;
|
||||
}
|
||||
|
@ -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_
|
||||
|
@ -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},
|
||||
};
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user