2010-05-15 04:28:10 +02:00
|
|
|
/**
|
|
|
|
* vim: set ts=4 sw=4 tw=99 et :
|
|
|
|
* =============================================================================
|
|
|
|
* SourceMod
|
|
|
|
* Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
|
|
|
|
* =============================================================================
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it under
|
|
|
|
* the terms of the GNU General Public License, version 3.0, as published by the
|
|
|
|
* Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
|
|
* details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with
|
|
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
* As a special exception, AlliedModders LLC gives you permission to link the
|
|
|
|
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
|
|
|
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
|
|
|
* by the Valve Corporation. You must obey the GNU General Public License in
|
|
|
|
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
|
|
|
* this exception to all derivative works. AlliedModders LLC defines further
|
|
|
|
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
|
|
|
* or <http://www.sourcemod.net/license.php>.
|
|
|
|
*
|
|
|
|
* Version: $Id$
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
2010-05-15 08:35:42 +02:00
|
|
|
#include <stdio.h>
|
2010-05-15 04:28:10 +02:00
|
|
|
#include <ctype.h>
|
2010-05-15 05:56:40 +02:00
|
|
|
#include <sm_platform.h>
|
2010-05-15 04:28:10 +02:00
|
|
|
#include "stringutil.h"
|
|
|
|
|
|
|
|
const char *stristr(const char *str, const char *substr)
|
|
|
|
{
|
|
|
|
if (!*substr)
|
|
|
|
{
|
|
|
|
return ((char *)str);
|
|
|
|
}
|
|
|
|
|
|
|
|
char *needle = (char *)substr;
|
|
|
|
char *prevloc = (char *)str;
|
|
|
|
char *haystack = (char *)str;
|
|
|
|
|
|
|
|
while (*haystack)
|
|
|
|
{
|
|
|
|
if (tolower(*haystack) == tolower(*needle))
|
|
|
|
{
|
|
|
|
haystack++;
|
|
|
|
if (!*++needle)
|
|
|
|
{
|
|
|
|
return prevloc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
haystack = ++prevloc;
|
|
|
|
needle = (char *)substr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-05-15 04:43:53 +02:00
|
|
|
unsigned int strncopy(char *dest, const char *src, size_t count)
|
|
|
|
{
|
|
|
|
if (!count)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *start = dest;
|
|
|
|
while ((*src) && (--count))
|
|
|
|
{
|
|
|
|
*dest++ = *src++;
|
|
|
|
}
|
|
|
|
*dest = '\0';
|
|
|
|
|
|
|
|
return (dest - start);
|
|
|
|
}
|
|
|
|
|
2010-05-15 05:04:30 +02:00
|
|
|
unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace, bool caseSensitive)
|
|
|
|
{
|
|
|
|
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, caseSensitive)) != 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 idiosyncrasies 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, bool caseSensitive)
|
|
|
|
{
|
|
|
|
char *ptr = subject;
|
|
|
|
size_t browsed = 0;
|
|
|
|
size_t textLen = strlen(subject);
|
|
|
|
|
|
|
|
/* It's not possible to search or replace */
|
|
|
|
if (searchLen > textLen)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle the case of one byte replacement.
|
|
|
|
* It's only valid in one case.
|
|
|
|
*/
|
|
|
|
if (maxLen == 1)
|
|
|
|
{
|
|
|
|
/* If the search matches and the replace length is 0,
|
|
|
|
* we can just terminate the string and be done.
|
|
|
|
*/
|
|
|
|
if ((caseSensitive ? strcmp(subject, search) : strcasecmp(subject, search)) == 0 && replaceLen == 0)
|
|
|
|
{
|
|
|
|
*subject = '\0';
|
|
|
|
return subject;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
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 ((caseSensitive ? strncmp(ptr, search, searchLen) : strncasecmp(ptr, search, searchLen)) == 0)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
* POSITION: ^
|
|
|
|
*/
|
|
|
|
/* 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;
|
|
|
|
}
|
2010-05-15 04:28:10 +02:00
|
|
|
|
2010-05-15 08:35:42 +02:00
|
|
|
size_t UTIL_DecodeHexString(unsigned char *buffer, size_t maxlength, const char *hexstr)
|
|
|
|
{
|
|
|
|
size_t written = 0;
|
|
|
|
size_t length = strlen(hexstr);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < length; i++)
|
|
|
|
{
|
|
|
|
if (written >= maxlength)
|
|
|
|
break;
|
|
|
|
buffer[written++] = hexstr[i];
|
|
|
|
if (hexstr[i] == '\\' && hexstr[i + 1] == 'x')
|
|
|
|
{
|
|
|
|
if (i + 3 >= length)
|
|
|
|
continue;
|
|
|
|
/* Get the hex part. */
|
|
|
|
char s_byte[3];
|
|
|
|
int r_byte;
|
|
|
|
s_byte[0] = hexstr[i + 2];
|
|
|
|
s_byte[1] = hexstr[i + 3];
|
|
|
|
s_byte[2] = '\0';
|
|
|
|
/* Read it as an integer */
|
|
|
|
sscanf(s_byte, "%x", &r_byte);
|
|
|
|
/* Save the value */
|
|
|
|
buffer[written - 1] = r_byte;
|
|
|
|
/* Adjust index */
|
|
|
|
i += 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return written;
|
|
|
|
}
|
|
|
|
|
2013-07-04 05:14:55 +02:00
|
|
|
#define PATHSEPARATOR(c) ((c) == '\\' || (c) == '/')
|
|
|
|
|
|
|
|
void UTIL_StripExtension(const char *in, char *out, int outSize)
|
|
|
|
{
|
|
|
|
// Find the last dot. If it's followed by a dot or a slash, then it's part of a
|
|
|
|
// directory specifier like ../../somedir/./blah.
|
|
|
|
|
|
|
|
// scan backward for '.'
|
|
|
|
int end = strlen(in) - 1;
|
|
|
|
while (end > 0 && in[end] != '.' && !PATHSEPARATOR(in[end]))
|
|
|
|
{
|
|
|
|
--end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (end > 0 && !PATHSEPARATOR(in[end]) && end < outSize)
|
|
|
|
{
|
|
|
|
int nChars = min(end, outSize-1);
|
|
|
|
if (out != in)
|
|
|
|
{
|
|
|
|
memcpy(out, in, nChars);
|
|
|
|
}
|
|
|
|
out[nChars] = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// nothing found
|
|
|
|
if (out != in)
|
|
|
|
{
|
|
|
|
strncopy(out, in, outSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|