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},
+};