core/smn_string.cpp
author David Anderson <dvander@alliedmods.net>
Wed Jun 06 06:27:47 2007 +0000 (2007-06-06)
changeset 884 a5f3239cef1a
parent 834 d6fac170d744
child 1072 79fb41b647d5
permissions -rw-r--r--
- vote menus now get cancelled properly (and get a new callback with it)
- added api for seeing if a menu is in a VoteDisplay
- fixed another item count bug
- removed some cruft from the vote handler header
     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 sm_numtostr(IPluginContext *pCtx, const cell_t *params)
   112 {
   113 	char *str;
   114 	pCtx->LocalToString(params[2], &str);
   115 	size_t res = UTIL_Format(str, params[3], "%d", params[1]);
   116 
   117 	return static_cast<cell_t>(res);
   118 }
   119 
   120 static cell_t sm_strtofloat(IPluginContext *pCtx, const cell_t *params)
   121 {
   122 	char *str, *dummy;
   123 	pCtx->LocalToString(params[1], &str);
   124 
   125 	float val = (float)strtod(str, &dummy);
   126 
   127 	return sp_ftoc(val);
   128 }
   129 
   130 static cell_t sm_floattostr(IPluginContext *pCtx, const cell_t *params)
   131 {
   132 	char *str;
   133 	pCtx->LocalToString(params[2], &str);
   134 	size_t res = UTIL_Format(str, params[3], "%f", sp_ctof(params[1]));
   135 
   136 	return static_cast<cell_t>(res);
   137 }
   138 
   139 static cell_t sm_formatex(IPluginContext *pCtx, const cell_t *params)
   140 {
   141 	char *buf, *fmt;
   142 	size_t res;
   143 	int arg = 4;
   144 
   145 	pCtx->LocalToString(params[1], &buf);
   146 	pCtx->LocalToString(params[3], &fmt);
   147 	res = atcprintf(buf, static_cast<size_t>(params[2]), fmt, pCtx, params, &arg);
   148 
   149 	return static_cast<cell_t>(res);
   150 }
   151 
   152 static char g_formatbuf[2048];
   153 static cell_t sm_format(IPluginContext *pCtx, const cell_t *params)
   154 {
   155 	char *buf, *fmt, *destbuf;
   156 	cell_t start_addr, end_addr, maxparam;
   157 	size_t res, maxlen;
   158 	int arg = 4;
   159 	bool copy = false;
   160 
   161 	pCtx->LocalToString(params[1], &destbuf);
   162 	pCtx->LocalToString(params[3], &fmt);
   163 
   164 	maxlen = static_cast<size_t>(params[2]);
   165 	start_addr = params[1];
   166 	end_addr = params[1] + maxlen;
   167 	maxparam = params[0];
   168 
   169 	for (cell_t i=3; i<=maxparam; i++)
   170 	{
   171 		if ((params[i] >= start_addr) && (params[i] <= end_addr))
   172 		{
   173 			copy = true;
   174 			break;
   175 		}
   176 	}
   177 	buf = (copy) ? g_formatbuf : destbuf;
   178 	res = atcprintf(buf, maxlen, fmt, pCtx, params, &arg);
   179 
   180 	if (copy)
   181 	{
   182 		memcpy(destbuf, g_formatbuf, res+1);
   183 	}
   184 
   185 	return static_cast<cell_t>(res);
   186 }
   187 
   188 static cell_t sm_vformat(IPluginContext *pContext, const cell_t *params)
   189 {
   190 	int vargPos = static_cast<int>(params[4]);
   191 
   192 	/* Get the parent parameter array */
   193 	sp_context_t *ctx = pContext->GetContext();
   194 	cell_t *local_params = (cell_t *)(ctx->memory + ctx->frm + (2 * sizeof(cell_t)));
   195 
   196 	cell_t max = local_params[0];
   197 	if (vargPos > (int)max + 1)
   198 	{
   199 		return pContext->ThrowNativeError("Argument index is invalid: %d", vargPos);
   200 	}
   201 
   202 	cell_t addr_start = params[1];
   203 	cell_t addr_end = addr_start + params[2];
   204 	bool copy = false;
   205 	for (int i=vargPos; i<=max; i++)
   206 	{
   207 		/* Does this clip bounds? */
   208 		if ((local_params[i] >= addr_start)
   209 			&& (local_params[i] <= addr_end))
   210 		{
   211 			copy = true;
   212 			break;
   213 		}
   214 	}
   215 
   216 	/* Get destination info */
   217 	char *format, *destination;
   218 	size_t maxlen = static_cast<size_t>(params[2]);
   219 
   220 	if (copy)
   221 	{
   222 		destination = g_formatbuf;
   223 	} else {
   224 		pContext->LocalToString(params[1], &destination);
   225 	}
   226 
   227 	pContext->LocalToString(params[3], &format);
   228 
   229 	size_t total = atcprintf(destination, maxlen, format, pContext, local_params, &vargPos);
   230 
   231 	/* Perform copy-on-write if we need to */
   232 	if (copy)
   233 	{
   234 		pContext->StringToLocal(params[1], maxlen, g_formatbuf);
   235 	}
   236 
   237 	return total;
   238 }
   239 
   240 /* :TODO: make this UTF8 safe */
   241 static cell_t BreakString(IPluginContext *pContext, const cell_t *params)
   242 {
   243 	const char *input;
   244 	char *out;
   245 	size_t outMax;
   246 
   247 	/* Get parameters */
   248 	pContext->LocalToString(params[1], (char **)&input);
   249 	pContext->LocalToString(params[2], &out);
   250 	outMax = params[3];
   251 
   252 	const char *inptr = input;
   253 	/* Eat up whitespace */
   254 	while (*inptr != '\0' && IsWhitespace(inptr))
   255 	{
   256 		inptr++;
   257 	}
   258 
   259 	if (*inptr == '\0')
   260 	{
   261 		if (outMax)
   262 		{
   263 			*out = '\0';
   264 		}
   265 		return -1;
   266 	}
   267 
   268 	const char *start, *end = NULL;
   269 
   270 	bool quoted = (*inptr == '"');
   271 	if (quoted)
   272 	{
   273 		inptr++;
   274 		start = inptr;
   275 		/* Read input until we reach a quote. */
   276 		while (*inptr != '\0' && *inptr != '"')
   277 		{
   278 			/* Update the end point, increment the stream. */
   279 			end = inptr++;
   280 		}
   281 		/* Read one more token if we reached an end quote */
   282 		if (*inptr == '"')
   283 		{
   284 			inptr++;
   285 		}
   286 	} else {
   287 		start = inptr;
   288 		/* Read input until we reach a space */
   289 		while (*inptr != '\0' && !IsWhitespace(inptr))
   290 		{
   291 			/* Update the end point, increment the stream. */
   292 			end = inptr++;
   293 		}
   294 	}
   295 
   296 	/* Copy the string we found, if necessary */
   297 	if (end == NULL)
   298 	{
   299 		if (outMax)
   300 		{
   301 			*out = '\0';
   302 		}
   303 	} else if (outMax) {
   304 		char *outptr = out;
   305 		outMax--;
   306 		for (const char *ptr=start;
   307 			(ptr <= end) && ((unsigned)(outptr - out) < (outMax));
   308 			ptr++, outptr++)
   309 			{
   310 			*outptr = *ptr;
   311 		}
   312 		*outptr = '\0';
   313 	}
   314 
   315 	/* Consume more of the string until we reach non-whitespace */
   316 	while (*inptr != '\0' && IsWhitespace(inptr))
   317 	{
   318 		inptr++;
   319 	}
   320 
   321 	if (*inptr == '\0')
   322 	{
   323 		return -1;
   324 	}
   325 
   326 	return inptr - input;
   327 }
   328 
   329 static cell_t GetCharBytes(IPluginContext *pContext, const cell_t *params)
   330 {
   331 	char *str;
   332 	pContext->LocalToString(params[1], &str);
   333 
   334 	return _GetUTF8CharBytes(str);
   335 };
   336 
   337 static cell_t IsCharAlpha(IPluginContext *pContext, const cell_t *params)
   338 {
   339 	char chr = params[1];
   340 
   341 	if (_GetUTF8CharBytes(&chr) != 1)
   342 	{
   343 		return 0;
   344 	}
   345 
   346 	return isalpha(chr);
   347 }
   348 
   349 static cell_t IsCharNumeric(IPluginContext *pContext, const cell_t *params)
   350 {
   351 	char chr = params[1];
   352 
   353 	if (_GetUTF8CharBytes(&chr) != 1)
   354 	{
   355 		return 0;
   356 	}
   357 
   358 	return isdigit(chr);
   359 }
   360 
   361 static cell_t IsCharSpace(IPluginContext *pContext, const cell_t *params)
   362 {
   363 	char chr = params[1];
   364 
   365 	if (_GetUTF8CharBytes(&chr) != 1)
   366 	{
   367 		return 0;
   368 	}
   369 
   370 	return isspace(chr);
   371 }
   372 
   373 static cell_t IsCharMB(IPluginContext *pContext, const cell_t *params)
   374 {
   375 	char chr = params[1];
   376 
   377 	unsigned int bytes = _GetUTF8CharBytes(&chr);
   378 	if (bytes == 1)
   379 	{
   380 		return 0;
   381 	}
   382 
   383 	return bytes;
   384 }
   385 
   386 static cell_t IsCharUpper(IPluginContext *pContext, const cell_t *params)
   387 {
   388 	char chr = params[1];
   389 
   390 	if (_GetUTF8CharBytes(&chr) != 1)
   391 	{
   392 		return 0;
   393 	}
   394 
   395 	return isupper(chr);
   396 }
   397 
   398 static cell_t IsCharLower(IPluginContext *pContext, const cell_t *params)
   399 {
   400 	char chr = params[1];
   401 
   402 	if (_GetUTF8CharBytes(&chr) != 1)
   403 	{
   404 		return 0;
   405 	}
   406 
   407 	return islower(chr);
   408 }
   409 
   410 static cell_t ReplaceString(IPluginContext *pContext, const cell_t *params)
   411 {
   412 	char *text, *search, *replace;
   413 	size_t maxlength;
   414 	
   415 	pContext->LocalToString(params[1], &text);
   416 	pContext->LocalToString(params[3], &search);
   417 	pContext->LocalToString(params[4], &replace);
   418 	maxlength = (size_t)params[2];
   419 
   420 
   421 	return  UTIL_ReplaceAll(text, maxlength, search, replace);
   422 }
   423 
   424 static cell_t ReplaceStringEx(IPluginContext *pContext, const cell_t *params)
   425 {
   426 	char *text, *search, *replace;
   427 	size_t maxlength;
   428 
   429 	pContext->LocalToString(params[1], &text);
   430 	pContext->LocalToString(params[3], &search);
   431 	pContext->LocalToString(params[4], &replace);
   432 	maxlength = (size_t)params[2];
   433 
   434 	size_t searchLen = (params[5] == -1) ? strlen(search) : (size_t)params[5];
   435 	size_t replaceLen = (params[6] == -1) ? strlen(replace) : (size_t)params[6];
   436 
   437 	char *ptr = UTIL_ReplaceEx(text, maxlength, search, searchLen, replace, replaceLen);
   438 
   439 	if (ptr == NULL)
   440 	{
   441 		return -1;
   442 	}
   443 
   444 	return ptr - text;
   445 }
   446 
   447 static cell_t TrimString(IPluginContext *pContext, const cell_t *params)
   448 {
   449 	char *str;
   450 	pContext->LocalToString(params[1], &str);
   451 
   452 	size_t chars = strlen(str);
   453 
   454 	if (chars == 0)
   455 	{
   456 		return 0;
   457 	}
   458 
   459 	char *end = str + chars - 1;
   460 
   461 	/* Iterate backwards through string until we reach first non-whitespace char */
   462 	while (end >= str && IsWhitespace(end))
   463 	{
   464 		end--;
   465 	}
   466 
   467 	/* Replace first whitespace char (at the end) with null terminator */
   468 	*(end + 1) = '\0';
   469 
   470 	/* Iterate forwards through string until first non-whitespace char is reached */
   471 	while (IsWhitespace(str))
   472 	{
   473 		str++;
   474 	}
   475 
   476 	size_t bytes;
   477 	pContext->StringToLocalUTF8(params[1], chars + 1, str, &bytes);
   478 
   479 	return bytes;
   480 }
   481 
   482 static cell_t SplitString(IPluginContext *pContext, const cell_t *params)
   483 {
   484 	char *text, *split;
   485 	
   486 	pContext->LocalToString(params[1], &text);
   487 	pContext->LocalToString(params[2], &split);
   488 
   489 	size_t maxLen = (size_t)params[4];
   490 	size_t textLen = strlen(text);
   491 	size_t splitLen = strlen(split);
   492 
   493 	if (splitLen > textLen)
   494 	{
   495 		return -1;
   496 	}
   497 
   498 	for (size_t i=0; i<textLen - splitLen; i++)
   499 	{
   500 		if (strncmp(&text[i], split, splitLen) == 0)
   501 		{
   502 			/* Split hereeeee */
   503 			if (i >= maxLen)
   504 			{
   505 				pContext->StringToLocalUTF8(params[3], maxLen, text, NULL);
   506 			} else {
   507 				pContext->StringToLocalUTF8(params[3], i+1, text, NULL);
   508 			}
   509 			return (cell_t)(i + splitLen);
   510 		}
   511 	}
   512 
   513 	return -1;
   514 }
   515 
   516 REGISTER_NATIVES(basicStrings)
   517 {
   518 	{"BreakString",			BreakString},
   519 	{"FloatToString",		sm_floattostr},
   520 	{"Format",				sm_format},
   521 	{"FormatEx",			sm_formatex},
   522 	{"GetCharBytes",		GetCharBytes},
   523 	{"IntToString",			sm_numtostr},
   524 	{"IsCharAlpha",			IsCharAlpha},
   525 	{"IsCharLower",			IsCharLower},
   526 	{"IsCharMB",			IsCharMB},
   527 	{"IsCharNumeric",		IsCharNumeric},
   528 	{"IsCharSpace",			IsCharSpace},
   529 	{"IsCharUpper",			IsCharUpper},
   530 	{"ReplaceString",		ReplaceString},
   531 	{"ReplaceStringEx",		ReplaceStringEx},
   532 	{"SplitString",			SplitString},
   533 	{"strlen",				sm_strlen},
   534 	{"StrBreak",			BreakString},		/* Backwards compat shim */
   535 	{"StrContains",			sm_contain},
   536 	{"strcmp",				sm_strcmp},
   537 	{"StrCompare",			sm_strcmp},			/* Backwards compat shim */
   538 	{"strncmp",				sm_strncmp},
   539 	{"strcopy",				sm_strcopy},
   540 	{"StrCopy",				sm_strcopy},		/* Backwards compat shim */
   541 	{"StringToInt",			sm_strconvint},
   542 	{"StringToFloat",		sm_strtofloat},
   543 	{"TrimString",			TrimString},
   544 	{"VFormat",				sm_vformat},
   545 	{NULL,					NULL},
   546 };