core/smn_string.cpp
author David Anderson <dvander@alliedmods.net>
Fri Oct 02 03:47:42 2009 -0700 (2009-10-02)
changeset 2815 2c7137f077fe
parent 2637 ed7b03e3bf16
child 3002 409a6de13f09
permissions -rw-r--r--
Re-enabled PDB files.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     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.
    11  * 
    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
    15  * details.
    16  *
    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/>.
    19  *
    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>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <string.h>
    33 #include <stdlib.h>
    34 #include "sm_globals.h"
    35 #include "sm_stringutil.h"
    36 #include <ITextParsers.h>
    37 #include <ctype.h>
    38 
    39 inline const char *_strstr(const char *str, const char *substr)
    40 {
    41 #ifdef PLATFORM_WINDOWS
    42 	return strstr(str, substr);
    43 #elif defined PLATFORM_LINUX || defined PLATFORM_APPLE
    44 	return (const char *)strstr(str, substr);
    45 #endif
    46 }
    47 
    48 /*********************************************
    49 *                                            *
    50 * STRING MANIPULATION NATIVE IMPLEMENTATIONS *
    51 *                                            *
    52 *********************************************/
    53 
    54 static cell_t sm_strlen(IPluginContext *pCtx, const cell_t *params)
    55 {
    56 	char *str;
    57 	pCtx->LocalToString(params[1], &str);
    58 
    59 	return strlen(str);
    60 }
    61 
    62 static cell_t sm_contain(IPluginContext *pCtx, const cell_t *params)
    63 {
    64 	typedef const char *(*STRSEARCH)(const char *, const char *);
    65 	STRSEARCH func;
    66 	char *str, *substr;
    67 
    68 	pCtx->LocalToString(params[1], &str);
    69 	pCtx->LocalToString(params[2], &substr);
    70 
    71 	func = (params[3]) ? _strstr : stristr;
    72 	const char *pos = func(str, substr);
    73 	if (pos)
    74 	{
    75 		return (pos - str);
    76 	}
    77 
    78 	return -1;
    79 }
    80 
    81 static cell_t sm_strcmp(IPluginContext *pCtx, const cell_t *params)
    82 {
    83 	typedef int (*STRCOMPARE)(const char *, const char *);
    84 	STRCOMPARE func;
    85 	char *str1, *str2;
    86 
    87 	pCtx->LocalToString(params[1], &str1);
    88 	pCtx->LocalToString(params[2], &str2);
    89 
    90 	func = (params[3]) ? strcmp : stricmp;
    91 
    92 	return (func(str1, str2));
    93 }
    94 
    95 static cell_t sm_strncmp(IPluginContext *pCtx, const cell_t *params)
    96 {
    97 	char *str1, *str2;
    98 
    99 	pCtx->LocalToString(params[1], &str1);
   100 	pCtx->LocalToString(params[2], &str2);
   101 	
   102 	if  (params[4])
   103 	{
   104 		return strncmp(str1, str2, (size_t)params[3]);
   105 	} else {
   106 		return strncasecmp(str1, str2, (size_t)params[3]);
   107 	}
   108 }
   109 
   110 static cell_t sm_strcopy(IPluginContext *pCtx, const cell_t *params)
   111 {
   112 	char *dest, *src;
   113 
   114 	pCtx->LocalToString(params[1], &dest);
   115 	pCtx->LocalToString(params[3], &src);
   116 
   117 	return strncopy(dest, src, params[2]);
   118 }
   119 
   120 static cell_t sm_strconvint(IPluginContext *pCtx, const cell_t *params)
   121 {
   122 	char *str, *dummy;
   123 	pCtx->LocalToString(params[1], &str);
   124 
   125 	return static_cast<cell_t>(strtol(str, &dummy, params[2]));
   126 }
   127 
   128 static cell_t StringToIntEx(IPluginContext *pCtx, const cell_t *params)
   129 {
   130 	char *str, *dummy = NULL;
   131 	cell_t *addr;
   132 	pCtx->LocalToString(params[1], &str);
   133 	pCtx->LocalToPhysAddr(params[2], &addr);
   134 
   135 	*addr = static_cast<cell_t>(strtol(str, &dummy, params[3]));
   136 
   137 	return dummy - str;
   138 }
   139 
   140 static cell_t sm_numtostr(IPluginContext *pCtx, const cell_t *params)
   141 {
   142 	char *str;
   143 	pCtx->LocalToString(params[2], &str);
   144 	size_t res = UTIL_Format(str, params[3], "%d", params[1]);
   145 
   146 	return static_cast<cell_t>(res);
   147 }
   148 
   149 static cell_t sm_strtofloat(IPluginContext *pCtx, const cell_t *params)
   150 {
   151 	char *str, *dummy;
   152 	pCtx->LocalToString(params[1], &str);
   153 
   154 	float val = (float)strtod(str, &dummy);
   155 
   156 	return sp_ftoc(val);
   157 }
   158 
   159 static cell_t StringToFloatEx(IPluginContext *pCtx, const cell_t *params)
   160 {
   161 	char *str, *dummy = NULL;
   162 	cell_t *addr;
   163 	pCtx->LocalToString(params[1], &str);
   164 	pCtx->LocalToPhysAddr(params[2], &addr);
   165 
   166 	float val = (float)strtod(str, &dummy);
   167 
   168 	*addr = sp_ftoc(val);
   169 
   170 	return dummy - str;
   171 }
   172 
   173 static cell_t sm_floattostr(IPluginContext *pCtx, const cell_t *params)
   174 {
   175 	char *str;
   176 	pCtx->LocalToString(params[2], &str);
   177 	size_t res = UTIL_Format(str, params[3], "%f", sp_ctof(params[1]));
   178 
   179 	return static_cast<cell_t>(res);
   180 }
   181 
   182 static cell_t sm_formatex(IPluginContext *pCtx, const cell_t *params)
   183 {
   184 	char *buf, *fmt;
   185 	size_t res;
   186 	int arg = 4;
   187 
   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);
   191 
   192 	return static_cast<cell_t>(res);
   193 }
   194 
   195 class StaticCharBuf
   196 {
   197     char *buffer;
   198     size_t max_size;
   199 public:
   200     StaticCharBuf() : buffer(NULL), max_size(0)
   201     {
   202     }
   203     ~StaticCharBuf()
   204     {
   205         free(buffer);
   206     }
   207     char* GetWithSize(size_t len)
   208     {
   209         if (len > max_size)
   210         {
   211             buffer = (char *)realloc(buffer, len);
   212             max_size = len;
   213         }
   214         return buffer;
   215     }
   216 };
   217 
   218 static char g_formatbuf[2048];
   219 static StaticCharBuf g_extrabuf;
   220 static cell_t sm_format(IPluginContext *pCtx, const cell_t *params)
   221 {
   222 	char *buf, *fmt, *destbuf;
   223 	cell_t start_addr, end_addr, maxparam;
   224 	size_t res, maxlen;
   225 	int arg = 4;
   226 	bool copy = false;
   227 	char *__copy_buf;
   228 
   229 	pCtx->LocalToString(params[1], &destbuf);
   230 	pCtx->LocalToString(params[3], &fmt);
   231 
   232 	maxlen = static_cast<size_t>(params[2]);
   233 	start_addr = params[1];
   234 	end_addr = params[1] + maxlen;
   235 	maxparam = params[0];
   236 
   237 	for (cell_t i=3; i<=maxparam; i++)
   238 	{
   239 		if ((params[i] >= start_addr) && (params[i] <= end_addr))
   240 		{
   241 			copy = true;
   242 			break;
   243 		}
   244 	}
   245 
   246 	if (copy)
   247 	{
   248 		if (maxlen > sizeof(g_formatbuf))
   249 		{
   250 			__copy_buf = g_extrabuf.GetWithSize(maxlen);
   251 		}
   252 		else
   253 		{
   254 			__copy_buf = g_formatbuf;
   255 		}
   256 	}
   257 
   258 	buf = (copy) ? __copy_buf : destbuf;
   259 	res = atcprintf(buf, maxlen, fmt, pCtx, params, &arg);
   260 
   261 	if (copy)
   262 	{
   263 		memcpy(destbuf, __copy_buf, res+1);
   264 	}
   265 
   266 	return static_cast<cell_t>(res);
   267 }
   268 
   269 static cell_t sm_vformat(IPluginContext *pContext, const cell_t *params)
   270 {
   271 	int vargPos = static_cast<int>(params[4]);
   272 
   273 	/* Get the parent parameter array */
   274 	cell_t *local_params = pContext->GetLocalParams();
   275 
   276 	cell_t max = local_params[0];
   277 	if (vargPos > (int)max + 1)
   278 	{
   279 		return pContext->ThrowNativeError("Argument index is invalid: %d", vargPos);
   280 	}
   281 
   282 	cell_t addr_start = params[1];
   283 	cell_t addr_end = addr_start + params[2];
   284 	bool copy = false;
   285 	for (int i=vargPos; i<=max; i++)
   286 	{
   287 		/* Does this clip bounds? */
   288 		if ((local_params[i] >= addr_start)
   289 			&& (local_params[i] <= addr_end))
   290 		{
   291 			copy = true;
   292 			break;
   293 		}
   294 	}
   295 
   296 	/* Get destination info */
   297 	char *format, *destination;
   298 	size_t maxlen = static_cast<size_t>(params[2]);
   299 
   300 	if (copy)
   301 	{
   302 		destination = g_formatbuf;
   303 	} else {
   304 		pContext->LocalToString(params[1], &destination);
   305 	}
   306 
   307 	pContext->LocalToString(params[3], &format);
   308 
   309 	size_t total = atcprintf(destination, maxlen, format, pContext, local_params, &vargPos);
   310 
   311 	/* Perform copy-on-write if we need to */
   312 	if (copy)
   313 	{
   314 		pContext->StringToLocal(params[1], maxlen, g_formatbuf);
   315 	}
   316 
   317 	return total;
   318 }
   319 
   320 /* :TODO: make this UTF8 safe */
   321 static cell_t BreakString(IPluginContext *pContext, const cell_t *params)
   322 {
   323 	const char *input;
   324 	char *out;
   325 	size_t outMax;
   326 
   327 	/* Get parameters */
   328 	pContext->LocalToString(params[1], (char **)&input);
   329 	pContext->LocalToString(params[2], &out);
   330 	outMax = params[3];
   331 
   332 	const char *inptr = input;
   333 	/* Eat up whitespace */
   334 	while (*inptr != '\0' && textparsers->IsWhitespace(inptr))
   335 	{
   336 		inptr++;
   337 	}
   338 
   339 	if (*inptr == '\0')
   340 	{
   341 		if (outMax)
   342 		{
   343 			*out = '\0';
   344 		}
   345 		return -1;
   346 	}
   347 
   348 	const char *start, *end = NULL;
   349 
   350 	bool quoted = (*inptr == '"');
   351 	if (quoted)
   352 	{
   353 		inptr++;
   354 		start = inptr;
   355 		/* Read input until we reach a quote. */
   356 		while (*inptr != '\0' && *inptr != '"')
   357 		{
   358 			/* Update the end point, increment the stream. */
   359 			end = inptr++;
   360 		}
   361 		/* Read one more token if we reached an end quote */
   362 		if (*inptr == '"')
   363 		{
   364 			inptr++;
   365 		}
   366 	} else {
   367 		start = inptr;
   368 		/* Read input until we reach a space */
   369 		while (*inptr != '\0' && !textparsers->IsWhitespace(inptr))
   370 		{
   371 			/* Update the end point, increment the stream. */
   372 			end = inptr++;
   373 		}
   374 	}
   375 
   376 	/* Copy the string we found, if necessary */
   377 	if (end == NULL)
   378 	{
   379 		if (outMax)
   380 		{
   381 			*out = '\0';
   382 		}
   383 	} else if (outMax) {
   384 		char *outptr = out;
   385 		outMax--;
   386 		for (const char *ptr=start;
   387 			(ptr <= end) && ((unsigned)(outptr - out) < (outMax));
   388 			ptr++, outptr++)
   389 			{
   390 			*outptr = *ptr;
   391 		}
   392 		*outptr = '\0';
   393 	}
   394 
   395 	/* Consume more of the string until we reach non-whitespace */
   396 	while (*inptr != '\0' && textparsers->IsWhitespace(inptr))
   397 	{
   398 		inptr++;
   399 	}
   400 
   401 	if (*inptr == '\0')
   402 	{
   403 		return -1;
   404 	}
   405 
   406 	return inptr - input;
   407 }
   408 
   409 static cell_t GetCharBytes(IPluginContext *pContext, const cell_t *params)
   410 {
   411 	char *str;
   412 	pContext->LocalToString(params[1], &str);
   413 
   414 	return _GetUTF8CharBytes(str);
   415 };
   416 
   417 static cell_t IsCharAlpha(IPluginContext *pContext, const cell_t *params)
   418 {
   419 	char chr = params[1];
   420 
   421 	if (_GetUTF8CharBytes(&chr) != 1)
   422 	{
   423 		return 0;
   424 	}
   425 
   426 	return isalpha(chr);
   427 }
   428 
   429 static cell_t IsCharNumeric(IPluginContext *pContext, const cell_t *params)
   430 {
   431 	char chr = params[1];
   432 
   433 	if (_GetUTF8CharBytes(&chr) != 1)
   434 	{
   435 		return 0;
   436 	}
   437 
   438 	return isdigit(chr);
   439 }
   440 
   441 static cell_t IsCharSpace(IPluginContext *pContext, const cell_t *params)
   442 {
   443 	char chr = params[1];
   444 
   445 	if (_GetUTF8CharBytes(&chr) != 1)
   446 	{
   447 		return 0;
   448 	}
   449 
   450 	return isspace(chr);
   451 }
   452 
   453 static cell_t IsCharMB(IPluginContext *pContext, const cell_t *params)
   454 {
   455 	char chr = params[1];
   456 
   457 	unsigned int bytes = _GetUTF8CharBytes(&chr);
   458 	if (bytes == 1)
   459 	{
   460 		return 0;
   461 	}
   462 
   463 	return bytes;
   464 }
   465 
   466 static cell_t IsCharUpper(IPluginContext *pContext, const cell_t *params)
   467 {
   468 	char chr = params[1];
   469 
   470 	if (_GetUTF8CharBytes(&chr) != 1)
   471 	{
   472 		return 0;
   473 	}
   474 
   475 	return isupper(chr);
   476 }
   477 
   478 static cell_t IsCharLower(IPluginContext *pContext, const cell_t *params)
   479 {
   480 	char chr = params[1];
   481 
   482 	if (_GetUTF8CharBytes(&chr) != 1)
   483 	{
   484 		return 0;
   485 	}
   486 
   487 	return islower(chr);
   488 }
   489 
   490 static cell_t ReplaceString(IPluginContext *pContext, const cell_t *params)
   491 {
   492 	char *text, *search, *replace;
   493 	size_t maxlength;
   494 	bool caseSensitive;
   495 	
   496 	pContext->LocalToString(params[1], &text);
   497 	pContext->LocalToString(params[3], &search);
   498 	pContext->LocalToString(params[4], &replace);
   499 	maxlength = (size_t)params[2];
   500 
   501 	/* Account for old binary plugins that won't pass the last parameter */
   502 	if (params[0] == 5) 
   503 	{
   504 		caseSensitive = (bool)params[5];
   505 	}
   506 	else
   507 	{
   508 		caseSensitive = true;
   509 	}
   510 
   511 	if (search[0] == '\0')
   512 	{
   513 		return pContext->ThrowNativeError("Cannot replace searches of empty strings");
   514 	}
   515 
   516 	return UTIL_ReplaceAll(text, maxlength, search, replace, caseSensitive);
   517 }
   518 
   519 static cell_t ReplaceStringEx(IPluginContext *pContext, const cell_t *params)
   520 {
   521 	char *text, *search, *replace;
   522 	size_t maxlength;
   523 	bool caseSensitive;
   524 
   525 	pContext->LocalToString(params[1], &text);
   526 	pContext->LocalToString(params[3], &search);
   527 	pContext->LocalToString(params[4], &replace);
   528 	maxlength = (size_t)params[2];
   529 
   530 	size_t searchLen = (params[5] == -1) ? strlen(search) : (size_t)params[5];
   531 	size_t replaceLen = (params[6] == -1) ? strlen(replace) : (size_t)params[6];
   532 
   533 	/* Account for old binary plugins that won't pass the last parameter */
   534 	if (params[0] == 7)
   535 	{
   536 		caseSensitive = (bool)params[7];
   537 	}
   538 	else
   539 	{
   540 		caseSensitive = true;
   541 	}
   542 
   543 	if (searchLen == 0)
   544 	{
   545 		return pContext->ThrowNativeError("Cannot replace searches of empty strings");
   546 	}
   547 
   548 	char *ptr = UTIL_ReplaceEx(text, maxlength, search, searchLen, replace, replaceLen, caseSensitive);
   549 
   550 	if (ptr == NULL)
   551 	{
   552 		return -1;
   553 	}
   554 
   555 	return ptr - text;
   556 }
   557 
   558 static cell_t TrimString(IPluginContext *pContext, const cell_t *params)
   559 {
   560 	char *str;
   561 	pContext->LocalToString(params[1], &str);
   562 
   563 	size_t chars = strlen(str);
   564 
   565 	if (chars == 0)
   566 	{
   567 		return 0;
   568 	}
   569 
   570 	char *end = str + chars - 1;
   571 
   572 	/* Iterate backwards through string until we reach first non-whitespace char */
   573 	while (end >= str && textparsers->IsWhitespace(end))
   574 	{
   575 		end--;
   576 	}
   577 
   578 	/* Replace first whitespace char (at the end) with null terminator */
   579 	*(end + 1) = '\0';
   580 
   581 	/* Iterate forwards through string until first non-whitespace char is reached */
   582 	while (textparsers->IsWhitespace(str))
   583 	{
   584 		str++;
   585 	}
   586 
   587 	size_t bytes;
   588 	pContext->StringToLocalUTF8(params[1], chars + 1, str, &bytes);
   589 
   590 	return bytes;
   591 }
   592 
   593 static cell_t SplitString(IPluginContext *pContext, const cell_t *params)
   594 {
   595 	char *text, *split;
   596 	
   597 	pContext->LocalToString(params[1], &text);
   598 	pContext->LocalToString(params[2], &split);
   599 
   600 	size_t maxLen = (size_t)params[4];
   601 	size_t textLen = strlen(text);
   602 	size_t splitLen = strlen(split);
   603 
   604 	if (splitLen > textLen)
   605 	{
   606 		return -1;
   607 	}
   608 
   609 	/**
   610 	 * Note that it's <= ... you could also just add 1,
   611 	 * but this is a bit nicer
   612 	 */
   613 	for (size_t i=0; i<=textLen - splitLen; i++)
   614 	{
   615 		if (strncmp(&text[i], split, splitLen) == 0)
   616 		{
   617 			/* Split hereeeee */
   618 			if (i >= maxLen)
   619 			{
   620 				pContext->StringToLocalUTF8(params[3], maxLen, text, NULL);
   621 			} else {
   622 				pContext->StringToLocalUTF8(params[3], i+1, text, NULL);
   623 			}
   624 			return (cell_t)(i + splitLen);
   625 		}
   626 	}
   627 
   628 	return -1;
   629 }
   630 
   631 static cell_t StripQuotes(IPluginContext *pContext, const cell_t *params)
   632 {
   633 	char *text;
   634 	size_t length;
   635 
   636 	pContext->LocalToString(params[1], &text);
   637 	length = strlen(text);
   638 
   639 	if (text[0] == '"' && text[length-1] == '"')
   640 	{
   641 		/* Null-terminate */
   642 		text[--length] = '\0';
   643 		/* Move the remaining bytes (including null terminator) down by one */
   644 		memmove(&text[0], &text[1], length);
   645 
   646 		return 1;
   647 	}
   648 
   649 	return 0;	
   650 }
   651 
   652 REGISTER_NATIVES(basicStrings)
   653 {
   654 	{"BreakString",			BreakString},
   655 	{"FloatToString",		sm_floattostr},
   656 	{"Format",				sm_format},
   657 	{"FormatEx",			sm_formatex},
   658 	{"GetCharBytes",		GetCharBytes},
   659 	{"IntToString",			sm_numtostr},
   660 	{"IsCharAlpha",			IsCharAlpha},
   661 	{"IsCharLower",			IsCharLower},
   662 	{"IsCharMB",			IsCharMB},
   663 	{"IsCharNumeric",		IsCharNumeric},
   664 	{"IsCharSpace",			IsCharSpace},
   665 	{"IsCharUpper",			IsCharUpper},
   666 	{"ReplaceString",		ReplaceString},
   667 	{"ReplaceStringEx",		ReplaceStringEx},
   668 	{"SplitString",			SplitString},
   669 	{"strlen",				sm_strlen},
   670 	{"StrContains",			sm_contain},
   671 	{"strcmp",				sm_strcmp},
   672 	{"strncmp",				sm_strncmp},
   673 	{"strcopy",				sm_strcopy},
   674 	{"StringToInt",			sm_strconvint},
   675 	{"StringToIntEx",		StringToIntEx},
   676 	{"StringToFloat",		sm_strtofloat},
   677 	{"StringToFloatEx",		StringToFloatEx},
   678 	{"StripQuotes",			StripQuotes},
   679 	{"TrimString",			TrimString},
   680 	{"VFormat",				sm_vformat},
   681 	{NULL,					NULL},
   682 };