core/smn_string.cpp
author David Anderson <dvander@alliedmods.net>
Sun Jul 08 18:00:35 2007 +0000 (2007-07-08)
changeset 1072 79fb41b647d5
parent 834 d6fac170d744
child 1231 17badaf66ce4
permissions -rw-r--r--
added requests amb565 and amb445
     1 /**
     2  * vim: set ts=4 :
     3  * ===============================================================
     4  * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     5  * ===============================================================
     6  *
     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
    11  *
    12  * Version: $Id$
    13  */
    14 
    15 #include <string.h>
    16 #include <stdlib.h>
    17 #include "sm_globals.h"
    18 #include "sm_stringutil.h"
    19 #include "TextParsers.h"
    20 #include <ctype.h>
    21 
    22 inline const char *_strstr(const char *str, const char *substr)
    23 {
    24 #ifdef PLATFORM_WINDOWS
    25 	return strstr(str, substr);
    26 #elif defined PLATFORM_LINUX
    27 	return (const char *)strstr(str, substr);
    28 #endif
    29 }
    30 
    31 /*********************************************
    32 *                                            *
    33 * STRING MANIPULATION NATIVE IMPLEMENTATIONS *
    34 *                                            *
    35 *********************************************/
    36 
    37 static cell_t sm_strlen(IPluginContext *pCtx, const cell_t *params)
    38 {
    39 	char *str;
    40 	pCtx->LocalToString(params[1], &str);
    41 
    42 	return strlen(str);
    43 }
    44 
    45 static cell_t sm_contain(IPluginContext *pCtx, const cell_t *params)
    46 {
    47 	typedef const char *(*STRSEARCH)(const char *, const char *);
    48 	STRSEARCH func;
    49 	char *str, *substr;
    50 
    51 	pCtx->LocalToString(params[1], &str);
    52 	pCtx->LocalToString(params[2], &substr);
    53 
    54 	func = (params[3]) ? _strstr : stristr;
    55 	const char *pos = func(str, substr);
    56 	if (pos)
    57 	{
    58 		return (pos - str);
    59 	}
    60 
    61 	return -1;
    62 }
    63 
    64 static cell_t sm_strcmp(IPluginContext *pCtx, const cell_t *params)
    65 {
    66 	typedef int (*STRCOMPARE)(const char *, const char *);
    67 	STRCOMPARE func;
    68 	char *str1, *str2;
    69 
    70 	pCtx->LocalToString(params[1], &str1);
    71 	pCtx->LocalToString(params[2], &str2);
    72 
    73 	func = (params[3]) ? strcmp : stricmp;
    74 
    75 	return (func(str1, str2));
    76 }
    77 
    78 static cell_t sm_strncmp(IPluginContext *pCtx, const cell_t *params)
    79 {
    80 	char *str1, *str2;
    81 
    82 	pCtx->LocalToString(params[1], &str1);
    83 	pCtx->LocalToString(params[2], &str2);
    84 	
    85 	if  (params[4])
    86 	{
    87 		return strncmp(str1, str2, (size_t)params[3]);
    88 	} else {
    89 		return strncasecmp(str1, str2, (size_t)params[3]);
    90 	}
    91 }
    92 
    93 static cell_t sm_strcopy(IPluginContext *pCtx, const cell_t *params)
    94 {
    95 	char *dest, *src;
    96 
    97 	pCtx->LocalToString(params[1], &dest);
    98 	pCtx->LocalToString(params[3], &src);
    99 
   100 	return strncopy(dest, src, params[2]);
   101 }
   102 
   103 static cell_t sm_strconvint(IPluginContext *pCtx, const cell_t *params)
   104 {
   105 	char *str, *dummy;
   106 	pCtx->LocalToString(params[1], &str);
   107 
   108 	return static_cast<cell_t>(strtol(str, &dummy, params[2]));
   109 }
   110 
   111 static cell_t StringToIntEx(IPluginContext *pCtx, const cell_t *params)
   112 {
   113 	char *str, *dummy = NULL;
   114 	cell_t *addr;
   115 	pCtx->LocalToString(params[1], &str);
   116 	pCtx->LocalToPhysAddr(params[2], &addr);
   117 
   118 	*addr = static_cast<cell_t>(strtol(str, &dummy, params[3]));
   119 
   120 	return dummy - str;
   121 }
   122 
   123 static cell_t sm_numtostr(IPluginContext *pCtx, const cell_t *params)
   124 {
   125 	char *str;
   126 	pCtx->LocalToString(params[2], &str);
   127 	size_t res = UTIL_Format(str, params[3], "%d", params[1]);
   128 
   129 	return static_cast<cell_t>(res);
   130 }
   131 
   132 static cell_t sm_strtofloat(IPluginContext *pCtx, const cell_t *params)
   133 {
   134 	char *str, *dummy;
   135 	pCtx->LocalToString(params[1], &str);
   136 
   137 	float val = (float)strtod(str, &dummy);
   138 
   139 	return sp_ftoc(val);
   140 }
   141 
   142 static cell_t StringToFloatEx(IPluginContext *pCtx, const cell_t *params)
   143 {
   144 	char *str, *dummy = NULL;
   145 	cell_t *addr;
   146 	pCtx->LocalToString(params[1], &str);
   147 	pCtx->LocalToPhysAddr(params[2], &addr);
   148 
   149 	float val = (float)strtod(str, &dummy);
   150 
   151 	*addr = sp_ftoc(val);
   152 
   153 	return dummy - str;
   154 }
   155 
   156 static cell_t sm_floattostr(IPluginContext *pCtx, const cell_t *params)
   157 {
   158 	char *str;
   159 	pCtx->LocalToString(params[2], &str);
   160 	size_t res = UTIL_Format(str, params[3], "%f", sp_ctof(params[1]));
   161 
   162 	return static_cast<cell_t>(res);
   163 }
   164 
   165 static cell_t sm_formatex(IPluginContext *pCtx, const cell_t *params)
   166 {
   167 	char *buf, *fmt;
   168 	size_t res;
   169 	int arg = 4;
   170 
   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);
   174 
   175 	return static_cast<cell_t>(res);
   176 }
   177 
   178 static char g_formatbuf[2048];
   179 static cell_t sm_format(IPluginContext *pCtx, const cell_t *params)
   180 {
   181 	char *buf, *fmt, *destbuf;
   182 	cell_t start_addr, end_addr, maxparam;
   183 	size_t res, maxlen;
   184 	int arg = 4;
   185 	bool copy = false;
   186 
   187 	pCtx->LocalToString(params[1], &destbuf);
   188 	pCtx->LocalToString(params[3], &fmt);
   189 
   190 	maxlen = static_cast<size_t>(params[2]);
   191 	start_addr = params[1];
   192 	end_addr = params[1] + maxlen;
   193 	maxparam = params[0];
   194 
   195 	for (cell_t i=3; i<=maxparam; i++)
   196 	{
   197 		if ((params[i] >= start_addr) && (params[i] <= end_addr))
   198 		{
   199 			copy = true;
   200 			break;
   201 		}
   202 	}
   203 	buf = (copy) ? g_formatbuf : destbuf;
   204 	res = atcprintf(buf, maxlen, fmt, pCtx, params, &arg);
   205 
   206 	if (copy)
   207 	{
   208 		memcpy(destbuf, g_formatbuf, res+1);
   209 	}
   210 
   211 	return static_cast<cell_t>(res);
   212 }
   213 
   214 static cell_t sm_vformat(IPluginContext *pContext, const cell_t *params)
   215 {
   216 	int vargPos = static_cast<int>(params[4]);
   217 
   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)));
   221 
   222 	cell_t max = local_params[0];
   223 	if (vargPos > (int)max + 1)
   224 	{
   225 		return pContext->ThrowNativeError("Argument index is invalid: %d", vargPos);
   226 	}
   227 
   228 	cell_t addr_start = params[1];
   229 	cell_t addr_end = addr_start + params[2];
   230 	bool copy = false;
   231 	for (int i=vargPos; i<=max; i++)
   232 	{
   233 		/* Does this clip bounds? */
   234 		if ((local_params[i] >= addr_start)
   235 			&& (local_params[i] <= addr_end))
   236 		{
   237 			copy = true;
   238 			break;
   239 		}
   240 	}
   241 
   242 	/* Get destination info */
   243 	char *format, *destination;
   244 	size_t maxlen = static_cast<size_t>(params[2]);
   245 
   246 	if (copy)
   247 	{
   248 		destination = g_formatbuf;
   249 	} else {
   250 		pContext->LocalToString(params[1], &destination);
   251 	}
   252 
   253 	pContext->LocalToString(params[3], &format);
   254 
   255 	size_t total = atcprintf(destination, maxlen, format, pContext, local_params, &vargPos);
   256 
   257 	/* Perform copy-on-write if we need to */
   258 	if (copy)
   259 	{
   260 		pContext->StringToLocal(params[1], maxlen, g_formatbuf);
   261 	}
   262 
   263 	return total;
   264 }
   265 
   266 /* :TODO: make this UTF8 safe */
   267 static cell_t BreakString(IPluginContext *pContext, const cell_t *params)
   268 {
   269 	const char *input;
   270 	char *out;
   271 	size_t outMax;
   272 
   273 	/* Get parameters */
   274 	pContext->LocalToString(params[1], (char **)&input);
   275 	pContext->LocalToString(params[2], &out);
   276 	outMax = params[3];
   277 
   278 	const char *inptr = input;
   279 	/* Eat up whitespace */
   280 	while (*inptr != '\0' && IsWhitespace(inptr))
   281 	{
   282 		inptr++;
   283 	}
   284 
   285 	if (*inptr == '\0')
   286 	{
   287 		if (outMax)
   288 		{
   289 			*out = '\0';
   290 		}
   291 		return -1;
   292 	}
   293 
   294 	const char *start, *end = NULL;
   295 
   296 	bool quoted = (*inptr == '"');
   297 	if (quoted)
   298 	{
   299 		inptr++;
   300 		start = inptr;
   301 		/* Read input until we reach a quote. */
   302 		while (*inptr != '\0' && *inptr != '"')
   303 		{
   304 			/* Update the end point, increment the stream. */
   305 			end = inptr++;
   306 		}
   307 		/* Read one more token if we reached an end quote */
   308 		if (*inptr == '"')
   309 		{
   310 			inptr++;
   311 		}
   312 	} else {
   313 		start = inptr;
   314 		/* Read input until we reach a space */
   315 		while (*inptr != '\0' && !IsWhitespace(inptr))
   316 		{
   317 			/* Update the end point, increment the stream. */
   318 			end = inptr++;
   319 		}
   320 	}
   321 
   322 	/* Copy the string we found, if necessary */
   323 	if (end == NULL)
   324 	{
   325 		if (outMax)
   326 		{
   327 			*out = '\0';
   328 		}
   329 	} else if (outMax) {
   330 		char *outptr = out;
   331 		outMax--;
   332 		for (const char *ptr=start;
   333 			(ptr <= end) && ((unsigned)(outptr - out) < (outMax));
   334 			ptr++, outptr++)
   335 			{
   336 			*outptr = *ptr;
   337 		}
   338 		*outptr = '\0';
   339 	}
   340 
   341 	/* Consume more of the string until we reach non-whitespace */
   342 	while (*inptr != '\0' && IsWhitespace(inptr))
   343 	{
   344 		inptr++;
   345 	}
   346 
   347 	if (*inptr == '\0')
   348 	{
   349 		return -1;
   350 	}
   351 
   352 	return inptr - input;
   353 }
   354 
   355 static cell_t GetCharBytes(IPluginContext *pContext, const cell_t *params)
   356 {
   357 	char *str;
   358 	pContext->LocalToString(params[1], &str);
   359 
   360 	return _GetUTF8CharBytes(str);
   361 };
   362 
   363 static cell_t IsCharAlpha(IPluginContext *pContext, const cell_t *params)
   364 {
   365 	char chr = params[1];
   366 
   367 	if (_GetUTF8CharBytes(&chr) != 1)
   368 	{
   369 		return 0;
   370 	}
   371 
   372 	return isalpha(chr);
   373 }
   374 
   375 static cell_t IsCharNumeric(IPluginContext *pContext, const cell_t *params)
   376 {
   377 	char chr = params[1];
   378 
   379 	if (_GetUTF8CharBytes(&chr) != 1)
   380 	{
   381 		return 0;
   382 	}
   383 
   384 	return isdigit(chr);
   385 }
   386 
   387 static cell_t IsCharSpace(IPluginContext *pContext, const cell_t *params)
   388 {
   389 	char chr = params[1];
   390 
   391 	if (_GetUTF8CharBytes(&chr) != 1)
   392 	{
   393 		return 0;
   394 	}
   395 
   396 	return isspace(chr);
   397 }
   398 
   399 static cell_t IsCharMB(IPluginContext *pContext, const cell_t *params)
   400 {
   401 	char chr = params[1];
   402 
   403 	unsigned int bytes = _GetUTF8CharBytes(&chr);
   404 	if (bytes == 1)
   405 	{
   406 		return 0;
   407 	}
   408 
   409 	return bytes;
   410 }
   411 
   412 static cell_t IsCharUpper(IPluginContext *pContext, const cell_t *params)
   413 {
   414 	char chr = params[1];
   415 
   416 	if (_GetUTF8CharBytes(&chr) != 1)
   417 	{
   418 		return 0;
   419 	}
   420 
   421 	return isupper(chr);
   422 }
   423 
   424 static cell_t IsCharLower(IPluginContext *pContext, const cell_t *params)
   425 {
   426 	char chr = params[1];
   427 
   428 	if (_GetUTF8CharBytes(&chr) != 1)
   429 	{
   430 		return 0;
   431 	}
   432 
   433 	return islower(chr);
   434 }
   435 
   436 static cell_t ReplaceString(IPluginContext *pContext, const cell_t *params)
   437 {
   438 	char *text, *search, *replace;
   439 	size_t maxlength;
   440 	
   441 	pContext->LocalToString(params[1], &text);
   442 	pContext->LocalToString(params[3], &search);
   443 	pContext->LocalToString(params[4], &replace);
   444 	maxlength = (size_t)params[2];
   445 
   446 
   447 	return  UTIL_ReplaceAll(text, maxlength, search, replace);
   448 }
   449 
   450 static cell_t ReplaceStringEx(IPluginContext *pContext, const cell_t *params)
   451 {
   452 	char *text, *search, *replace;
   453 	size_t maxlength;
   454 
   455 	pContext->LocalToString(params[1], &text);
   456 	pContext->LocalToString(params[3], &search);
   457 	pContext->LocalToString(params[4], &replace);
   458 	maxlength = (size_t)params[2];
   459 
   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];
   462 
   463 	char *ptr = UTIL_ReplaceEx(text, maxlength, search, searchLen, replace, replaceLen);
   464 
   465 	if (ptr == NULL)
   466 	{
   467 		return -1;
   468 	}
   469 
   470 	return ptr - text;
   471 }
   472 
   473 static cell_t TrimString(IPluginContext *pContext, const cell_t *params)
   474 {
   475 	char *str;
   476 	pContext->LocalToString(params[1], &str);
   477 
   478 	size_t chars = strlen(str);
   479 
   480 	if (chars == 0)
   481 	{
   482 		return 0;
   483 	}
   484 
   485 	char *end = str + chars - 1;
   486 
   487 	/* Iterate backwards through string until we reach first non-whitespace char */
   488 	while (end >= str && IsWhitespace(end))
   489 	{
   490 		end--;
   491 	}
   492 
   493 	/* Replace first whitespace char (at the end) with null terminator */
   494 	*(end + 1) = '\0';
   495 
   496 	/* Iterate forwards through string until first non-whitespace char is reached */
   497 	while (IsWhitespace(str))
   498 	{
   499 		str++;
   500 	}
   501 
   502 	size_t bytes;
   503 	pContext->StringToLocalUTF8(params[1], chars + 1, str, &bytes);
   504 
   505 	return bytes;
   506 }
   507 
   508 static cell_t SplitString(IPluginContext *pContext, const cell_t *params)
   509 {
   510 	char *text, *split;
   511 	
   512 	pContext->LocalToString(params[1], &text);
   513 	pContext->LocalToString(params[2], &split);
   514 
   515 	size_t maxLen = (size_t)params[4];
   516 	size_t textLen = strlen(text);
   517 	size_t splitLen = strlen(split);
   518 
   519 	if (splitLen > textLen)
   520 	{
   521 		return -1;
   522 	}
   523 
   524 	for (size_t i=0; i<textLen - splitLen; i++)
   525 	{
   526 		if (strncmp(&text[i], split, splitLen) == 0)
   527 		{
   528 			/* Split hereeeee */
   529 			if (i >= maxLen)
   530 			{
   531 				pContext->StringToLocalUTF8(params[3], maxLen, text, NULL);
   532 			} else {
   533 				pContext->StringToLocalUTF8(params[3], i+1, text, NULL);
   534 			}
   535 			return (cell_t)(i + splitLen);
   536 		}
   537 	}
   538 
   539 	return -1;
   540 }
   541 
   542 REGISTER_NATIVES(basicStrings)
   543 {
   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},
   573 	{NULL,					NULL},
   574 };