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