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>
39 inline const char *_strstr(const char *str, const char *substr)
41 #ifdef PLATFORM_WINDOWS
42 return strstr(str, substr);
43 #elif defined PLATFORM_LINUX
44 return (const char *)strstr(str, substr);
48 /*********************************************
50 * STRING MANIPULATION NATIVE IMPLEMENTATIONS *
52 *********************************************/
54 static cell_t sm_strlen(IPluginContext *pCtx, const cell_t *params)
57 pCtx->LocalToString(params[1], &str);
62 static cell_t sm_contain(IPluginContext *pCtx, const cell_t *params)
64 typedef const char *(*STRSEARCH)(const char *, const char *);
68 pCtx->LocalToString(params[1], &str);
69 pCtx->LocalToString(params[2], &substr);
71 func = (params[3]) ? _strstr : stristr;
72 const char *pos = func(str, substr);
81 static cell_t sm_strcmp(IPluginContext *pCtx, const cell_t *params)
83 typedef int (*STRCOMPARE)(const char *, const char *);
87 pCtx->LocalToString(params[1], &str1);
88 pCtx->LocalToString(params[2], &str2);
90 func = (params[3]) ? strcmp : stricmp;
92 return (func(str1, str2));
95 static cell_t sm_strncmp(IPluginContext *pCtx, const cell_t *params)
99 pCtx->LocalToString(params[1], &str1);
100 pCtx->LocalToString(params[2], &str2);
104 return strncmp(str1, str2, (size_t)params[3]);
106 return strncasecmp(str1, str2, (size_t)params[3]);
110 static cell_t sm_strcopy(IPluginContext *pCtx, const cell_t *params)
114 pCtx->LocalToString(params[1], &dest);
115 pCtx->LocalToString(params[3], &src);
117 return strncopy(dest, src, params[2]);
120 static cell_t sm_strconvint(IPluginContext *pCtx, const cell_t *params)
123 pCtx->LocalToString(params[1], &str);
125 return static_cast<cell_t>(strtol(str, &dummy, params[2]));
128 static cell_t StringToIntEx(IPluginContext *pCtx, const cell_t *params)
130 char *str, *dummy = NULL;
132 pCtx->LocalToString(params[1], &str);
133 pCtx->LocalToPhysAddr(params[2], &addr);
135 *addr = static_cast<cell_t>(strtol(str, &dummy, params[3]));
140 static cell_t sm_numtostr(IPluginContext *pCtx, const cell_t *params)
143 pCtx->LocalToString(params[2], &str);
144 size_t res = UTIL_Format(str, params[3], "%d", params[1]);
146 return static_cast<cell_t>(res);
149 static cell_t sm_strtofloat(IPluginContext *pCtx, const cell_t *params)
152 pCtx->LocalToString(params[1], &str);
154 float val = (float)strtod(str, &dummy);
159 static cell_t StringToFloatEx(IPluginContext *pCtx, const cell_t *params)
161 char *str, *dummy = NULL;
163 pCtx->LocalToString(params[1], &str);
164 pCtx->LocalToPhysAddr(params[2], &addr);
166 float val = (float)strtod(str, &dummy);
168 *addr = sp_ftoc(val);
173 static cell_t sm_floattostr(IPluginContext *pCtx, const cell_t *params)
176 pCtx->LocalToString(params[2], &str);
177 size_t res = UTIL_Format(str, params[3], "%f", sp_ctof(params[1]));
179 return static_cast<cell_t>(res);
182 static cell_t sm_formatex(IPluginContext *pCtx, const cell_t *params)
188 pCtx->LocalToString(params[1], &buf);
189 pCtx->LocalToString(params[3], &fmt);
190 res = atcprintf(buf, static_cast<size_t>(params[2]), fmt, pCtx, params, &arg);
192 return static_cast<cell_t>(res);
195 static char g_formatbuf[2048];
196 static cell_t sm_format(IPluginContext *pCtx, const cell_t *params)
198 char *buf, *fmt, *destbuf;
199 cell_t start_addr, end_addr, maxparam;
204 pCtx->LocalToString(params[1], &destbuf);
205 pCtx->LocalToString(params[3], &fmt);
207 maxlen = static_cast<size_t>(params[2]);
208 start_addr = params[1];
209 end_addr = params[1] + maxlen;
210 maxparam = params[0];
212 for (cell_t i=3; i<=maxparam; i++)
214 if ((params[i] >= start_addr) && (params[i] <= end_addr))
220 buf = (copy) ? g_formatbuf : destbuf;
221 res = atcprintf(buf, maxlen, fmt, pCtx, params, &arg);
225 memcpy(destbuf, g_formatbuf, res+1);
228 return static_cast<cell_t>(res);
231 static cell_t sm_vformat(IPluginContext *pContext, const cell_t *params)
233 int vargPos = static_cast<int>(params[4]);
235 /* Get the parent parameter array */
236 sp_context_t *ctx = pContext->GetContext();
237 cell_t *local_params = (cell_t *)(ctx->memory + ctx->frm + (2 * sizeof(cell_t)));
239 cell_t max = local_params[0];
240 if (vargPos > (int)max + 1)
242 return pContext->ThrowNativeError("Argument index is invalid: %d", vargPos);
245 cell_t addr_start = params[1];
246 cell_t addr_end = addr_start + params[2];
248 for (int i=vargPos; i<=max; i++)
250 /* Does this clip bounds? */
251 if ((local_params[i] >= addr_start)
252 && (local_params[i] <= addr_end))
259 /* Get destination info */
260 char *format, *destination;
261 size_t maxlen = static_cast<size_t>(params[2]);
265 destination = g_formatbuf;
267 pContext->LocalToString(params[1], &destination);
270 pContext->LocalToString(params[3], &format);
272 size_t total = atcprintf(destination, maxlen, format, pContext, local_params, &vargPos);
274 /* Perform copy-on-write if we need to */
277 pContext->StringToLocal(params[1], maxlen, g_formatbuf);
283 /* :TODO: make this UTF8 safe */
284 static cell_t BreakString(IPluginContext *pContext, const cell_t *params)
291 pContext->LocalToString(params[1], (char **)&input);
292 pContext->LocalToString(params[2], &out);
295 const char *inptr = input;
296 /* Eat up whitespace */
297 while (*inptr != '\0' && textparsers->IsWhitespace(inptr))
311 const char *start, *end = NULL;
313 bool quoted = (*inptr == '"');
318 /* Read input until we reach a quote. */
319 while (*inptr != '\0' && *inptr != '"')
321 /* Update the end point, increment the stream. */
324 /* Read one more token if we reached an end quote */
331 /* Read input until we reach a space */
332 while (*inptr != '\0' && !textparsers->IsWhitespace(inptr))
334 /* Update the end point, increment the stream. */
339 /* Copy the string we found, if necessary */
349 for (const char *ptr=start;
350 (ptr <= end) && ((unsigned)(outptr - out) < (outMax));
358 /* Consume more of the string until we reach non-whitespace */
359 while (*inptr != '\0' && textparsers->IsWhitespace(inptr))
369 return inptr - input;
372 static cell_t GetCharBytes(IPluginContext *pContext, const cell_t *params)
375 pContext->LocalToString(params[1], &str);
377 return _GetUTF8CharBytes(str);
380 static cell_t IsCharAlpha(IPluginContext *pContext, const cell_t *params)
382 char chr = params[1];
384 if (_GetUTF8CharBytes(&chr) != 1)
392 static cell_t IsCharNumeric(IPluginContext *pContext, const cell_t *params)
394 char chr = params[1];
396 if (_GetUTF8CharBytes(&chr) != 1)
404 static cell_t IsCharSpace(IPluginContext *pContext, const cell_t *params)
406 char chr = params[1];
408 if (_GetUTF8CharBytes(&chr) != 1)
416 static cell_t IsCharMB(IPluginContext *pContext, const cell_t *params)
418 char chr = params[1];
420 unsigned int bytes = _GetUTF8CharBytes(&chr);
429 static cell_t IsCharUpper(IPluginContext *pContext, const cell_t *params)
431 char chr = params[1];
433 if (_GetUTF8CharBytes(&chr) != 1)
441 static cell_t IsCharLower(IPluginContext *pContext, const cell_t *params)
443 char chr = params[1];
445 if (_GetUTF8CharBytes(&chr) != 1)
453 static cell_t ReplaceString(IPluginContext *pContext, const cell_t *params)
455 char *text, *search, *replace;
458 pContext->LocalToString(params[1], &text);
459 pContext->LocalToString(params[3], &search);
460 pContext->LocalToString(params[4], &replace);
461 maxlength = (size_t)params[2];
464 return UTIL_ReplaceAll(text, maxlength, search, replace);
467 static cell_t ReplaceStringEx(IPluginContext *pContext, const cell_t *params)
469 char *text, *search, *replace;
472 pContext->LocalToString(params[1], &text);
473 pContext->LocalToString(params[3], &search);
474 pContext->LocalToString(params[4], &replace);
475 maxlength = (size_t)params[2];
477 size_t searchLen = (params[5] == -1) ? strlen(search) : (size_t)params[5];
478 size_t replaceLen = (params[6] == -1) ? strlen(replace) : (size_t)params[6];
480 char *ptr = UTIL_ReplaceEx(text, maxlength, search, searchLen, replace, replaceLen);
490 static cell_t TrimString(IPluginContext *pContext, const cell_t *params)
493 pContext->LocalToString(params[1], &str);
495 size_t chars = strlen(str);
502 char *end = str + chars - 1;
504 /* Iterate backwards through string until we reach first non-whitespace char */
505 while (end >= str && textparsers->IsWhitespace(end))
510 /* Replace first whitespace char (at the end) with null terminator */
513 /* Iterate forwards through string until first non-whitespace char is reached */
514 while (textparsers->IsWhitespace(str))
520 pContext->StringToLocalUTF8(params[1], chars + 1, str, &bytes);
525 static cell_t SplitString(IPluginContext *pContext, const cell_t *params)
529 pContext->LocalToString(params[1], &text);
530 pContext->LocalToString(params[2], &split);
532 size_t maxLen = (size_t)params[4];
533 size_t textLen = strlen(text);
534 size_t splitLen = strlen(split);
536 if (splitLen > textLen)
542 * Note that it's <= ... you could also just add 1,
543 * but this is a bit nicer
545 for (size_t i=0; i<=textLen - splitLen; i++)
547 if (strncmp(&text[i], split, splitLen) == 0)
552 pContext->StringToLocalUTF8(params[3], maxLen, text, NULL);
554 pContext->StringToLocalUTF8(params[3], i+1, text, NULL);
556 return (cell_t)(i + splitLen);
563 REGISTER_NATIVES(basicStrings)
565 {"BreakString", BreakString},
566 {"FloatToString", sm_floattostr},
567 {"Format", sm_format},
568 {"FormatEx", sm_formatex},
569 {"GetCharBytes", GetCharBytes},
570 {"IntToString", sm_numtostr},
571 {"IsCharAlpha", IsCharAlpha},
572 {"IsCharLower", IsCharLower},
573 {"IsCharMB", IsCharMB},
574 {"IsCharNumeric", IsCharNumeric},
575 {"IsCharSpace", IsCharSpace},
576 {"IsCharUpper", IsCharUpper},
577 {"ReplaceString", ReplaceString},
578 {"ReplaceStringEx", ReplaceStringEx},
579 {"SplitString", SplitString},
580 {"strlen", sm_strlen},
581 {"StrContains", sm_contain},
582 {"strcmp", sm_strcmp},
583 {"strncmp", sm_strncmp},
584 {"strcopy", sm_strcopy},
585 {"StringToInt", sm_strconvint},
586 {"StringToIntEx", StringToIntEx},
587 {"StringToFloat", sm_strtofloat},
588 {"StringToFloatEx", StringToFloatEx},
589 {"TrimString", TrimString},
590 {"VFormat", sm_vformat},