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