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: }