3 * ===============================================================
4 * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
5 * ===============================================================
7 * This file is not open source and may not be copied without explicit
8 * written permission of AlliedModders LLC. This file may not be redistributed
9 * in whole or significant part.
10 * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
17 #include "sm_globals.h"
18 #include "sm_stringutil.h"
19 #include "TextParsers.h"
22 inline const char *_strstr(const char *str, const char *substr)
24 #ifdef PLATFORM_WINDOWS
25 return strstr(str, substr);
26 #elif defined PLATFORM_LINUX
27 return (const char *)strstr(str, substr);
31 /*********************************************
33 * STRING MANIPULATION NATIVE IMPLEMENTATIONS *
35 *********************************************/
37 static cell_t sm_strlen(IPluginContext *pCtx, const cell_t *params)
40 pCtx->LocalToString(params[1], &str);
45 static cell_t sm_contain(IPluginContext *pCtx, const cell_t *params)
47 typedef const char *(*STRSEARCH)(const char *, const char *);
51 pCtx->LocalToString(params[1], &str);
52 pCtx->LocalToString(params[2], &substr);
54 func = (params[3]) ? _strstr : stristr;
55 const char *pos = func(str, substr);
64 static cell_t sm_strcmp(IPluginContext *pCtx, const cell_t *params)
66 typedef int (*STRCOMPARE)(const char *, const char *);
70 pCtx->LocalToString(params[1], &str1);
71 pCtx->LocalToString(params[2], &str2);
73 func = (params[3]) ? strcmp : stricmp;
75 return (func(str1, str2));
78 static cell_t sm_strncmp(IPluginContext *pCtx, const cell_t *params)
82 pCtx->LocalToString(params[1], &str1);
83 pCtx->LocalToString(params[2], &str2);
87 return strncmp(str1, str2, (size_t)params[3]);
89 return strncasecmp(str1, str2, (size_t)params[3]);
93 static cell_t sm_strcopy(IPluginContext *pCtx, const cell_t *params)
97 pCtx->LocalToString(params[1], &dest);
98 pCtx->LocalToString(params[3], &src);
100 return strncopy(dest, src, params[2]);
103 static cell_t sm_strconvint(IPluginContext *pCtx, const cell_t *params)
106 pCtx->LocalToString(params[1], &str);
108 return static_cast<cell_t>(strtol(str, &dummy, params[2]));
111 static cell_t StringToIntEx(IPluginContext *pCtx, const cell_t *params)
113 char *str, *dummy = NULL;
115 pCtx->LocalToString(params[1], &str);
116 pCtx->LocalToPhysAddr(params[2], &addr);
118 *addr = static_cast<cell_t>(strtol(str, &dummy, params[3]));
123 static cell_t sm_numtostr(IPluginContext *pCtx, const cell_t *params)
126 pCtx->LocalToString(params[2], &str);
127 size_t res = UTIL_Format(str, params[3], "%d", params[1]);
129 return static_cast<cell_t>(res);
132 static cell_t sm_strtofloat(IPluginContext *pCtx, const cell_t *params)
135 pCtx->LocalToString(params[1], &str);
137 float val = (float)strtod(str, &dummy);
142 static cell_t StringToFloatEx(IPluginContext *pCtx, const cell_t *params)
144 char *str, *dummy = NULL;
146 pCtx->LocalToString(params[1], &str);
147 pCtx->LocalToPhysAddr(params[2], &addr);
149 float val = (float)strtod(str, &dummy);
151 *addr = sp_ftoc(val);
156 static cell_t sm_floattostr(IPluginContext *pCtx, const cell_t *params)
159 pCtx->LocalToString(params[2], &str);
160 size_t res = UTIL_Format(str, params[3], "%f", sp_ctof(params[1]));
162 return static_cast<cell_t>(res);
165 static cell_t sm_formatex(IPluginContext *pCtx, const cell_t *params)
171 pCtx->LocalToString(params[1], &buf);
172 pCtx->LocalToString(params[3], &fmt);
173 res = atcprintf(buf, static_cast<size_t>(params[2]), fmt, pCtx, params, &arg);
175 return static_cast<cell_t>(res);
178 static char g_formatbuf[2048];
179 static cell_t sm_format(IPluginContext *pCtx, const cell_t *params)
181 char *buf, *fmt, *destbuf;
182 cell_t start_addr, end_addr, maxparam;
187 pCtx->LocalToString(params[1], &destbuf);
188 pCtx->LocalToString(params[3], &fmt);
190 maxlen = static_cast<size_t>(params[2]);
191 start_addr = params[1];
192 end_addr = params[1] + maxlen;
193 maxparam = params[0];
195 for (cell_t i=3; i<=maxparam; i++)
197 if ((params[i] >= start_addr) && (params[i] <= end_addr))
203 buf = (copy) ? g_formatbuf : destbuf;
204 res = atcprintf(buf, maxlen, fmt, pCtx, params, &arg);
208 memcpy(destbuf, g_formatbuf, res+1);
211 return static_cast<cell_t>(res);
214 static cell_t sm_vformat(IPluginContext *pContext, const cell_t *params)
216 int vargPos = static_cast<int>(params[4]);
218 /* Get the parent parameter array */
219 sp_context_t *ctx = pContext->GetContext();
220 cell_t *local_params = (cell_t *)(ctx->memory + ctx->frm + (2 * sizeof(cell_t)));
222 cell_t max = local_params[0];
223 if (vargPos > (int)max + 1)
225 return pContext->ThrowNativeError("Argument index is invalid: %d", vargPos);
228 cell_t addr_start = params[1];
229 cell_t addr_end = addr_start + params[2];
231 for (int i=vargPos; i<=max; i++)
233 /* Does this clip bounds? */
234 if ((local_params[i] >= addr_start)
235 && (local_params[i] <= addr_end))
242 /* Get destination info */
243 char *format, *destination;
244 size_t maxlen = static_cast<size_t>(params[2]);
248 destination = g_formatbuf;
250 pContext->LocalToString(params[1], &destination);
253 pContext->LocalToString(params[3], &format);
255 size_t total = atcprintf(destination, maxlen, format, pContext, local_params, &vargPos);
257 /* Perform copy-on-write if we need to */
260 pContext->StringToLocal(params[1], maxlen, g_formatbuf);
266 /* :TODO: make this UTF8 safe */
267 static cell_t BreakString(IPluginContext *pContext, const cell_t *params)
274 pContext->LocalToString(params[1], (char **)&input);
275 pContext->LocalToString(params[2], &out);
278 const char *inptr = input;
279 /* Eat up whitespace */
280 while (*inptr != '\0' && IsWhitespace(inptr))
294 const char *start, *end = NULL;
296 bool quoted = (*inptr == '"');
301 /* Read input until we reach a quote. */
302 while (*inptr != '\0' && *inptr != '"')
304 /* Update the end point, increment the stream. */
307 /* Read one more token if we reached an end quote */
314 /* Read input until we reach a space */
315 while (*inptr != '\0' && !IsWhitespace(inptr))
317 /* Update the end point, increment the stream. */
322 /* Copy the string we found, if necessary */
332 for (const char *ptr=start;
333 (ptr <= end) && ((unsigned)(outptr - out) < (outMax));
341 /* Consume more of the string until we reach non-whitespace */
342 while (*inptr != '\0' && IsWhitespace(inptr))
352 return inptr - input;
355 static cell_t GetCharBytes(IPluginContext *pContext, const cell_t *params)
358 pContext->LocalToString(params[1], &str);
360 return _GetUTF8CharBytes(str);
363 static cell_t IsCharAlpha(IPluginContext *pContext, const cell_t *params)
365 char chr = params[1];
367 if (_GetUTF8CharBytes(&chr) != 1)
375 static cell_t IsCharNumeric(IPluginContext *pContext, const cell_t *params)
377 char chr = params[1];
379 if (_GetUTF8CharBytes(&chr) != 1)
387 static cell_t IsCharSpace(IPluginContext *pContext, const cell_t *params)
389 char chr = params[1];
391 if (_GetUTF8CharBytes(&chr) != 1)
399 static cell_t IsCharMB(IPluginContext *pContext, const cell_t *params)
401 char chr = params[1];
403 unsigned int bytes = _GetUTF8CharBytes(&chr);
412 static cell_t IsCharUpper(IPluginContext *pContext, const cell_t *params)
414 char chr = params[1];
416 if (_GetUTF8CharBytes(&chr) != 1)
424 static cell_t IsCharLower(IPluginContext *pContext, const cell_t *params)
426 char chr = params[1];
428 if (_GetUTF8CharBytes(&chr) != 1)
436 static cell_t ReplaceString(IPluginContext *pContext, const cell_t *params)
438 char *text, *search, *replace;
441 pContext->LocalToString(params[1], &text);
442 pContext->LocalToString(params[3], &search);
443 pContext->LocalToString(params[4], &replace);
444 maxlength = (size_t)params[2];
447 return UTIL_ReplaceAll(text, maxlength, search, replace);
450 static cell_t ReplaceStringEx(IPluginContext *pContext, const cell_t *params)
452 char *text, *search, *replace;
455 pContext->LocalToString(params[1], &text);
456 pContext->LocalToString(params[3], &search);
457 pContext->LocalToString(params[4], &replace);
458 maxlength = (size_t)params[2];
460 size_t searchLen = (params[5] == -1) ? strlen(search) : (size_t)params[5];
461 size_t replaceLen = (params[6] == -1) ? strlen(replace) : (size_t)params[6];
463 char *ptr = UTIL_ReplaceEx(text, maxlength, search, searchLen, replace, replaceLen);
473 static cell_t TrimString(IPluginContext *pContext, const cell_t *params)
476 pContext->LocalToString(params[1], &str);
478 size_t chars = strlen(str);
485 char *end = str + chars - 1;
487 /* Iterate backwards through string until we reach first non-whitespace char */
488 while (end >= str && IsWhitespace(end))
493 /* Replace first whitespace char (at the end) with null terminator */
496 /* Iterate forwards through string until first non-whitespace char is reached */
497 while (IsWhitespace(str))
503 pContext->StringToLocalUTF8(params[1], chars + 1, str, &bytes);
508 static cell_t SplitString(IPluginContext *pContext, const cell_t *params)
512 pContext->LocalToString(params[1], &text);
513 pContext->LocalToString(params[2], &split);
515 size_t maxLen = (size_t)params[4];
516 size_t textLen = strlen(text);
517 size_t splitLen = strlen(split);
519 if (splitLen > textLen)
524 for (size_t i=0; i<textLen - splitLen; i++)
526 if (strncmp(&text[i], split, splitLen) == 0)
531 pContext->StringToLocalUTF8(params[3], maxLen, text, NULL);
533 pContext->StringToLocalUTF8(params[3], i+1, text, NULL);
535 return (cell_t)(i + splitLen);
542 REGISTER_NATIVES(basicStrings)
544 {"BreakString", BreakString},
545 {"FloatToString", sm_floattostr},
546 {"Format", sm_format},
547 {"FormatEx", sm_formatex},
548 {"GetCharBytes", GetCharBytes},
549 {"IntToString", sm_numtostr},
550 {"IsCharAlpha", IsCharAlpha},
551 {"IsCharLower", IsCharLower},
552 {"IsCharMB", IsCharMB},
553 {"IsCharNumeric", IsCharNumeric},
554 {"IsCharSpace", IsCharSpace},
555 {"IsCharUpper", IsCharUpper},
556 {"ReplaceString", ReplaceString},
557 {"ReplaceStringEx", ReplaceStringEx},
558 {"SplitString", SplitString},
559 {"strlen", sm_strlen},
560 {"StrBreak", BreakString}, /* Backwards compat shim */
561 {"StrContains", sm_contain},
562 {"strcmp", sm_strcmp},
563 {"StrCompare", sm_strcmp}, /* Backwards compat shim */
564 {"strncmp", sm_strncmp},
565 {"strcopy", sm_strcopy},
566 {"StrCopy", sm_strcopy}, /* Backwards compat shim */
567 {"StringToInt", sm_strconvint},
568 {"StringToIntEx", StringToIntEx},
569 {"StringToFloat", sm_strtofloat},
570 {"StringToFloatEx", StringToFloatEx},
571 {"TrimString", TrimString},
572 {"VFormat", sm_vformat},