3 * =============================================================================
5 * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
6 * =============================================================================
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, version 3.0, as published by the
10 * Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to link the
21 * code of this program (as well as its derivative works) to "Half-Life 2," the
22 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
23 * by the Valve Corporation. You must obey the GNU General Public License in
24 * all respects for all other code used. Additionally, AlliedModders LLC grants
25 * this exception to all derivative works. AlliedModders LLC defines further
26 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
27 * or <http://www.sourcemod.net/license.php>.
34 #include "sm_globals.h"
35 #include "sm_stringutil.h"
36 #include <ITextParsers.h>
38 #include "logic_bridge.h"
40 inline const char *_strstr(const char *str, const char *substr)
42 #ifdef PLATFORM_WINDOWS
43 return strstr(str, substr);
44 #elif defined PLATFORM_LINUX || defined PLATFORM_APPLE
45 return (const char *)strstr(str, substr);
49 /*********************************************
51 * STRING MANIPULATION NATIVE IMPLEMENTATIONS *
53 *********************************************/
55 static cell_t sm_strlen(IPluginContext *pCtx, const cell_t *params)
58 pCtx->LocalToString(params[1], &str);
63 static cell_t sm_contain(IPluginContext *pCtx, const cell_t *params)
65 typedef const char *(*STRSEARCH)(const char *, const char *);
69 pCtx->LocalToString(params[1], &str);
70 pCtx->LocalToString(params[2], &substr);
72 func = (params[3]) ? _strstr : logicore.stristr;
73 const char *pos = func(str, substr);
82 static cell_t sm_strcmp(IPluginContext *pCtx, const cell_t *params)
84 typedef int (*STRCOMPARE)(const char *, const char *);
88 pCtx->LocalToString(params[1], &str1);
89 pCtx->LocalToString(params[2], &str2);
91 func = (params[3]) ? strcmp : stricmp;
93 return (func(str1, str2));
96 static cell_t sm_strncmp(IPluginContext *pCtx, const cell_t *params)
100 pCtx->LocalToString(params[1], &str1);
101 pCtx->LocalToString(params[2], &str2);
105 return strncmp(str1, str2, (size_t)params[3]);
107 return strncasecmp(str1, str2, (size_t)params[3]);
111 static cell_t sm_strcopy(IPluginContext *pCtx, const cell_t *params)
115 pCtx->LocalToString(params[1], &dest);
116 pCtx->LocalToString(params[3], &src);
118 return strncopy(dest, src, params[2]);
121 static cell_t sm_strconvint(IPluginContext *pCtx, const cell_t *params)
124 pCtx->LocalToString(params[1], &str);
126 return static_cast<cell_t>(strtol(str, &dummy, params[2]));
129 static cell_t StringToIntEx(IPluginContext *pCtx, const cell_t *params)
131 char *str, *dummy = NULL;
133 pCtx->LocalToString(params[1], &str);
134 pCtx->LocalToPhysAddr(params[2], &addr);
136 *addr = static_cast<cell_t>(strtol(str, &dummy, params[3]));
141 static cell_t sm_numtostr(IPluginContext *pCtx, const cell_t *params)
144 pCtx->LocalToString(params[2], &str);
145 size_t res = UTIL_Format(str, params[3], "%d", params[1]);
147 return static_cast<cell_t>(res);
150 static cell_t sm_strtofloat(IPluginContext *pCtx, const cell_t *params)
153 pCtx->LocalToString(params[1], &str);
155 float val = (float)strtod(str, &dummy);
160 static cell_t StringToFloatEx(IPluginContext *pCtx, const cell_t *params)
162 char *str, *dummy = NULL;
164 pCtx->LocalToString(params[1], &str);
165 pCtx->LocalToPhysAddr(params[2], &addr);
167 float val = (float)strtod(str, &dummy);
169 *addr = sp_ftoc(val);
174 static cell_t sm_floattostr(IPluginContext *pCtx, const cell_t *params)
177 pCtx->LocalToString(params[2], &str);
178 size_t res = UTIL_Format(str, params[3], "%f", sp_ctof(params[1]));
180 return static_cast<cell_t>(res);
183 static cell_t sm_formatex(IPluginContext *pCtx, const cell_t *params)
189 pCtx->LocalToString(params[1], &buf);
190 pCtx->LocalToString(params[3], &fmt);
191 res = atcprintf(buf, static_cast<size_t>(params[2]), fmt, pCtx, params, &arg);
193 return static_cast<cell_t>(res);
201 StaticCharBuf() : buffer(NULL), max_size(0)
208 char* GetWithSize(size_t len)
212 buffer = (char *)realloc(buffer, len);
219 static char g_formatbuf[2048];
220 static StaticCharBuf g_extrabuf;
221 static cell_t sm_format(IPluginContext *pCtx, const cell_t *params)
223 char *buf, *fmt, *destbuf;
224 cell_t start_addr, end_addr, maxparam;
230 pCtx->LocalToString(params[1], &destbuf);
231 pCtx->LocalToString(params[3], &fmt);
233 maxlen = static_cast<size_t>(params[2]);
234 start_addr = params[1];
235 end_addr = params[1] + maxlen;
236 maxparam = params[0];
238 for (cell_t i=3; i<=maxparam; i++)
240 if ((params[i] >= start_addr) && (params[i] <= end_addr))
249 if (maxlen > sizeof(g_formatbuf))
251 __copy_buf = g_extrabuf.GetWithSize(maxlen);
255 __copy_buf = g_formatbuf;
259 buf = (copy) ? __copy_buf : destbuf;
260 res = atcprintf(buf, maxlen, fmt, pCtx, params, &arg);
264 memcpy(destbuf, __copy_buf, res+1);
267 return static_cast<cell_t>(res);
270 static cell_t sm_vformat(IPluginContext *pContext, const cell_t *params)
272 int vargPos = static_cast<int>(params[4]);
274 /* Get the parent parameter array */
275 cell_t *local_params = pContext->GetLocalParams();
277 cell_t max = local_params[0];
278 if (vargPos > (int)max + 1)
280 return pContext->ThrowNativeError("Argument index is invalid: %d", vargPos);
283 cell_t addr_start = params[1];
284 cell_t addr_end = addr_start + params[2];
286 for (int i=vargPos; i<=max; i++)
288 /* Does this clip bounds? */
289 if ((local_params[i] >= addr_start)
290 && (local_params[i] <= addr_end))
297 /* Get destination info */
298 char *format, *destination;
299 size_t maxlen = static_cast<size_t>(params[2]);
303 destination = g_formatbuf;
305 pContext->LocalToString(params[1], &destination);
308 pContext->LocalToString(params[3], &format);
310 size_t total = atcprintf(destination, maxlen, format, pContext, local_params, &vargPos);
312 /* Perform copy-on-write if we need to */
315 pContext->StringToLocal(params[1], maxlen, g_formatbuf);
321 /* :TODO: make this UTF8 safe */
322 static cell_t BreakString(IPluginContext *pContext, const cell_t *params)
329 pContext->LocalToString(params[1], (char **)&input);
330 pContext->LocalToString(params[2], &out);
333 const char *inptr = input;
334 /* Eat up whitespace */
335 while (*inptr != '\0' && textparsers->IsWhitespace(inptr))
349 const char *start, *end = NULL;
351 bool quoted = (*inptr == '"');
356 /* Read input until we reach a quote. */
357 while (*inptr != '\0' && *inptr != '"')
359 /* Update the end point, increment the stream. */
362 /* Read one more token if we reached an end quote */
369 /* Read input until we reach a space */
370 while (*inptr != '\0' && !textparsers->IsWhitespace(inptr))
372 /* Update the end point, increment the stream. */
377 /* Copy the string we found, if necessary */
387 for (const char *ptr=start;
388 (ptr <= end) && ((unsigned)(outptr - out) < (outMax));
396 /* Consume more of the string until we reach non-whitespace */
397 while (*inptr != '\0' && textparsers->IsWhitespace(inptr))
407 return inptr - input;
410 static cell_t GetCharBytes(IPluginContext *pContext, const cell_t *params)
413 pContext->LocalToString(params[1], &str);
415 return _GetUTF8CharBytes(str);
418 static cell_t IsCharAlpha(IPluginContext *pContext, const cell_t *params)
420 char chr = params[1];
422 if (_GetUTF8CharBytes(&chr) != 1)
430 static cell_t IsCharNumeric(IPluginContext *pContext, const cell_t *params)
432 char chr = params[1];
434 if (_GetUTF8CharBytes(&chr) != 1)
442 static cell_t IsCharSpace(IPluginContext *pContext, const cell_t *params)
444 char chr = params[1];
446 if (_GetUTF8CharBytes(&chr) != 1)
454 static cell_t IsCharMB(IPluginContext *pContext, const cell_t *params)
456 char chr = params[1];
458 unsigned int bytes = _GetUTF8CharBytes(&chr);
467 static cell_t IsCharUpper(IPluginContext *pContext, const cell_t *params)
469 char chr = params[1];
471 if (_GetUTF8CharBytes(&chr) != 1)
479 static cell_t IsCharLower(IPluginContext *pContext, const cell_t *params)
481 char chr = params[1];
483 if (_GetUTF8CharBytes(&chr) != 1)
491 static cell_t ReplaceString(IPluginContext *pContext, const cell_t *params)
493 char *text, *search, *replace;
497 pContext->LocalToString(params[1], &text);
498 pContext->LocalToString(params[3], &search);
499 pContext->LocalToString(params[4], &replace);
500 maxlength = (size_t)params[2];
502 /* Account for old binary plugins that won't pass the last parameter */
505 caseSensitive = (bool)params[5];
509 caseSensitive = true;
512 if (search[0] == '\0')
514 return pContext->ThrowNativeError("Cannot replace searches of empty strings");
517 return UTIL_ReplaceAll(text, maxlength, search, replace, caseSensitive);
520 static cell_t ReplaceStringEx(IPluginContext *pContext, const cell_t *params)
522 char *text, *search, *replace;
526 pContext->LocalToString(params[1], &text);
527 pContext->LocalToString(params[3], &search);
528 pContext->LocalToString(params[4], &replace);
529 maxlength = (size_t)params[2];
531 size_t searchLen = (params[5] == -1) ? strlen(search) : (size_t)params[5];
532 size_t replaceLen = (params[6] == -1) ? strlen(replace) : (size_t)params[6];
534 /* Account for old binary plugins that won't pass the last parameter */
537 caseSensitive = (bool)params[7];
541 caseSensitive = true;
546 return pContext->ThrowNativeError("Cannot replace searches of empty strings");
549 char *ptr = UTIL_ReplaceEx(text, maxlength, search, searchLen, replace, replaceLen, caseSensitive);
559 static cell_t TrimString(IPluginContext *pContext, const cell_t *params)
562 pContext->LocalToString(params[1], &str);
564 size_t chars = strlen(str);
571 char *end = str + chars - 1;
573 /* Iterate backwards through string until we reach first non-whitespace char */
574 while (end >= str && textparsers->IsWhitespace(end))
579 /* Replace first whitespace char (at the end) with null terminator */
582 /* Iterate forwards through string until first non-whitespace char is reached */
583 while (textparsers->IsWhitespace(str))
589 pContext->StringToLocalUTF8(params[1], chars + 1, str, &bytes);
594 static cell_t SplitString(IPluginContext *pContext, const cell_t *params)
598 pContext->LocalToString(params[1], &text);
599 pContext->LocalToString(params[2], &split);
601 size_t maxLen = (size_t)params[4];
602 size_t textLen = strlen(text);
603 size_t splitLen = strlen(split);
605 if (splitLen > textLen)
611 * Note that it's <= ... you could also just add 1,
612 * but this is a bit nicer
614 for (size_t i=0; i<=textLen - splitLen; i++)
616 if (strncmp(&text[i], split, splitLen) == 0)
621 pContext->StringToLocalUTF8(params[3], maxLen, text, NULL);
623 pContext->StringToLocalUTF8(params[3], i+1, text, NULL);
625 return (cell_t)(i + splitLen);
632 static cell_t StripQuotes(IPluginContext *pContext, const cell_t *params)
637 pContext->LocalToString(params[1], &text);
638 length = strlen(text);
640 if (text[0] == '"' && text[length-1] == '"')
643 text[--length] = '\0';
644 /* Move the remaining bytes (including null terminator) down by one */
645 memmove(&text[0], &text[1], length);
653 REGISTER_NATIVES(basicStrings)
655 {"BreakString", BreakString},
656 {"FloatToString", sm_floattostr},
657 {"Format", sm_format},
658 {"FormatEx", sm_formatex},
659 {"GetCharBytes", GetCharBytes},
660 {"IntToString", sm_numtostr},
661 {"IsCharAlpha", IsCharAlpha},
662 {"IsCharLower", IsCharLower},
663 {"IsCharMB", IsCharMB},
664 {"IsCharNumeric", IsCharNumeric},
665 {"IsCharSpace", IsCharSpace},
666 {"IsCharUpper", IsCharUpper},
667 {"ReplaceString", ReplaceString},
668 {"ReplaceStringEx", ReplaceStringEx},
669 {"SplitString", SplitString},
670 {"strlen", sm_strlen},
671 {"StrContains", sm_contain},
672 {"strcmp", sm_strcmp},
673 {"strncmp", sm_strncmp},
674 {"strcopy", sm_strcopy},
675 {"StringToInt", sm_strconvint},
676 {"StringToIntEx", StringToIntEx},
677 {"StringToFloat", sm_strtofloat},
678 {"StringToFloatEx", StringToFloatEx},
679 {"StripQuotes", StripQuotes},
680 {"TrimString", TrimString},
681 {"VFormat", sm_vformat},