/** * vim: set ts=4 : * ============================================================================= * 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 #include #include "sm_globals.h" #include "sm_stringutil.h" #include #include inline const char *_strstr(const char *str, const char *substr) { #ifdef PLATFORM_WINDOWS return strstr(str, substr); #elif defined PLATFORM_LINUX 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 = UTIL_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 = UTIL_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 = atcprintf(buf, static_cast(params[2]), fmt, pCtx, params, &arg); return static_cast(res); } static char g_formatbuf[2048]; 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; 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; } } buf = (copy) ? g_formatbuf : destbuf; res = atcprintf(buf, maxlen, fmt, pCtx, params, &arg); if (copy) { memcpy(destbuf, g_formatbuf, 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 = 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; pContext->LocalToString(params[1], &text); pContext->LocalToString(params[3], &search); pContext->LocalToString(params[4], &replace); maxlength = (size_t)params[2]; if (search[0] == '\0') { return pContext->ThrowNativeError("Cannot replace searches of empty strings"); } return UTIL_ReplaceAll(text, maxlength, search, replace); } static cell_t ReplaceStringEx(IPluginContext *pContext, const cell_t *params) { char *text, *search, *replace; size_t maxlength; 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]; if (searchLen == 0) { return pContext->ThrowNativeError("Cannot replace searches of empty strings"); } char *ptr = UTIL_ReplaceEx(text, maxlength, search, searchLen, replace, replaceLen); 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}, };