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