diff -r 2db965f8db3f -r 39a4611feaba core/logic/smn_string.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/logic/smn_string.cpp Fri May 14 20:04:44 2010 -0700 @@ -0,0 +1,682 @@ +/** + * vim: set ts=4 sw=4 tw=99 noet : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 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 . + * + * 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 . + * + * Version: $Id$ + */ + +#include "common_logic.h" +#include +#include +#include +#include +#include "stringutil.h" + +inline const char *_strstr(const char *str, const char *substr) +{ +#ifdef PLATFORM_WINDOWS + return strstr(str, substr); +#elif defined PLATFORM_LINUX || defined PLATFORM_APPLE + return (const char *)strstr(str, substr); +#endif +} + +/********************************************* +* * +* STRING MANIPULATION NATIVE IMPLEMENTATIONS * +* * +*********************************************/ + +static cell_t sm_strlen(IPluginContext *pCtx, const cell_t *params) +{ + char *str; + pCtx->LocalToString(params[1], &str); + + return strlen(str); +} + +static cell_t sm_contain(IPluginContext *pCtx, const cell_t *params) +{ + typedef const char *(*STRSEARCH)(const char *, const char *); + STRSEARCH func; + char *str, *substr; + + pCtx->LocalToString(params[1], &str); + pCtx->LocalToString(params[2], &substr); + + func = (params[3]) ? _strstr : stristr; + const char *pos = func(str, substr); + if (pos) + { + return (pos - str); + } + + return -1; +} + +static cell_t sm_strcmp(IPluginContext *pCtx, const cell_t *params) +{ + typedef int (*STRCOMPARE)(const char *, const char *); + STRCOMPARE func; + char *str1, *str2; + + pCtx->LocalToString(params[1], &str1); + pCtx->LocalToString(params[2], &str2); + + func = (params[3]) ? strcmp : stricmp; + + return (func(str1, str2)); +} + +static cell_t sm_strncmp(IPluginContext *pCtx, const cell_t *params) +{ + char *str1, *str2; + + pCtx->LocalToString(params[1], &str1); + pCtx->LocalToString(params[2], &str2); + + if (params[4]) + { + return strncmp(str1, str2, (size_t)params[3]); + } else { + return strncasecmp(str1, str2, (size_t)params[3]); + } +} + +static cell_t sm_strcopy(IPluginContext *pCtx, const cell_t *params) +{ + char *dest, *src; + + pCtx->LocalToString(params[1], &dest); + pCtx->LocalToString(params[3], &src); + + return strncopy(dest, src, params[2]); +} + +static cell_t sm_strconvint(IPluginContext *pCtx, const cell_t *params) +{ + char *str, *dummy; + pCtx->LocalToString(params[1], &str); + + return static_cast(strtol(str, &dummy, params[2])); +} + +static cell_t StringToIntEx(IPluginContext *pCtx, const cell_t *params) +{ + char *str, *dummy = NULL; + cell_t *addr; + pCtx->LocalToString(params[1], &str); + pCtx->LocalToPhysAddr(params[2], &addr); + + *addr = static_cast(strtol(str, &dummy, params[3])); + + return dummy - str; +} + +static cell_t sm_numtostr(IPluginContext *pCtx, const cell_t *params) +{ + char *str; + pCtx->LocalToString(params[2], &str); + size_t res = smcore.Format(str, params[3], "%d", params[1]); + + return static_cast(res); +} + +static cell_t sm_strtofloat(IPluginContext *pCtx, const cell_t *params) +{ + char *str, *dummy; + pCtx->LocalToString(params[1], &str); + + float val = (float)strtod(str, &dummy); + + return sp_ftoc(val); +} + +static cell_t StringToFloatEx(IPluginContext *pCtx, const cell_t *params) +{ + char *str, *dummy = NULL; + cell_t *addr; + pCtx->LocalToString(params[1], &str); + pCtx->LocalToPhysAddr(params[2], &addr); + + float val = (float)strtod(str, &dummy); + + *addr = sp_ftoc(val); + + return dummy - str; +} + +static cell_t sm_floattostr(IPluginContext *pCtx, const cell_t *params) +{ + char *str; + pCtx->LocalToString(params[2], &str); + size_t res = smcore.Format(str, params[3], "%f", sp_ctof(params[1])); + + return static_cast(res); +} + +static cell_t sm_formatex(IPluginContext *pCtx, const cell_t *params) +{ + char *buf, *fmt; + size_t res; + int arg = 4; + + pCtx->LocalToString(params[1], &buf); + pCtx->LocalToString(params[3], &fmt); + res = smcore.atcprintf(buf, static_cast(params[2]), fmt, pCtx, params, &arg); + + return static_cast(res); +} + +class StaticCharBuf +{ + char *buffer; + size_t max_size; +public: + StaticCharBuf() : buffer(NULL), max_size(0) + { + } + ~StaticCharBuf() + { + free(buffer); + } + char* GetWithSize(size_t len) + { + if (len > max_size) + { + buffer = (char *)realloc(buffer, len); + max_size = len; + } + return buffer; + } +}; + +static char g_formatbuf[2048]; +static StaticCharBuf g_extrabuf; +static cell_t sm_format(IPluginContext *pCtx, const cell_t *params) +{ + char *buf, *fmt, *destbuf; + cell_t start_addr, end_addr, maxparam; + size_t res, maxlen; + int arg = 4; + bool copy = false; + char *__copy_buf; + + pCtx->LocalToString(params[1], &destbuf); + pCtx->LocalToString(params[3], &fmt); + + maxlen = static_cast(params[2]); + start_addr = params[1]; + end_addr = params[1] + maxlen; + maxparam = params[0]; + + for (cell_t i=3; i<=maxparam; i++) + { + if ((params[i] >= start_addr) && (params[i] <= end_addr)) + { + copy = true; + break; + } + } + + if (copy) + { + if (maxlen > sizeof(g_formatbuf)) + { + __copy_buf = g_extrabuf.GetWithSize(maxlen); + } + else + { + __copy_buf = g_formatbuf; + } + } + + buf = (copy) ? __copy_buf : destbuf; + res = smcore.atcprintf(buf, maxlen, fmt, pCtx, params, &arg); + + if (copy) + { + memcpy(destbuf, __copy_buf, res+1); + } + + return static_cast(res); +} + +static cell_t sm_vformat(IPluginContext *pContext, const cell_t *params) +{ + int vargPos = static_cast(params[4]); + + /* Get the parent parameter array */ + cell_t *local_params = pContext->GetLocalParams(); + + cell_t max = local_params[0]; + if (vargPos > (int)max + 1) + { + return pContext->ThrowNativeError("Argument index is invalid: %d", vargPos); + } + + cell_t addr_start = params[1]; + cell_t addr_end = addr_start + params[2]; + bool copy = false; + for (int i=vargPos; i<=max; i++) + { + /* Does this clip bounds? */ + if ((local_params[i] >= addr_start) + && (local_params[i] <= addr_end)) + { + copy = true; + break; + } + } + + /* Get destination info */ + char *format, *destination; + size_t maxlen = static_cast(params[2]); + + if (copy) + { + destination = g_formatbuf; + } else { + pContext->LocalToString(params[1], &destination); + } + + pContext->LocalToString(params[3], &format); + + size_t total = smcore.atcprintf(destination, maxlen, format, pContext, local_params, &vargPos); + + /* Perform copy-on-write if we need to */ + if (copy) + { + pContext->StringToLocal(params[1], maxlen, g_formatbuf); + } + + return total; +} + +/* :TODO: make this UTF8 safe */ +static cell_t BreakString(IPluginContext *pContext, const cell_t *params) +{ + const char *input; + char *out; + size_t outMax; + + /* Get parameters */ + pContext->LocalToString(params[1], (char **)&input); + pContext->LocalToString(params[2], &out); + outMax = params[3]; + + const char *inptr = input; + /* Eat up whitespace */ + while (*inptr != '\0' && textparsers->IsWhitespace(inptr)) + { + inptr++; + } + + if (*inptr == '\0') + { + if (outMax) + { + *out = '\0'; + } + return -1; + } + + const char *start, *end = NULL; + + bool quoted = (*inptr == '"'); + if (quoted) + { + inptr++; + start = inptr; + /* Read input until we reach a quote. */ + while (*inptr != '\0' && *inptr != '"') + { + /* Update the end point, increment the stream. */ + end = inptr++; + } + /* Read one more token if we reached an end quote */ + if (*inptr == '"') + { + inptr++; + } + } else { + start = inptr; + /* Read input until we reach a space */ + while (*inptr != '\0' && !textparsers->IsWhitespace(inptr)) + { + /* Update the end point, increment the stream. */ + end = inptr++; + } + } + + /* Copy the string we found, if necessary */ + if (end == NULL) + { + if (outMax) + { + *out = '\0'; + } + } else if (outMax) { + char *outptr = out; + outMax--; + for (const char *ptr=start; + (ptr <= end) && ((unsigned)(outptr - out) < (outMax)); + ptr++, outptr++) + { + *outptr = *ptr; + } + *outptr = '\0'; + } + + /* Consume more of the string until we reach non-whitespace */ + while (*inptr != '\0' && textparsers->IsWhitespace(inptr)) + { + inptr++; + } + + if (*inptr == '\0') + { + return -1; + } + + return inptr - input; +} + +static cell_t GetCharBytes(IPluginContext *pContext, const cell_t *params) +{ + char *str; + pContext->LocalToString(params[1], &str); + + return _GetUTF8CharBytes(str); +}; + +static cell_t IsCharAlpha(IPluginContext *pContext, const cell_t *params) +{ + char chr = params[1]; + + if (_GetUTF8CharBytes(&chr) != 1) + { + return 0; + } + + return isalpha(chr); +} + +static cell_t IsCharNumeric(IPluginContext *pContext, const cell_t *params) +{ + char chr = params[1]; + + if (_GetUTF8CharBytes(&chr) != 1) + { + return 0; + } + + return isdigit(chr); +} + +static cell_t IsCharSpace(IPluginContext *pContext, const cell_t *params) +{ + char chr = params[1]; + + if (_GetUTF8CharBytes(&chr) != 1) + { + return 0; + } + + return isspace(chr); +} + +static cell_t IsCharMB(IPluginContext *pContext, const cell_t *params) +{ + char chr = params[1]; + + unsigned int bytes = _GetUTF8CharBytes(&chr); + if (bytes == 1) + { + return 0; + } + + return bytes; +} + +static cell_t IsCharUpper(IPluginContext *pContext, const cell_t *params) +{ + char chr = params[1]; + + if (_GetUTF8CharBytes(&chr) != 1) + { + return 0; + } + + return isupper(chr); +} + +static cell_t IsCharLower(IPluginContext *pContext, const cell_t *params) +{ + char chr = params[1]; + + if (_GetUTF8CharBytes(&chr) != 1) + { + return 0; + } + + return islower(chr); +} + +static cell_t ReplaceString(IPluginContext *pContext, const cell_t *params) +{ + char *text, *search, *replace; + size_t maxlength; + bool caseSensitive; + + pContext->LocalToString(params[1], &text); + pContext->LocalToString(params[3], &search); + pContext->LocalToString(params[4], &replace); + maxlength = (size_t)params[2]; + + /* Account for old binary plugins that won't pass the last parameter */ + if (params[0] == 5) + { + caseSensitive = (bool)params[5]; + } + else + { + caseSensitive = true; + } + + if (search[0] == '\0') + { + return pContext->ThrowNativeError("Cannot replace searches of empty strings"); + } + + return UTIL_ReplaceAll(text, maxlength, search, replace, caseSensitive); +} + +static cell_t ReplaceStringEx(IPluginContext *pContext, const cell_t *params) +{ + char *text, *search, *replace; + size_t maxlength; + bool caseSensitive; + + pContext->LocalToString(params[1], &text); + pContext->LocalToString(params[3], &search); + pContext->LocalToString(params[4], &replace); + maxlength = (size_t)params[2]; + + size_t searchLen = (params[5] == -1) ? strlen(search) : (size_t)params[5]; + size_t replaceLen = (params[6] == -1) ? strlen(replace) : (size_t)params[6]; + + /* Account for old binary plugins that won't pass the last parameter */ + if (params[0] == 7) + { + caseSensitive = (bool)params[7]; + } + else + { + caseSensitive = true; + } + + if (searchLen == 0) + { + return pContext->ThrowNativeError("Cannot replace searches of empty strings"); + } + + char *ptr = UTIL_ReplaceEx(text, maxlength, search, searchLen, replace, replaceLen, caseSensitive); + + if (ptr == NULL) + { + return -1; + } + + return ptr - text; +} + +static cell_t TrimString(IPluginContext *pContext, const cell_t *params) +{ + char *str; + pContext->LocalToString(params[1], &str); + + size_t chars = strlen(str); + + if (chars == 0) + { + return 0; + } + + char *end = str + chars - 1; + + /* Iterate backwards through string until we reach first non-whitespace char */ + while (end >= str && textparsers->IsWhitespace(end)) + { + end--; + } + + /* Replace first whitespace char (at the end) with null terminator */ + *(end + 1) = '\0'; + + /* Iterate forwards through string until first non-whitespace char is reached */ + while (textparsers->IsWhitespace(str)) + { + str++; + } + + size_t bytes; + pContext->StringToLocalUTF8(params[1], chars + 1, str, &bytes); + + return bytes; +} + +static cell_t SplitString(IPluginContext *pContext, const cell_t *params) +{ + char *text, *split; + + pContext->LocalToString(params[1], &text); + pContext->LocalToString(params[2], &split); + + size_t maxLen = (size_t)params[4]; + size_t textLen = strlen(text); + size_t splitLen = strlen(split); + + if (splitLen > textLen) + { + return -1; + } + + /** + * Note that it's <= ... you could also just add 1, + * but this is a bit nicer + */ + for (size_t i=0; i<=textLen - splitLen; i++) + { + if (strncmp(&text[i], split, splitLen) == 0) + { + /* Split hereeeee */ + if (i >= maxLen) + { + pContext->StringToLocalUTF8(params[3], maxLen, text, NULL); + } else { + pContext->StringToLocalUTF8(params[3], i+1, text, NULL); + } + return (cell_t)(i + splitLen); + } + } + + return -1; +} + +static cell_t StripQuotes(IPluginContext *pContext, const cell_t *params) +{ + char *text; + size_t length; + + pContext->LocalToString(params[1], &text); + length = strlen(text); + + if (text[0] == '"' && text[length-1] == '"') + { + /* Null-terminate */ + text[--length] = '\0'; + /* Move the remaining bytes (including null terminator) down by one */ + memmove(&text[0], &text[1], length); + + return 1; + } + + return 0; +} + +REGISTER_NATIVES(basicStrings) +{ + {"BreakString", BreakString}, + {"FloatToString", sm_floattostr}, + {"Format", sm_format}, + {"FormatEx", sm_formatex}, + {"GetCharBytes", GetCharBytes}, + {"IntToString", sm_numtostr}, + {"IsCharAlpha", IsCharAlpha}, + {"IsCharLower", IsCharLower}, + {"IsCharMB", IsCharMB}, + {"IsCharNumeric", IsCharNumeric}, + {"IsCharSpace", IsCharSpace}, + {"IsCharUpper", IsCharUpper}, + {"ReplaceString", ReplaceString}, + {"ReplaceStringEx", ReplaceStringEx}, + {"SplitString", SplitString}, + {"strlen", sm_strlen}, + {"StrContains", sm_contain}, + {"strcmp", sm_strcmp}, + {"strncmp", sm_strncmp}, + {"strcopy", sm_strcopy}, + {"StringToInt", sm_strconvint}, + {"StringToIntEx", StringToIntEx}, + {"StringToFloat", sm_strtofloat}, + {"StringToFloatEx", StringToFloatEx}, + {"StripQuotes", StripQuotes}, + {"TrimString", TrimString}, + {"VFormat", sm_vformat}, + {NULL, NULL}, +};