core/logic/stringutil.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Thu Jul 04 11:28:32 2013 -0400 (2013-07-04)
changeset 3931 576fa42454dc
parent 3930 bf78b97106e5
permissions -rw-r--r--
Fixed build for real.
     1 /**
     2  * vim: set ts=4 sw=4 tw=99 et :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2009 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <string.h>
    33 #include <stdio.h>
    34 #include <ctype.h>
    35 #include <sm_platform.h>
    36 #include "stringutil.h"
    37 
    38 // We're in logic so we don't have this from the SDK.
    39 #ifndef MIN
    40 #define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) )
    41 #endif
    42 
    43 const char *stristr(const char *str, const char *substr)
    44 {
    45 	if (!*substr)
    46 	{
    47 		return ((char *)str);
    48 	}
    49 
    50 	char *needle = (char *)substr;
    51 	char *prevloc = (char *)str;
    52 	char *haystack = (char *)str;
    53 
    54 	while (*haystack)
    55 	{
    56 		if (tolower(*haystack) == tolower(*needle))
    57 		{
    58 			haystack++;
    59 			if (!*++needle)
    60 			{
    61 				return prevloc;
    62 			}
    63 		}
    64 		else
    65 		{
    66 			haystack = ++prevloc;
    67 			needle = (char *)substr;
    68 		}
    69 	}
    70 
    71 	return NULL;
    72 }
    73 
    74 unsigned int strncopy(char *dest, const char *src, size_t count)
    75 {
    76 	if (!count)
    77 	{
    78 		return 0;
    79 	}
    80 
    81 	char *start = dest;
    82 	while ((*src) && (--count))
    83 	{
    84 		*dest++ = *src++;
    85 	}
    86 	*dest = '\0';
    87 
    88 	return (dest - start);
    89 }
    90 
    91 unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace, bool caseSensitive)
    92 {
    93 	size_t searchLen = strlen(search);
    94 	size_t replaceLen = strlen(replace);
    95 
    96 	char *ptr = subject;
    97 	unsigned int total = 0;
    98 	while ((ptr = UTIL_ReplaceEx(ptr, maxlength, search, searchLen, replace, replaceLen, caseSensitive)) != NULL)
    99 	{
   100 		total++;
   101 		if (*ptr == '\0')
   102 		{
   103 			break;
   104 		}
   105 	}
   106 
   107 	return total;
   108 }
   109 
   110 /**
   111  * NOTE: Do not edit this for the love of god unless you have 
   112  * read the test cases and understand the code behind each one.
   113  * While I don't guarantee there aren't mistakes, I do guarantee
   114  * that plugins will end up relying on tiny idiosyncrasies of this
   115  * function, just like they did with AMX Mod X.
   116  *
   117  * There are explicitly more cases than the AMX Mod X version because
   118  * we're not doing a blind copy.  Each case is specifically optimized
   119  * for what needs to be done.  Even better, we don't have to error on
   120  * bad buffer sizes.  Instead, this function will smartly cut off the 
   121  * string in a way that pushes old data out.
   122  */
   123 char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen, bool caseSensitive)
   124 {
   125 	char *ptr = subject;
   126 	size_t browsed = 0;
   127 	size_t textLen = strlen(subject);
   128 
   129 	/* It's not possible to search or replace */
   130 	if (searchLen > textLen)
   131 	{
   132 		return NULL;
   133 	}
   134 
   135 	/* Handle the case of one byte replacement.
   136 	 * It's only valid in one case.
   137 	 */
   138 	if (maxLen == 1)
   139 	{
   140 		/* If the search matches and the replace length is 0, 
   141 		 * we can just terminate the string and be done.
   142 		 */
   143 		if ((caseSensitive ? strcmp(subject, search) : strcasecmp(subject, search)) == 0 && replaceLen == 0)
   144 		{
   145 			*subject = '\0';
   146 			return subject;
   147 		}
   148 		else
   149 		{
   150 			return NULL;
   151 		}
   152 	}
   153 
   154 	/* Subtract one off the maxlength so we can include the null terminator */
   155 	maxLen--;
   156 
   157 	while (*ptr != '\0' && (browsed <= textLen - searchLen))
   158 	{
   159 		/* See if we get a comparison */
   160 		if ((caseSensitive ? strncmp(ptr, search, searchLen) : strncasecmp(ptr, search, searchLen)) == 0)
   161 		{
   162 			if (replaceLen > searchLen)
   163 			{
   164 				/* First, see if we have enough space to do this operation */
   165 				if (maxLen - textLen < replaceLen - searchLen)
   166 				{
   167 					/* First, see if the replacement length goes out of bounds. */
   168 					if (browsed + replaceLen >= maxLen)
   169 					{
   170 						/* EXAMPLE CASE:
   171 						 * Subject: AABBBCCC
   172 						 * Buffer : 12 bytes
   173 						 * Search : BBB
   174 						 * Replace: DDDDDDDDDD
   175 						 * OUTPUT : AADDDDDDDDD
   176 						 * POSITION:           ^
   177 						 */
   178 						/* If it does, we'll just bound the length and do a strcpy. */
   179 						replaceLen = maxLen - browsed;
   180 						/* Note, we add one to the final result for the null terminator */
   181 						strncopy(ptr, replace, replaceLen+1);
   182 					}
   183 					else
   184 					{
   185 						/* EXAMPLE CASE:
   186 						 * Subject: AABBBCCC
   187 						 * Buffer : 12 bytes
   188 						 * Search : BBB
   189 						 * Replace: DDDDDDD
   190 						 * OUTPUT : AADDDDDDDCC
   191 						 * POSITION:         ^
   192 						 */
   193 						/* We're going to have some bytes left over... */
   194 						size_t origBytesToCopy = (textLen - (browsed + searchLen)) + 1;
   195 						size_t realBytesToCopy = (maxLen - (browsed + replaceLen)) + 1;
   196 						char *moveFrom = ptr + searchLen + (origBytesToCopy - realBytesToCopy);
   197 						char *moveTo = ptr + replaceLen;
   198 
   199 						/* First, move our old data out of the way. */
   200 						memmove(moveTo, moveFrom, realBytesToCopy);
   201 
   202 						/* Now, do our replacement. */
   203 						memcpy(ptr, replace, replaceLen);
   204 					}
   205 				}
   206 				else
   207 				{
   208 					/* EXAMPLE CASE:
   209 					 * Subject: AABBBCCC
   210 					 * Buffer : 12 bytes
   211 					 * Search : BBB
   212 					 * Replace: DDDD
   213 					 * OUTPUT : AADDDDCCC
   214 					 * POSITION:      ^
   215 					 */
   216 					/* Yes, we have enough space.  Do a normal move operation. */
   217 					char *moveFrom = ptr + searchLen;
   218 					char *moveTo = ptr + replaceLen;
   219 
   220 					/* First move our old data out of the way. */
   221 					size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
   222 					memmove(moveTo, moveFrom, bytesToCopy);
   223 
   224 					/* Now do our replacement. */
   225 					memcpy(ptr, replace, replaceLen);
   226 				}
   227 			}
   228 			else if (replaceLen < searchLen)
   229 			{
   230 				/* EXAMPLE CASE:
   231 				 * Subject: AABBBCCC
   232 				 * Buffer : 12 bytes
   233 				 * Search : BBB
   234 				 * Replace: D
   235 				 * OUTPUT : AADCCC
   236 				 * POSITION:   ^
   237 				 */
   238 				/* If the replacement does not grow the string length, we do not
   239 				 * need to do any fancy checking at all.  Yay!
   240 				 */
   241 				char *moveFrom = ptr + searchLen;		/* Start after the search pointer */
   242 				char *moveTo = ptr + replaceLen;		/* Copy to where the replacement ends */
   243 
   244 				/* Copy our replacement in, if any */
   245 				if (replaceLen)
   246 				{
   247 					memcpy(ptr, replace, replaceLen);
   248 				}
   249 
   250 				/* Figure out how many bytes to move down, including null terminator */
   251 				size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
   252 
   253 				/* Move the rest of the string down */
   254 				memmove(moveTo, moveFrom, bytesToCopy);
   255 			}
   256 			else
   257 			{
   258 				/* EXAMPLE CASE:
   259 				 * Subject: AABBBCCC
   260 				 * Buffer : 12 bytes
   261 				 * Search : BBB
   262 				 * Replace: DDD
   263 				 * OUTPUT : AADDDCCC
   264 				 * POSITION:     ^
   265 				 */
   266 				/* We don't have to move anything around, just do a straight copy */
   267 				memcpy(ptr, replace, replaceLen);
   268 			}
   269 
   270 			return ptr + replaceLen;
   271 		}
   272 		ptr++;
   273 		browsed++;
   274 	}
   275 
   276 	return NULL;
   277 }
   278 
   279 size_t UTIL_DecodeHexString(unsigned char *buffer, size_t maxlength, const char *hexstr)
   280 {
   281 	size_t written = 0;
   282 	size_t length = strlen(hexstr);
   283 
   284 	for (size_t i = 0; i < length; i++)
   285 	{
   286 		if (written >= maxlength)
   287 			break;
   288 		buffer[written++] = hexstr[i];
   289 		if (hexstr[i] == '\\' && hexstr[i + 1] == 'x')
   290 		{
   291 			if (i + 3 >= length)
   292 				continue;
   293 			/* Get the hex part. */
   294 			char s_byte[3];
   295 			int r_byte;
   296 			s_byte[0] = hexstr[i + 2];
   297 			s_byte[1] = hexstr[i + 3];
   298 			s_byte[2] = '\0';
   299 			/* Read it as an integer */
   300 			sscanf(s_byte, "%x", &r_byte);
   301 			/* Save the value */
   302 			buffer[written - 1] = r_byte;
   303 			/* Adjust index */
   304 			i += 3;
   305 		}
   306 	}
   307 
   308 	return written;
   309 }
   310 
   311 #define PATHSEPARATOR(c) ((c) == '\\' || (c) == '/')
   312 
   313 void UTIL_StripExtension(const char *in, char *out, int outSize)
   314 {
   315 	// Find the last dot. If it's followed by a dot or a slash, then it's part of a 
   316 	// directory specifier like ../../somedir/./blah.
   317 
   318 	// scan backward for '.'
   319 	int end = strlen(in) - 1;
   320 	while (end > 0 && in[end] != '.' && !PATHSEPARATOR(in[end]))
   321 	{
   322 		--end;
   323 	}
   324 
   325 	if (end > 0 && !PATHSEPARATOR(in[end]) && end < outSize)
   326 	{
   327 		int nChars = MIN(end, outSize-1);
   328 		if (out != in)
   329 		{
   330 			memcpy(out, in, nChars);
   331 		}
   332 		out[nChars] = 0;
   333 	}
   334 	else
   335 	{
   336 		// nothing found
   337 		if (out != in)
   338 		{
   339 			strncopy(out, in, outSize);
   340 		}
   341 	}
   342 }