dvander@3002: /**
dvander@3002: * vim: set ts=4 sw=4 tw=99 et :
dvander@3002: * =============================================================================
dvander@3002: * SourceMod
dvander@3002: * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
dvander@3002: * =============================================================================
dvander@3002: *
dvander@3002: * This program is free software; you can redistribute it and/or modify it under
dvander@3002: * the terms of the GNU General Public License, version 3.0, as published by the
dvander@3002: * Free Software Foundation.
dvander@3002: *
dvander@3002: * This program is distributed in the hope that it will be useful, but WITHOUT
dvander@3002: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
dvander@3002: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
dvander@3002: * details.
dvander@3002: *
dvander@3002: * You should have received a copy of the GNU General Public License along with
dvander@3002: * this program. If not, see .
dvander@3002: *
dvander@3002: * As a special exception, AlliedModders LLC gives you permission to link the
dvander@3002: * code of this program (as well as its derivative works) to "Half-Life 2," the
dvander@3002: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
dvander@3002: * by the Valve Corporation. You must obey the GNU General Public License in
dvander@3002: * all respects for all other code used. Additionally, AlliedModders LLC grants
dvander@3002: * this exception to all derivative works. AlliedModders LLC defines further
dvander@3002: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
dvander@3002: * or .
dvander@3002: *
dvander@3002: * Version: $Id$
dvander@3002: */
dvander@3002:
dvander@3002: #include
dvander@3010: #include
dvander@3002: #include
dvander@3007: #include
dvander@3002: #include "stringutil.h"
dvander@3002:
skamonkey@3931: // We're in logic so we don't have this from the SDK.
skamonkey@3931: #ifndef MIN
skamonkey@3931: #define MIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) )
skamonkey@3931: #endif
skamonkey@3931:
dvander@3002: const char *stristr(const char *str, const char *substr)
dvander@3002: {
dvander@3002: if (!*substr)
dvander@3002: {
dvander@3002: return ((char *)str);
dvander@3002: }
dvander@3002:
dvander@3002: char *needle = (char *)substr;
dvander@3002: char *prevloc = (char *)str;
dvander@3002: char *haystack = (char *)str;
dvander@3002:
dvander@3002: while (*haystack)
dvander@3002: {
dvander@3002: if (tolower(*haystack) == tolower(*needle))
dvander@3002: {
dvander@3002: haystack++;
dvander@3002: if (!*++needle)
dvander@3002: {
dvander@3002: return prevloc;
dvander@3002: }
dvander@3002: }
dvander@3002: else
dvander@3002: {
dvander@3002: haystack = ++prevloc;
dvander@3002: needle = (char *)substr;
dvander@3002: }
dvander@3002: }
dvander@3002:
dvander@3002: return NULL;
dvander@3002: }
dvander@3002:
dvander@3004: unsigned int strncopy(char *dest, const char *src, size_t count)
dvander@3004: {
dvander@3004: if (!count)
dvander@3004: {
dvander@3004: return 0;
dvander@3004: }
dvander@3002:
dvander@3004: char *start = dest;
dvander@3004: while ((*src) && (--count))
dvander@3004: {
dvander@3004: *dest++ = *src++;
dvander@3004: }
dvander@3004: *dest = '\0';
dvander@3004:
dvander@3004: return (dest - start);
dvander@3004: }
dvander@3004:
dvander@3005: unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace, bool caseSensitive)
dvander@3005: {
dvander@3005: size_t searchLen = strlen(search);
dvander@3005: size_t replaceLen = strlen(replace);
dvander@3004:
dvander@3005: char *ptr = subject;
dvander@3005: unsigned int total = 0;
dvander@3005: while ((ptr = UTIL_ReplaceEx(ptr, maxlength, search, searchLen, replace, replaceLen, caseSensitive)) != NULL)
dvander@3005: {
dvander@3005: total++;
dvander@3005: if (*ptr == '\0')
dvander@3005: {
dvander@3005: break;
dvander@3005: }
dvander@3005: }
dvander@3005:
dvander@3005: return total;
dvander@3005: }
dvander@3005:
dvander@3005: /**
dvander@3005: * NOTE: Do not edit this for the love of god unless you have
dvander@3005: * read the test cases and understand the code behind each one.
dvander@3005: * While I don't guarantee there aren't mistakes, I do guarantee
dvander@3005: * that plugins will end up relying on tiny idiosyncrasies of this
dvander@3005: * function, just like they did with AMX Mod X.
dvander@3005: *
dvander@3005: * There are explicitly more cases than the AMX Mod X version because
dvander@3005: * we're not doing a blind copy. Each case is specifically optimized
dvander@3005: * for what needs to be done. Even better, we don't have to error on
dvander@3005: * bad buffer sizes. Instead, this function will smartly cut off the
dvander@3005: * string in a way that pushes old data out.
dvander@3005: */
dvander@3005: char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen, bool caseSensitive)
dvander@3005: {
dvander@3005: char *ptr = subject;
dvander@3005: size_t browsed = 0;
dvander@3005: size_t textLen = strlen(subject);
dvander@3005:
dvander@3005: /* It's not possible to search or replace */
dvander@3005: if (searchLen > textLen)
dvander@3005: {
dvander@3005: return NULL;
dvander@3005: }
dvander@3005:
dvander@3005: /* Handle the case of one byte replacement.
dvander@3005: * It's only valid in one case.
dvander@3005: */
dvander@3005: if (maxLen == 1)
dvander@3005: {
dvander@3005: /* If the search matches and the replace length is 0,
dvander@3005: * we can just terminate the string and be done.
dvander@3005: */
dvander@3005: if ((caseSensitive ? strcmp(subject, search) : strcasecmp(subject, search)) == 0 && replaceLen == 0)
dvander@3005: {
dvander@3005: *subject = '\0';
dvander@3005: return subject;
dvander@3005: }
dvander@3005: else
dvander@3005: {
dvander@3005: return NULL;
dvander@3005: }
dvander@3005: }
dvander@3005:
dvander@3005: /* Subtract one off the maxlength so we can include the null terminator */
dvander@3005: maxLen--;
dvander@3005:
dvander@3005: while (*ptr != '\0' && (browsed <= textLen - searchLen))
dvander@3005: {
dvander@3005: /* See if we get a comparison */
dvander@3005: if ((caseSensitive ? strncmp(ptr, search, searchLen) : strncasecmp(ptr, search, searchLen)) == 0)
dvander@3005: {
dvander@3005: if (replaceLen > searchLen)
dvander@3005: {
dvander@3005: /* First, see if we have enough space to do this operation */
dvander@3005: if (maxLen - textLen < replaceLen - searchLen)
dvander@3005: {
dvander@3005: /* First, see if the replacement length goes out of bounds. */
dvander@3005: if (browsed + replaceLen >= maxLen)
dvander@3005: {
dvander@3005: /* EXAMPLE CASE:
dvander@3005: * Subject: AABBBCCC
dvander@3005: * Buffer : 12 bytes
dvander@3005: * Search : BBB
dvander@3005: * Replace: DDDDDDDDDD
dvander@3005: * OUTPUT : AADDDDDDDDD
dvander@3005: * POSITION: ^
dvander@3005: */
dvander@3005: /* If it does, we'll just bound the length and do a strcpy. */
dvander@3005: replaceLen = maxLen - browsed;
dvander@3005: /* Note, we add one to the final result for the null terminator */
dvander@3005: strncopy(ptr, replace, replaceLen+1);
dvander@3005: }
dvander@3005: else
dvander@3005: {
dvander@3005: /* EXAMPLE CASE:
dvander@3005: * Subject: AABBBCCC
dvander@3005: * Buffer : 12 bytes
dvander@3005: * Search : BBB
dvander@3005: * Replace: DDDDDDD
dvander@3005: * OUTPUT : AADDDDDDDCC
dvander@3005: * POSITION: ^
dvander@3005: */
dvander@3005: /* We're going to have some bytes left over... */
dvander@3005: size_t origBytesToCopy = (textLen - (browsed + searchLen)) + 1;
dvander@3005: size_t realBytesToCopy = (maxLen - (browsed + replaceLen)) + 1;
dvander@3005: char *moveFrom = ptr + searchLen + (origBytesToCopy - realBytesToCopy);
dvander@3005: char *moveTo = ptr + replaceLen;
dvander@3005:
dvander@3005: /* First, move our old data out of the way. */
dvander@3005: memmove(moveTo, moveFrom, realBytesToCopy);
dvander@3005:
dvander@3005: /* Now, do our replacement. */
dvander@3005: memcpy(ptr, replace, replaceLen);
dvander@3005: }
dvander@3005: }
dvander@3005: else
dvander@3005: {
dvander@3005: /* EXAMPLE CASE:
dvander@3005: * Subject: AABBBCCC
dvander@3005: * Buffer : 12 bytes
dvander@3005: * Search : BBB
dvander@3005: * Replace: DDDD
dvander@3005: * OUTPUT : AADDDDCCC
dvander@3005: * POSITION: ^
dvander@3005: */
dvander@3005: /* Yes, we have enough space. Do a normal move operation. */
dvander@3005: char *moveFrom = ptr + searchLen;
dvander@3005: char *moveTo = ptr + replaceLen;
dvander@3005:
dvander@3005: /* First move our old data out of the way. */
dvander@3005: size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
dvander@3005: memmove(moveTo, moveFrom, bytesToCopy);
dvander@3005:
dvander@3005: /* Now do our replacement. */
dvander@3005: memcpy(ptr, replace, replaceLen);
dvander@3005: }
dvander@3005: }
dvander@3005: else if (replaceLen < searchLen)
dvander@3005: {
dvander@3005: /* EXAMPLE CASE:
dvander@3005: * Subject: AABBBCCC
dvander@3005: * Buffer : 12 bytes
dvander@3005: * Search : BBB
dvander@3005: * Replace: D
dvander@3005: * OUTPUT : AADCCC
dvander@3005: * POSITION: ^
dvander@3005: */
dvander@3005: /* If the replacement does not grow the string length, we do not
dvander@3005: * need to do any fancy checking at all. Yay!
dvander@3005: */
dvander@3005: char *moveFrom = ptr + searchLen; /* Start after the search pointer */
dvander@3005: char *moveTo = ptr + replaceLen; /* Copy to where the replacement ends */
dvander@3005:
dvander@3005: /* Copy our replacement in, if any */
dvander@3005: if (replaceLen)
dvander@3005: {
dvander@3005: memcpy(ptr, replace, replaceLen);
dvander@3005: }
dvander@3005:
dvander@3005: /* Figure out how many bytes to move down, including null terminator */
dvander@3005: size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
dvander@3005:
dvander@3005: /* Move the rest of the string down */
dvander@3005: memmove(moveTo, moveFrom, bytesToCopy);
dvander@3005: }
dvander@3005: else
dvander@3005: {
dvander@3005: /* EXAMPLE CASE:
dvander@3005: * Subject: AABBBCCC
dvander@3005: * Buffer : 12 bytes
dvander@3005: * Search : BBB
dvander@3005: * Replace: DDD
dvander@3005: * OUTPUT : AADDDCCC
dvander@3005: * POSITION: ^
dvander@3005: */
dvander@3005: /* We don't have to move anything around, just do a straight copy */
dvander@3005: memcpy(ptr, replace, replaceLen);
dvander@3005: }
dvander@3005:
dvander@3005: return ptr + replaceLen;
dvander@3005: }
dvander@3005: ptr++;
dvander@3005: browsed++;
dvander@3005: }
dvander@3005:
dvander@3005: return NULL;
dvander@3005: }
dvander@3005:
dvander@3010: size_t UTIL_DecodeHexString(unsigned char *buffer, size_t maxlength, const char *hexstr)
dvander@3010: {
dvander@3010: size_t written = 0;
dvander@3010: size_t length = strlen(hexstr);
dvander@3010:
dvander@3010: for (size_t i = 0; i < length; i++)
dvander@3010: {
dvander@3010: if (written >= maxlength)
dvander@3010: break;
dvander@3010: buffer[written++] = hexstr[i];
dvander@3010: if (hexstr[i] == '\\' && hexstr[i + 1] == 'x')
dvander@3010: {
dvander@3010: if (i + 3 >= length)
dvander@3010: continue;
dvander@3010: /* Get the hex part. */
dvander@3010: char s_byte[3];
dvander@3010: int r_byte;
dvander@3010: s_byte[0] = hexstr[i + 2];
dvander@3010: s_byte[1] = hexstr[i + 3];
dvander@3010: s_byte[2] = '\0';
dvander@3010: /* Read it as an integer */
dvander@3010: sscanf(s_byte, "%x", &r_byte);
dvander@3010: /* Save the value */
dvander@3010: buffer[written - 1] = r_byte;
dvander@3010: /* Adjust index */
dvander@3010: i += 3;
dvander@3010: }
dvander@3010: }
dvander@3010:
dvander@3010: return written;
dvander@3010: }
dvander@3010:
voidedweasel@3929: #define PATHSEPARATOR(c) ((c) == '\\' || (c) == '/')
voidedweasel@3929:
voidedweasel@3929: void UTIL_StripExtension(const char *in, char *out, int outSize)
voidedweasel@3929: {
voidedweasel@3929: // Find the last dot. If it's followed by a dot or a slash, then it's part of a
voidedweasel@3929: // directory specifier like ../../somedir/./blah.
voidedweasel@3929:
voidedweasel@3929: // scan backward for '.'
voidedweasel@3929: int end = strlen(in) - 1;
voidedweasel@3929: while (end > 0 && in[end] != '.' && !PATHSEPARATOR(in[end]))
voidedweasel@3929: {
voidedweasel@3929: --end;
voidedweasel@3929: }
voidedweasel@3929:
voidedweasel@3929: if (end > 0 && !PATHSEPARATOR(in[end]) && end < outSize)
voidedweasel@3929: {
skamonkey@3930: int nChars = MIN(end, outSize-1);
voidedweasel@3929: if (out != in)
voidedweasel@3929: {
voidedweasel@3929: memcpy(out, in, nChars);
voidedweasel@3929: }
voidedweasel@3929: out[nChars] = 0;
voidedweasel@3929: }
voidedweasel@3929: else
voidedweasel@3929: {
voidedweasel@3929: // nothing found
voidedweasel@3929: if (out != in)
voidedweasel@3929: {
voidedweasel@3929: strncopy(out, in, outSize);
voidedweasel@3929: }
voidedweasel@3929: }
voidedweasel@3929: }