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);
|
strcpy(ptr, str);
|
||||||
return ptr;
|
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);
|
size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap);
|
||||||
char *sm_strdup(const char *str);
|
char *sm_strdup(const char *str);
|
||||||
size_t CorePlayerTranslate(int client, char *buffer, size_t maxlength, const char *phrase, void **params);
|
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_
|
#endif // _INCLUDE_SOURCEMOD_STRINGUTIL_H_
|
||||||
|
@ -407,6 +407,43 @@ static cell_t IsCharLower(IPluginContext *pContext, const cell_t *params)
|
|||||||
return islower(chr);
|
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)
|
static cell_t TrimString(IPluginContext *pContext, const cell_t *params)
|
||||||
{
|
{
|
||||||
char *str;
|
char *str;
|
||||||
@ -444,6 +481,10 @@ static cell_t TrimString(IPluginContext *pContext, const cell_t *params)
|
|||||||
|
|
||||||
REGISTER_NATIVES(basicStrings)
|
REGISTER_NATIVES(basicStrings)
|
||||||
{
|
{
|
||||||
|
{"BreakString", BreakString},
|
||||||
|
{"FloatToString", sm_floattostr},
|
||||||
|
{"Format", sm_format},
|
||||||
|
{"FormatEx", sm_formatex},
|
||||||
{"GetCharBytes", GetCharBytes},
|
{"GetCharBytes", GetCharBytes},
|
||||||
{"IntToString", sm_numtostr},
|
{"IntToString", sm_numtostr},
|
||||||
{"IsCharAlpha", IsCharAlpha},
|
{"IsCharAlpha", IsCharAlpha},
|
||||||
@ -452,9 +493,10 @@ REGISTER_NATIVES(basicStrings)
|
|||||||
{"IsCharNumeric", IsCharNumeric},
|
{"IsCharNumeric", IsCharNumeric},
|
||||||
{"IsCharSpace", IsCharSpace},
|
{"IsCharSpace", IsCharSpace},
|
||||||
{"IsCharUpper", IsCharUpper},
|
{"IsCharUpper", IsCharUpper},
|
||||||
|
{"ReplaceString", ReplaceString},
|
||||||
|
{"ReplaceStringEx", ReplaceStringEx},
|
||||||
{"strlen", sm_strlen},
|
{"strlen", sm_strlen},
|
||||||
{"StrBreak", BreakString}, /* Backwards compat shim */
|
{"StrBreak", BreakString}, /* Backwards compat shim */
|
||||||
{"BreakString", BreakString},
|
|
||||||
{"StrContains", sm_contain},
|
{"StrContains", sm_contain},
|
||||||
{"strcmp", sm_strcmp},
|
{"strcmp", sm_strcmp},
|
||||||
{"StrCompare", sm_strcmp}, /* Backwards compat shim */
|
{"StrCompare", sm_strcmp}, /* Backwards compat shim */
|
||||||
@ -463,10 +505,7 @@ REGISTER_NATIVES(basicStrings)
|
|||||||
{"StrCopy", sm_strcopy}, /* Backwards compat shim */
|
{"StrCopy", sm_strcopy}, /* Backwards compat shim */
|
||||||
{"StringToInt", sm_strconvint},
|
{"StringToInt", sm_strconvint},
|
||||||
{"StringToFloat", sm_strtofloat},
|
{"StringToFloat", sm_strtofloat},
|
||||||
{"FloatToString", sm_floattostr},
|
|
||||||
{"Format", sm_format},
|
|
||||||
{"FormatEx", sm_formatex},
|
|
||||||
{"VFormat", sm_vformat},
|
|
||||||
{"TrimString", TrimString},
|
{"TrimString", TrimString},
|
||||||
|
{"VFormat", sm_vformat},
|
||||||
{NULL, NULL},
|
{NULL, NULL},
|
||||||
};
|
};
|
||||||
|
@ -216,6 +216,36 @@ stock StrBreak(const String:source[], String:arg[], argLen)
|
|||||||
return BreakString(source, 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
|
* Returns the number of bytes a character is using. This is
|
||||||
* for multi-byte characters (UTF-8). For normal ASCII characters,
|
* for multi-byte characters (UTF-8). For normal ASCII characters,
|
||||||
|
Loading…
Reference in New Issue
Block a user