core/smn_string.cpp
author Scott Ehlert <ds@alliedmods.net>
Sun Mar 30 07:00:22 2008 +0000 (2008-03-30)
changeset 1952 b7aa2b626807
child 2004 c9f1dc4cdaed
permissions -rw-r--r--
Spring Cleaning, Part Ichi (1)
Various minor things done to project files
Updated sample extension project file and updated makefile to the new unified version (more changes likely on the way)
Updated regex project file and makefile
     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
    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 static char g_formatbuf[2048];
   196 static cell_t sm_format(IPluginContext *pCtx, const cell_t *params)
   197 {
   198 	char *buf, *fmt, *destbuf;
   199 	cell_t start_addr, end_addr, maxparam;
   200 	size_t res, maxlen;
   201 	int arg = 4;
   202 	bool copy = false;
   203 
   204 	pCtx->LocalToString(params[1], &destbuf);
   205 	pCtx->LocalToString(params[3], &fmt);
   206 
   207 	maxlen = static_cast<size_t>(params[2]);
   208 	start_addr = params[1];
   209 	end_addr = params[1] + maxlen;
   210 	maxparam = params[0];
   211 
   212 	for (cell_t i=3; i<=maxparam; i++)
   213 	{
   214 		if ((params[i] >= start_addr) && (params[i] <= end_addr))
   215 		{
   216 			copy = true;
   217 			break;
   218 		}
   219 	}
   220 	buf = (copy) ? g_formatbuf : destbuf;
   221 	res = atcprintf(buf, maxlen, fmt, pCtx, params, &arg);
   222 
   223 	if (copy)
   224 	{
   225 		memcpy(destbuf, g_formatbuf, res+1);
   226 	}
   227 
   228 	return static_cast<cell_t>(res);
   229 }
   230 
   231 static cell_t sm_vformat(IPluginContext *pContext, const cell_t *params)
   232 {
   233 	int vargPos = static_cast<int>(params[4]);
   234 
   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)));
   238 
   239 	cell_t max = local_params[0];
   240 	if (vargPos > (int)max + 1)
   241 	{
   242 		return pContext->ThrowNativeError("Argument index is invalid: %d", vargPos);
   243 	}
   244 
   245 	cell_t addr_start = params[1];
   246 	cell_t addr_end = addr_start + params[2];
   247 	bool copy = false;
   248 	for (int i=vargPos; i<=max; i++)
   249 	{
   250 		/* Does this clip bounds? */
   251 		if ((local_params[i] >= addr_start)
   252 			&& (local_params[i] <= addr_end))
   253 		{
   254 			copy = true;
   255 			break;
   256 		}
   257 	}
   258 
   259 	/* Get destination info */
   260 	char *format, *destination;
   261 	size_t maxlen = static_cast<size_t>(params[2]);
   262 
   263 	if (copy)
   264 	{
   265 		destination = g_formatbuf;
   266 	} else {
   267 		pContext->LocalToString(params[1], &destination);
   268 	}
   269 
   270 	pContext->LocalToString(params[3], &format);
   271 
   272 	size_t total = atcprintf(destination, maxlen, format, pContext, local_params, &vargPos);
   273 
   274 	/* Perform copy-on-write if we need to */
   275 	if (copy)
   276 	{
   277 		pContext->StringToLocal(params[1], maxlen, g_formatbuf);
   278 	}
   279 
   280 	return total;
   281 }
   282 
   283 /* :TODO: make this UTF8 safe */
   284 static cell_t BreakString(IPluginContext *pContext, const cell_t *params)
   285 {
   286 	const char *input;
   287 	char *out;
   288 	size_t outMax;
   289 
   290 	/* Get parameters */
   291 	pContext->LocalToString(params[1], (char **)&input);
   292 	pContext->LocalToString(params[2], &out);
   293 	outMax = params[3];
   294 
   295 	const char *inptr = input;
   296 	/* Eat up whitespace */
   297 	while (*inptr != '\0' && textparsers->IsWhitespace(inptr))
   298 	{
   299 		inptr++;
   300 	}
   301 
   302 	if (*inptr == '\0')
   303 	{
   304 		if (outMax)
   305 		{
   306 			*out = '\0';
   307 		}
   308 		return -1;
   309 	}
   310 
   311 	const char *start, *end = NULL;
   312 
   313 	bool quoted = (*inptr == '"');
   314 	if (quoted)
   315 	{
   316 		inptr++;
   317 		start = inptr;
   318 		/* Read input until we reach a quote. */
   319 		while (*inptr != '\0' && *inptr != '"')
   320 		{
   321 			/* Update the end point, increment the stream. */
   322 			end = inptr++;
   323 		}
   324 		/* Read one more token if we reached an end quote */
   325 		if (*inptr == '"')
   326 		{
   327 			inptr++;
   328 		}
   329 	} else {
   330 		start = inptr;
   331 		/* Read input until we reach a space */
   332 		while (*inptr != '\0' && !textparsers->IsWhitespace(inptr))
   333 		{
   334 			/* Update the end point, increment the stream. */
   335 			end = inptr++;
   336 		}
   337 	}
   338 
   339 	/* Copy the string we found, if necessary */
   340 	if (end == NULL)
   341 	{
   342 		if (outMax)
   343 		{
   344 			*out = '\0';
   345 		}
   346 	} else if (outMax) {
   347 		char *outptr = out;
   348 		outMax--;
   349 		for (const char *ptr=start;
   350 			(ptr <= end) && ((unsigned)(outptr - out) < (outMax));
   351 			ptr++, outptr++)
   352 			{
   353 			*outptr = *ptr;
   354 		}
   355 		*outptr = '\0';
   356 	}
   357 
   358 	/* Consume more of the string until we reach non-whitespace */
   359 	while (*inptr != '\0' && textparsers->IsWhitespace(inptr))
   360 	{
   361 		inptr++;
   362 	}
   363 
   364 	if (*inptr == '\0')
   365 	{
   366 		return -1;
   367 	}
   368 
   369 	return inptr - input;
   370 }
   371 
   372 static cell_t GetCharBytes(IPluginContext *pContext, const cell_t *params)
   373 {
   374 	char *str;
   375 	pContext->LocalToString(params[1], &str);
   376 
   377 	return _GetUTF8CharBytes(str);
   378 };
   379 
   380 static cell_t IsCharAlpha(IPluginContext *pContext, const cell_t *params)
   381 {
   382 	char chr = params[1];
   383 
   384 	if (_GetUTF8CharBytes(&chr) != 1)
   385 	{
   386 		return 0;
   387 	}
   388 
   389 	return isalpha(chr);
   390 }
   391 
   392 static cell_t IsCharNumeric(IPluginContext *pContext, const cell_t *params)
   393 {
   394 	char chr = params[1];
   395 
   396 	if (_GetUTF8CharBytes(&chr) != 1)
   397 	{
   398 		return 0;
   399 	}
   400 
   401 	return isdigit(chr);
   402 }
   403 
   404 static cell_t IsCharSpace(IPluginContext *pContext, const cell_t *params)
   405 {
   406 	char chr = params[1];
   407 
   408 	if (_GetUTF8CharBytes(&chr) != 1)
   409 	{
   410 		return 0;
   411 	}
   412 
   413 	return isspace(chr);
   414 }
   415 
   416 static cell_t IsCharMB(IPluginContext *pContext, const cell_t *params)
   417 {
   418 	char chr = params[1];
   419 
   420 	unsigned int bytes = _GetUTF8CharBytes(&chr);
   421 	if (bytes == 1)
   422 	{
   423 		return 0;
   424 	}
   425 
   426 	return bytes;
   427 }
   428 
   429 static cell_t IsCharUpper(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 isupper(chr);
   439 }
   440 
   441 static cell_t IsCharLower(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 islower(chr);
   451 }
   452 
   453 static cell_t ReplaceString(IPluginContext *pContext, const cell_t *params)
   454 {
   455 	char *text, *search, *replace;
   456 	size_t maxlength;
   457 	
   458 	pContext->LocalToString(params[1], &text);
   459 	pContext->LocalToString(params[3], &search);
   460 	pContext->LocalToString(params[4], &replace);
   461 	maxlength = (size_t)params[2];
   462 
   463 
   464 	return  UTIL_ReplaceAll(text, maxlength, search, replace);
   465 }
   466 
   467 static cell_t ReplaceStringEx(IPluginContext *pContext, const cell_t *params)
   468 {
   469 	char *text, *search, *replace;
   470 	size_t maxlength;
   471 
   472 	pContext->LocalToString(params[1], &text);
   473 	pContext->LocalToString(params[3], &search);
   474 	pContext->LocalToString(params[4], &replace);
   475 	maxlength = (size_t)params[2];
   476 
   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];
   479 
   480 	char *ptr = UTIL_ReplaceEx(text, maxlength, search, searchLen, replace, replaceLen);
   481 
   482 	if (ptr == NULL)
   483 	{
   484 		return -1;
   485 	}
   486 
   487 	return ptr - text;
   488 }
   489 
   490 static cell_t TrimString(IPluginContext *pContext, const cell_t *params)
   491 {
   492 	char *str;
   493 	pContext->LocalToString(params[1], &str);
   494 
   495 	size_t chars = strlen(str);
   496 
   497 	if (chars == 0)
   498 	{
   499 		return 0;
   500 	}
   501 
   502 	char *end = str + chars - 1;
   503 
   504 	/* Iterate backwards through string until we reach first non-whitespace char */
   505 	while (end >= str && textparsers->IsWhitespace(end))
   506 	{
   507 		end--;
   508 	}
   509 
   510 	/* Replace first whitespace char (at the end) with null terminator */
   511 	*(end + 1) = '\0';
   512 
   513 	/* Iterate forwards through string until first non-whitespace char is reached */
   514 	while (textparsers->IsWhitespace(str))
   515 	{
   516 		str++;
   517 	}
   518 
   519 	size_t bytes;
   520 	pContext->StringToLocalUTF8(params[1], chars + 1, str, &bytes);
   521 
   522 	return bytes;
   523 }
   524 
   525 static cell_t SplitString(IPluginContext *pContext, const cell_t *params)
   526 {
   527 	char *text, *split;
   528 	
   529 	pContext->LocalToString(params[1], &text);
   530 	pContext->LocalToString(params[2], &split);
   531 
   532 	size_t maxLen = (size_t)params[4];
   533 	size_t textLen = strlen(text);
   534 	size_t splitLen = strlen(split);
   535 
   536 	if (splitLen > textLen)
   537 	{
   538 		return -1;
   539 	}
   540 
   541 	/**
   542 	 * Note that it's <= ... you could also just add 1,
   543 	 * but this is a bit nicer
   544 	 */
   545 	for (size_t i=0; i<=textLen - splitLen; i++)
   546 	{
   547 		if (strncmp(&text[i], split, splitLen) == 0)
   548 		{
   549 			/* Split hereeeee */
   550 			if (i >= maxLen)
   551 			{
   552 				pContext->StringToLocalUTF8(params[3], maxLen, text, NULL);
   553 			} else {
   554 				pContext->StringToLocalUTF8(params[3], i+1, text, NULL);
   555 			}
   556 			return (cell_t)(i + splitLen);
   557 		}
   558 	}
   559 
   560 	return -1;
   561 }
   562 
   563 REGISTER_NATIVES(basicStrings)
   564 {
   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},
   591 	{NULL,					NULL},
   592 };