core/sm_stringutil.cpp
author David Anderson <dvander@alliedmods.net>
Wed Mar 12 03:33:52 2008 +0000 (2008-03-12)
changeset 1925 61d7a244b641
parent 1738 cb3be0b31d99
child 1947 e73a1e51833b
permissions -rw-r--r--
reworked antiflood. it now has some logic in core to take care of loading order nastiness, and to fully prevent trigger spamming
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 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 <stdlib.h>
    33 #include <ctype.h>
    34 #include <stdarg.h>
    35 #include "sm_stringutil.h"
    36 #include "Logger.h"
    37 #include "PluginSys.h"
    38 #include "Translator.h"
    39 #include "PlayerManager.h"
    40 
    41 #define LADJUST			0x00000004		/* left adjustment */
    42 #define ZEROPAD			0x00000080		/* zero (as opposed to blank) pad */
    43 #define UPPERDIGITS		0x00000200		/* make alpha digits uppercase */
    44 #define to_digit(c)		((c) - '0')
    45 #define is_digit(c)		((unsigned)to_digit(c) <= 9)
    46 
    47 #define CHECK_ARGS(x) \
    48 	if ((arg+x) > args) { \
    49 		pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "String formatted incorrectly - parameter %d (total %d)", arg, args); \
    50 		return 0; \
    51 	}
    52 
    53 size_t CorePlayerTranslate(int client, char *buffer, size_t maxlength, const char *phrase, void **params)
    54 {
    55 	Translation pTrans;
    56 	TransError err;
    57 
    58 	err = g_pCorePhrases->GetTranslation(phrase, g_Translator.GetClientLanguage(client), &pTrans);
    59 	if (err != Trans_Okay)
    60 	{
    61 		err = g_pCorePhrases->GetTranslation(phrase, g_Translator.GetServerLanguage(), &pTrans);
    62 		if (err != Trans_Okay && g_Translator.GetServerLanguage() != CORELANG_ENGLISH)
    63 		{
    64 			err = g_pCorePhrases->GetTranslation(phrase, CORELANG_ENGLISH, &pTrans);
    65 		}
    66 	}
    67 
    68 	if (err != Trans_Okay)
    69 	{
    70 		return UTIL_Format(buffer, maxlength, "%s", phrase);
    71 	}
    72 
    73 	return g_Translator.Translate(buffer, maxlength, params, &pTrans);
    74 }
    75 
    76 inline bool TryTranslation(CPlugin *pl, const char *key, unsigned int langid, unsigned int langcount, Translation *pTrans)
    77 {
    78 	TransError err = Trans_BadLanguage;
    79 	CPhraseFile *phrfl;
    80 
    81 	for (size_t i=0; i<langcount && err!=Trans_Okay; i++)
    82 	{
    83 		phrfl = g_Translator.GetFileByIndex(pl->GetLangFileByIndex(i));
    84 		err = phrfl->GetTranslation(key, langid, pTrans);
    85 	}
    86 
    87 	return (err == Trans_Okay) ? true : false;
    88 }
    89 
    90 inline void ReorderTranslationParams(const Translation *pTrans, cell_t *params)
    91 {
    92 	cell_t new_params[MAX_TRANSLATE_PARAMS];
    93 	for (unsigned int i = 0; i < pTrans->fmt_count; i++)
    94 	{
    95 		new_params[i] = params[pTrans->fmt_order[i]];
    96 	}
    97 	memcpy(params, new_params, pTrans->fmt_count * sizeof(cell_t));
    98 }
    99 
   100 size_t Translate(char *buffer,
   101 				 size_t maxlen,
   102 				 IPluginContext *pCtx,
   103 				 const char *key,
   104 				 cell_t target,
   105 				 const cell_t *params,
   106 				 int *arg,
   107 				 bool *error)
   108 {
   109 	unsigned int langid;
   110 	*error = false;
   111 	Translation pTrans;
   112 	CPlugin *pl = (CPlugin *)g_PluginSys.FindPluginByContext(pCtx->GetContext());
   113 	size_t langcount = pl->GetLangFileCount();
   114 	unsigned int max_params = 0;
   115 
   116 try_serverlang:
   117 	if (target == LANG_SERVER)
   118 	{
   119 		langid = g_Translator.GetServerLanguage();
   120  	}
   121 	else if ((target >= 1) && (target <= g_Players.GetMaxClients()))
   122 	{
   123 		langid = g_Translator.GetClientLanguage(target);
   124 	}
   125 	else
   126 	{
   127 		pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Translation failed: invalid client index %d", target);
   128 		goto error_out;
   129 	}
   130 
   131 	if (!TryTranslation(pl, key, langid, langcount, &pTrans))
   132 	{
   133 		if (target != LANG_SERVER && langid != g_Translator.GetServerLanguage())
   134 		{
   135 			target = LANG_SERVER;
   136 			goto try_serverlang;
   137 		}
   138 		else if (langid != LANGUAGE_ENGLISH)
   139 		{
   140 			if (!TryTranslation(pl, key, LANGUAGE_ENGLISH, langcount, &pTrans))
   141 			{
   142 				pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
   143 				goto error_out;
   144 			}
   145 		}
   146 		else
   147 		{
   148 			pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
   149 			goto error_out;
   150 		}
   151 	}
   152 
   153 	max_params = pTrans.fmt_count;
   154 
   155 	if (max_params)
   156 	{
   157 		cell_t new_params[MAX_TRANSLATE_PARAMS];
   158 
   159 		/* Check if we're going to over the limit */
   160 		if ((*arg) + (max_params - 1) > (size_t)params[0])
   161 		{
   162 			pCtx->ThrowNativeErrorEx(SP_ERROR_PARAMS_MAX, 
   163 				"Translation string formatted incorrectly - missing at least %d parameters", 
   164 				((*arg + (max_params - 1)) - params[0])
   165 				);
   166 			goto error_out;
   167 		}
   168 
   169 		/* If we need to re-order the parameters, do so with a temporary array.
   170 		 * Otherwise, we could run into trouble with continual formats, a la ShowActivity().
   171 		 */
   172 		memcpy(new_params, params, sizeof(cell_t) * (params[0] + 1));
   173 		ReorderTranslationParams(&pTrans, &new_params[*arg]);
   174 
   175 		return atcprintf(buffer, maxlen, pTrans.szPhrase, pCtx, new_params, arg);
   176 	}
   177 	else
   178 	{
   179 		return atcprintf(buffer, maxlen, pTrans.szPhrase, pCtx, params, arg);
   180 	}
   181 	
   182 error_out:
   183 	*error = true;
   184 	return 0;
   185 }
   186 
   187 void AddString(char **buf_p, size_t &maxlen, const char *string, int width, int prec)
   188 {
   189 	int size = 0;
   190 	char *buf;
   191 	static char nlstr[] = {'(','n','u','l','l',')','\0'};
   192 
   193 	buf = *buf_p;
   194 
   195 	if (string == NULL)
   196 	{
   197 		string = nlstr;
   198 		prec = -1;
   199 	}
   200 
   201 	if (prec >= 0)
   202 	{
   203 		for (size = 0; size < prec; size++) 
   204 		{
   205 			if (string[size] == '\0')
   206 			{
   207 				break;
   208 			}
   209 		}
   210 	}
   211 	else
   212 	{
   213 		while (string[size++]);
   214 		size--;
   215 	}
   216 
   217 	if (size > (int)maxlen)
   218 	{
   219 		size = maxlen;
   220 	}
   221 
   222 	maxlen -= size;
   223 	width -= size;
   224 
   225 	while (size--)
   226 	{
   227 		*buf++ = *string++;
   228 	}
   229 
   230 	while ((width-- > 0) && maxlen)
   231 	{
   232 		*buf++ = ' ';
   233 		maxlen--;
   234 	}
   235 
   236 	*buf_p = buf;
   237 }
   238 
   239 void AddFloat(char **buf_p, size_t &maxlen, double fval, int width, int prec, int flags)
   240 {
   241 	int digits;					// non-fraction part digits
   242 	double tmp;					// temporary
   243 	char *buf = *buf_p;			// output buffer pointer
   244 	int val;					// temporary
   245 	int sign = 0;				// 0: positive, 1: negative
   246 	int fieldlength;			// for padding
   247 	int significant_digits = 0;	// number of significant digits written
   248 	const int MAX_SIGNIFICANT_DIGITS = 16;
   249 
   250 	// default precision
   251 	if (prec < 0)
   252 	{
   253 		prec = 6;
   254 	}
   255 
   256 	// get the sign
   257 	if (fval < 0)
   258 	{
   259 		fval = -fval;
   260 		sign = 1;
   261 	}
   262 
   263 	// compute whole-part digits count
   264 	digits = (int)log10(fval) + 1;
   265 
   266 	// Only print 0.something if 0 < fval < 1
   267 	if (digits < 1)
   268 	{
   269 		digits = 1;
   270 	}
   271 
   272 	// compute the field length
   273 	fieldlength = digits + prec + ((prec > 0) ? 1 : 0) + sign;
   274 
   275 	// minus sign BEFORE left padding if padding with zeros
   276 	if (sign && maxlen && (flags & ZEROPAD))
   277 	{
   278 		*buf++ = '-';
   279 		maxlen--;
   280 	}
   281 
   282 	// right justify if required
   283 	if ((flags & LADJUST) == 0)
   284 	{
   285 		while ((fieldlength < width) && maxlen)
   286 		{
   287 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   288 			width--;
   289 			maxlen--;
   290 		}
   291 	}
   292 
   293 	// minus sign AFTER left padding if padding with spaces
   294 	if (sign && maxlen && !(flags & ZEROPAD))
   295 	{
   296 		*buf++ = '-';
   297 		maxlen--;
   298 	}
   299 
   300 	// write the whole part
   301 	tmp = pow(10.0, digits-1);
   302 	while ((digits--) && maxlen)
   303 	{
   304 		if (++significant_digits > MAX_SIGNIFICANT_DIGITS)
   305 		{
   306 			*buf++ = '0';
   307 		}
   308 		else
   309 		{
   310 			val = (int)(fval / tmp);
   311 			*buf++ = '0' + val;
   312 			fval -= val * tmp;
   313 			tmp *= 0.1;
   314 		}
   315 		maxlen--;
   316 	}
   317 
   318 	// write the fraction part
   319 	if (maxlen)
   320 	{
   321 		*buf++ = '.';
   322 		maxlen--;
   323 	}
   324 
   325 	tmp = pow(10.0, prec);
   326 
   327 	fval *= tmp;
   328 	while (prec-- && maxlen)
   329 	{
   330 		if (++significant_digits > MAX_SIGNIFICANT_DIGITS)
   331 		{
   332 			*buf++ = '0';
   333 		}
   334 		else
   335 		{
   336 			tmp *= 0.1;
   337 			val = (int)(fval / tmp);
   338 			*buf++ = '0' + val;
   339 			fval -= val * tmp;
   340 		}
   341 		maxlen--;
   342 	}
   343 
   344 	// left justify if required
   345 	if (flags & LADJUST)
   346 	{
   347 		while ((fieldlength < width) && maxlen)
   348 		{
   349 			// right-padding only with spaces, ZEROPAD is ignored
   350 			*buf++ = ' ';
   351 			width--;
   352 			maxlen--;
   353 		}
   354 	}
   355 
   356 	// update parent's buffer pointer
   357 	*buf_p = buf;
   358 }
   359 
   360 void AddUInt(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
   361 {
   362 	char text[32];
   363 	int digits;
   364 	char *buf;
   365 
   366 	digits = 0;
   367 	do
   368 	{
   369 		text[digits++] = '0' + val % 10;
   370 		val /= 10;
   371 	} while (val);
   372 
   373 	buf = *buf_p;
   374 
   375 	if (!(flags & LADJUST))
   376 	{
   377 		while (digits < width && maxlen)
   378 		{
   379 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   380 			width--;
   381 			maxlen--;
   382 		}
   383 	}
   384 
   385 	while (digits-- && maxlen)
   386 	{
   387 		*buf++ = text[digits];
   388 		width--;
   389 		maxlen--;
   390 	}
   391 
   392 	if (flags & LADJUST)
   393 	{
   394 		while (width-- && maxlen)
   395 		{
   396 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   397 			maxlen--;
   398 		}
   399 	}
   400 
   401 	*buf_p = buf;
   402 }
   403 
   404 void AddInt(char **buf_p, size_t &maxlen, int val, int width, int flags)
   405 {
   406 	char text[32];
   407 	int digits;
   408 	int signedVal;
   409 	char *buf;
   410 	unsigned int unsignedVal;
   411 
   412 	digits = 0;
   413 	signedVal = val;
   414 	if (val < 0)
   415 	{
   416 		/* we want the unsigned version */
   417 		unsignedVal = abs(val);
   418 	}
   419 	else
   420 	{
   421 		unsignedVal = val;
   422 	}
   423 
   424 	do
   425 	{
   426 		text[digits++] = '0' + unsignedVal % 10;
   427 		unsignedVal /= 10;
   428 	} while (unsignedVal);
   429 
   430 	if (signedVal < 0)
   431 	{
   432 		text[digits++] = '-';
   433 	}
   434 
   435 	buf = *buf_p;
   436 
   437 	if (!(flags & LADJUST))
   438 	{
   439 		while ((digits < width) && maxlen)
   440 		{
   441 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   442 			width--;
   443 			maxlen--;
   444 		}
   445 	}
   446 
   447 	while (digits-- && maxlen)
   448 	{
   449 		*buf++ = text[digits];
   450 		width--;
   451 		maxlen--;
   452 	}
   453 
   454 	if (flags & LADJUST)
   455 	{
   456 		while (width-- && maxlen)
   457 		{
   458 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   459 			maxlen--;
   460 		}
   461 	}
   462 
   463 	*buf_p = buf;
   464 }
   465 
   466 void AddHex(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
   467 {
   468 	char text[32];
   469 	int digits;
   470 	char *buf;
   471 	char digit;
   472 	int hexadjust;
   473 
   474 	if (flags & UPPERDIGITS)
   475 	{
   476 		hexadjust = 'A' - '9' - 1;
   477 	}
   478 	else
   479 	{
   480 		hexadjust = 'a' - '9' - 1;
   481 	}
   482 
   483 	digits = 0;
   484 	do 
   485 	{
   486 		digit = ('0' + val % 16);
   487 		if (digit > '9')
   488 		{
   489 			digit += hexadjust;
   490 		}
   491 
   492 		text[digits++] = digit;
   493 		val /= 16;
   494 	} while(val);
   495 
   496 	buf = *buf_p;
   497 
   498 	if (!(flags & LADJUST))
   499 	{
   500 		while (digits < width && maxlen)
   501 		{
   502 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   503 			width--;
   504 			maxlen--;
   505 		}
   506 	}
   507 
   508 	while (digits-- && maxlen)
   509 	{
   510 		*buf++ = text[digits];
   511 		width--;
   512 		maxlen--;
   513 	}
   514 
   515 	if (flags & LADJUST)
   516 	{
   517 		while (width-- && maxlen)
   518 		{
   519 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   520 			maxlen--;
   521 		}
   522 	}
   523 
   524 	*buf_p = buf;
   525 }
   526 
   527 size_t gnprintf(char *buffer, size_t maxlen, const char *format, void **args)
   528 {
   529 	if (!buffer || !maxlen)
   530 	{
   531 		return 0;
   532 	}
   533 
   534 	int arg = 0;
   535 	char *buf_p;
   536 	char ch;
   537 	int flags;
   538 	int width;
   539 	int prec;
   540 	int n;
   541 	char sign;
   542 	const char *fmt;
   543 	size_t llen = maxlen - 1;
   544 
   545 	buf_p = buffer;
   546 	fmt = format;
   547 
   548 	while (true)
   549 	{
   550 		// run through the format string until we hit a '%' or '\0'
   551 		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
   552 		{
   553 			*buf_p++ = ch;
   554 			llen--;
   555 		}
   556 		if ((ch == '\0') || (llen <= 0))
   557 		{
   558 			goto done;
   559 		}
   560 
   561 		// skip over the '%'
   562 		fmt++;
   563 
   564 		// reset formatting state
   565 		flags = 0;
   566 		width = 0;
   567 		prec = -1;
   568 		sign = '\0';
   569 
   570 rflag:
   571 		ch = *fmt++;
   572 reswitch:
   573 		switch(ch)
   574 		{
   575 		case '-':
   576 			{
   577 				flags |= LADJUST;
   578 				goto rflag;
   579 			}
   580 		case '.':
   581 			{
   582 				n = 0;
   583 				while(is_digit((ch = *fmt++)))
   584 				{
   585 					n = 10 * n + (ch - '0');
   586 				}
   587 				prec = (n < 0) ? -1 : n;
   588 				goto reswitch;
   589 			}
   590 		case '0':
   591 			{
   592 				flags |= ZEROPAD;
   593 				goto rflag;
   594 			}
   595 		case '1':
   596 		case '2':
   597 		case '3':
   598 		case '4':
   599 		case '5':
   600 		case '6':
   601 		case '7':
   602 		case '8':
   603 		case '9':
   604 			{
   605 				n = 0;
   606 				do
   607 				{
   608 					n = 10 * n + (ch - '0');
   609 					ch = *fmt++;
   610 				} while(is_digit(ch));
   611 				width = n;
   612 				goto reswitch;
   613 			}
   614 		case 'c':
   615 			{
   616 				if (!llen)
   617 				{
   618 					goto done;
   619 				}
   620 				char *c = (char *)args[arg];
   621 				*buf_p++ = *c;
   622 				llen--;
   623 				arg++;
   624 				break;
   625 			}
   626 		case 'd':
   627 		case 'i':
   628 			{
   629 				int *value = (int *)args[arg];
   630 				AddInt(&buf_p, llen, *value, width, flags);
   631 				arg++;
   632 				break;
   633 			}
   634 		case 'u':
   635 			{
   636 				unsigned int *value = (unsigned int *)args[arg];
   637 				AddUInt(&buf_p, llen, *value, width, flags);
   638 				arg++;
   639 				break;
   640 			}
   641 		case 'f':
   642 			{
   643 				float *value = (float *)args[arg];
   644 				AddFloat(&buf_p, llen, *value, width, prec, flags);
   645 				arg++;
   646 				break;
   647 			}
   648 		case 's':
   649 			{
   650 				const char *str = (const char *)args[arg];
   651 				AddString(&buf_p, llen, str, width, prec);
   652 				arg++;
   653 				break;
   654 			}
   655 		case 'X':
   656 			{
   657 				unsigned int *value = (unsigned int *)args[arg];
   658 				flags |= UPPERDIGITS;
   659 				AddHex(&buf_p, llen, *value, width, flags);
   660 				arg++;
   661 				break;
   662 			}
   663 		case 'x':
   664 			{
   665 				unsigned int *value = (unsigned int *)args[arg];
   666 				AddHex(&buf_p, llen, *value, width, flags);
   667 				arg++;
   668 				break;
   669 			}
   670 		case '%':
   671 			{
   672 				if (!llen)
   673 				{
   674 					goto done;
   675 				}
   676 				*buf_p++ = ch;
   677 				llen--;
   678 				break;
   679 			}
   680 		case '\0':
   681 			{
   682 				if (!llen)
   683 				{
   684 					goto done;
   685 				}
   686 				*buf_p++ = '%';
   687 				llen--;
   688 				goto done;
   689 			}
   690 		default:
   691 			{
   692 				if (!llen)
   693 				{
   694 					goto done;
   695 				}
   696 				*buf_p++ = ch;
   697 				llen--;
   698 				break;
   699 			}
   700 		}
   701 	}
   702 
   703 done:
   704 	*buf_p = '\0';
   705 
   706 	return (maxlen - llen - 1);
   707 }
   708 
   709 size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param)
   710 {
   711 	if (!buffer || !maxlen)
   712 	{
   713 		return 0;
   714 	}
   715 
   716 	int arg;
   717 	int args = params[0];
   718 	char *buf_p;
   719 	char ch;
   720 	int flags;
   721 	int width;
   722 	int prec;
   723 	int n;
   724 	char sign;
   725 	const char *fmt;
   726 	size_t llen = maxlen - 1;
   727 
   728 	buf_p = buffer;
   729 	arg = *param;
   730 	fmt = format;
   731 
   732 	while (true)
   733 	{
   734 		// run through the format string until we hit a '%' or '\0'
   735 		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
   736 		{
   737 			*buf_p++ = ch;
   738 			llen--;
   739 		}
   740 		if ((ch == '\0') || (llen <= 0))
   741 		{
   742 			goto done;
   743 		}
   744 
   745 		// skip over the '%'
   746 		fmt++;
   747 
   748 		// reset formatting state
   749 		flags = 0;
   750 		width = 0;
   751 		prec = -1;
   752 		sign = '\0';
   753 
   754 rflag:
   755 		ch = *fmt++;
   756 reswitch:
   757 		switch(ch)
   758 		{
   759 		case '-':
   760 			{
   761 				flags |= LADJUST;
   762 				goto rflag;
   763 			}
   764 		case '.':
   765 			{
   766 				n = 0;
   767 				while(is_digit((ch = *fmt++)))
   768 				{
   769 					n = 10 * n + (ch - '0');
   770 				}
   771 				prec = (n < 0) ? -1 : n;
   772 				goto reswitch;
   773 			}
   774 		case '0':
   775 			{
   776 				flags |= ZEROPAD;
   777 				goto rflag;
   778 			}
   779 		case '1':
   780 		case '2':
   781 		case '3':
   782 		case '4':
   783 		case '5':
   784 		case '6':
   785 		case '7':
   786 		case '8':
   787 		case '9':
   788 			{
   789 				n = 0;
   790 				do
   791 				{
   792 					n = 10 * n + (ch - '0');
   793 					ch = *fmt++;
   794 				} while(is_digit(ch));
   795 				width = n;
   796 				goto reswitch;
   797 			}
   798 		case 'c':
   799 			{
   800 				CHECK_ARGS(0);
   801 				if (!llen)
   802 				{
   803 					goto done;
   804 				}
   805 				char *c;
   806 				pCtx->LocalToString(params[arg], &c);
   807 				*buf_p++ = *c;
   808 				llen--;
   809 				arg++;
   810 				break;
   811 			}
   812 		case 'd':
   813 		case 'i':
   814 			{
   815 				CHECK_ARGS(0);
   816 				cell_t *value;
   817 				pCtx->LocalToPhysAddr(params[arg], &value);
   818 				AddInt(&buf_p, llen, static_cast<int>(*value), width, flags);
   819 				arg++;
   820 				break;
   821 			}
   822 		case 'u':
   823 			{
   824 				CHECK_ARGS(0);
   825 				cell_t *value;
   826 				pCtx->LocalToPhysAddr(params[arg], &value);
   827 				AddUInt(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
   828 				arg++;
   829 				break;
   830 			}
   831 		case 'f':
   832 			{
   833 				CHECK_ARGS(0);
   834 				cell_t *value;
   835 				pCtx->LocalToPhysAddr(params[arg], &value);
   836 				AddFloat(&buf_p, llen, sp_ctof(*value), width, prec, flags);
   837 				arg++;
   838 				break;
   839 			}
   840 		case 'L':
   841 			{
   842 				CHECK_ARGS(0);
   843 				cell_t *value;
   844 				pCtx->LocalToPhysAddr(params[arg], &value);
   845 				char buffer[255];
   846 				if (*value)
   847 				{
   848 					CPlayer *player = g_Players.GetPlayerByIndex(*value);
   849 					if (!player || !player->IsConnected())
   850 					{
   851 						return pCtx->ThrowNativeError("Client index %d is invalid", *value);
   852 					}
   853 					const char *auth = player->GetAuthString();
   854 					if (!auth || auth[0] == '\0')
   855 					{
   856 						auth = "STEAM_ID_PENDING";
   857 					}
   858 					int userid = engine->GetPlayerUserId(player->GetEdict());
   859 					UTIL_Format(buffer, 
   860 						sizeof(buffer), 
   861 						"%s<%d><%s><>", 
   862 						player->GetName(),
   863 						userid,
   864 						auth);
   865 				}
   866 				else
   867 				{
   868 					UTIL_Format(buffer,
   869 						sizeof(buffer),
   870 						"Console<0><Console><Console>");
   871 				}
   872 				AddString(&buf_p, llen, buffer, width, prec);
   873 				arg++;
   874 				break;
   875 			}
   876 		case 'N':
   877 			{
   878 				CHECK_ARGS(0);
   879 				cell_t *value;
   880 				pCtx->LocalToPhysAddr(params[arg], &value);
   881 
   882 				const char *name = "Console";
   883 				if (*value)
   884 				{
   885 					CPlayer *player = g_Players.GetPlayerByIndex(*value);
   886 					if (!player || !player->IsConnected())
   887 					{
   888 						return pCtx->ThrowNativeError("Client index %d is invalid", *value);
   889 					}
   890 					name = player->GetName();
   891 				}
   892 				AddString(&buf_p, llen, name, width, prec);
   893 				arg++;
   894 				break;
   895 			}
   896 		case 's':
   897 			{
   898 				CHECK_ARGS(0);
   899 				char *str;
   900 				int err;
   901 				if ((err=pCtx->LocalToString(params[arg], &str)) != SP_ERROR_NONE)
   902 				{
   903 					pCtx->ThrowNativeErrorEx(err, "Could not deference string");
   904 					return 0;
   905 				}
   906 				AddString(&buf_p, llen, str, width, prec);
   907 				arg++;
   908 				break;
   909 			}
   910 		case 'T':
   911 			{
   912 				CHECK_ARGS(1);
   913 				char *key;
   914 				bool error;
   915 				size_t res;
   916 				cell_t *target;
   917 				pCtx->LocalToString(params[arg++], &key);
   918 				pCtx->LocalToPhysAddr(params[arg++], &target);
   919 				res = Translate(buf_p, llen, pCtx, key, *target, params, &arg, &error);
   920 				if (error)
   921 				{
   922 					return 0;
   923 				}
   924 				buf_p += res;
   925 				llen -= res;
   926 				break;
   927 			}
   928 		case 't':
   929 			{
   930 				CHECK_ARGS(0);
   931 				char *key;
   932 				bool error;
   933 				size_t res;
   934 				cell_t target = g_SourceMod.GetGlobalTarget();
   935 				pCtx->LocalToString(params[arg++], &key);
   936 				res = Translate(buf_p, llen, pCtx, key, target, params, &arg, &error);
   937 				if (error)
   938 				{
   939 					return 0;
   940 				}
   941 				buf_p += res;
   942 				llen -= res;
   943 				break;
   944 			}
   945 		case 'X':
   946 			{
   947 				CHECK_ARGS(0);
   948 				cell_t *value;
   949 				pCtx->LocalToPhysAddr(params[arg], &value);
   950 				flags |= UPPERDIGITS;
   951 				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
   952 				arg++;
   953 				break;
   954 			}
   955 		case 'x':
   956 			{
   957 				CHECK_ARGS(0);
   958 				cell_t *value;
   959 				pCtx->LocalToPhysAddr(params[arg], &value);
   960 				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
   961 				arg++;
   962 				break;
   963 			}
   964 		case '%':
   965 			{
   966 				if (!llen)
   967 				{
   968 					goto done;
   969 				}
   970 				*buf_p++ = ch;
   971 				llen--;
   972 				break;
   973 			}
   974 		case '\0':
   975 			{
   976 				if (!llen)
   977 				{
   978 					goto done;
   979 				}
   980 				*buf_p++ = '%';
   981 				llen--;
   982 				goto done;
   983 			}
   984 		default:
   985 			{
   986 				if (!llen)
   987 				{
   988 					goto done;
   989 				}
   990 				*buf_p++ = ch;
   991 				llen--;
   992 				break;
   993 			}
   994 		}
   995 	}
   996 
   997 done:
   998 	*buf_p = '\0';
   999 	*param = arg;
  1000 	return (maxlen - llen - 1);
  1001 }
  1002 
  1003 const char *stristr(const char *str, const char *substr)
  1004 {
  1005 	if (!*substr)
  1006 	{
  1007 		return ((char *)str);
  1008 	}
  1009 
  1010 	char *needle = (char *)substr;
  1011 	char *prevloc = (char *)str;
  1012 	char *haystack = (char *)str;
  1013 
  1014 	while (*haystack)
  1015 	{
  1016 		if (tolower(*haystack) == tolower(*needle))
  1017 		{
  1018 			haystack++;
  1019 			if (!*++needle)
  1020 			{
  1021 				return prevloc;
  1022 			}
  1023 		}
  1024 		else
  1025 		{
  1026 			haystack = ++prevloc;
  1027 			needle = (char *)substr;
  1028 		}
  1029 	}
  1030 
  1031 	return NULL;
  1032 }
  1033 
  1034 unsigned int strncopy(char *dest, const char *src, size_t count)
  1035 {
  1036 	if (!count)
  1037 	{
  1038 		return 0;
  1039 	}
  1040 
  1041 	char *start = dest;
  1042 	while ((*src) && (--count))
  1043 	{
  1044 		*dest++ = *src++;
  1045 	}
  1046 	*dest = '\0';
  1047 
  1048 	return (dest - start);
  1049 }
  1050 
  1051 size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
  1052 {
  1053 	va_list ap;
  1054 	va_start(ap, fmt);
  1055 	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
  1056 	va_end(ap);
  1057 
  1058 	if (len >= maxlength)
  1059 	{
  1060 		buffer[maxlength - 1] = '\0';
  1061 		return (maxlength - 1);
  1062 	}
  1063 	else
  1064 	{
  1065 		return len;
  1066 	}
  1067 }
  1068 
  1069 size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap)
  1070 {
  1071 	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
  1072 
  1073 	if (len >= maxlength)
  1074 	{
  1075 		buffer[maxlength - 1] = '\0';
  1076 		return (maxlength - 1);
  1077 	}
  1078 	else
  1079 	{
  1080 		return len;
  1081 	}
  1082 }
  1083 
  1084 char *sm_strdup(const char *str)
  1085 {
  1086 	char *ptr = new char[strlen(str)+1];
  1087 	strcpy(ptr, str);
  1088 	return ptr;
  1089 }
  1090 
  1091 unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace)
  1092 {
  1093 	size_t searchLen = strlen(search);
  1094 	size_t replaceLen = strlen(replace);
  1095 
  1096 	char *ptr = subject;
  1097 	unsigned int total = 0;
  1098 	while ((ptr = UTIL_ReplaceEx(ptr, maxlength, search, searchLen, replace, replaceLen)) != NULL)
  1099 	{
  1100 		total++;
  1101 		if (*ptr == '\0')
  1102 		{
  1103 			break;
  1104 		}
  1105 	}
  1106 
  1107 	return total;
  1108 }
  1109 
  1110 /**
  1111  * NOTE: Do not edit this for the love of god unless you have 
  1112  * read the test cases and understand the code behind each one.
  1113  * While I don't guarantee there aren't mistakes, I do guarantee
  1114  * that plugins will end up relying on tiny idiosyncracies of this
  1115  * function, just like they did with AMX Mod X.
  1116  *
  1117  * There are explicitly more cases than the AMX Mod X version because
  1118  * we're not doing a blind copy.  Each case is specifically optimized
  1119  * for what needs to be done.  Even better, we don't have to error on
  1120  * bad buffer sizes.  Instead, this function will smartly cut off the 
  1121  * string in a way that pushes old data out.
  1122  */
  1123 char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen)
  1124 {
  1125 	char *ptr = subject;
  1126 	size_t browsed = 0;
  1127 	size_t textLen = strlen(subject);
  1128 
  1129 	/* It's not possible to search or replace */
  1130 	if (searchLen > textLen)
  1131 	{
  1132 		return NULL;
  1133 	}
  1134 
  1135 	/* Handle the case of one byte replacement.
  1136 	 * It's only valid in one case.
  1137 	 */
  1138 	if (maxLen == 1)
  1139 	{
  1140 		/* If the search matches and the replace length is 0, 
  1141 		 * we can just terminate the string and be done.
  1142 		 */
  1143 		if (strcmp(subject, search) == 0 && replaceLen == 0)
  1144 		{
  1145 			*subject = '\0';
  1146 			return subject;
  1147 		}
  1148 		else
  1149 		{
  1150 			return NULL;
  1151 		}
  1152 	}
  1153 
  1154 	/* Subtract one off the maxlength so we can include the null terminator */
  1155 	maxLen--;
  1156 
  1157 	while (*ptr != '\0' && (browsed <= textLen - searchLen))
  1158 	{
  1159 		/* See if we get a comparison */
  1160 		if (strncmp(ptr, search, searchLen) == 0)
  1161 		{
  1162 			if (replaceLen > searchLen)
  1163 			{
  1164 				/* First, see if we have enough space to do this operation */
  1165 				if (maxLen - textLen < replaceLen - searchLen)
  1166 				{
  1167 					/* First, see if the replacement length goes out of bounds. */
  1168 					if (browsed + replaceLen >= maxLen)
  1169 					{
  1170 						/* EXAMPLE CASE:
  1171 						 * Subject: AABBBCCC
  1172 						 * Buffer : 12 bytes
  1173 						 * Search : BBB
  1174 						 * Replace: DDDDDDDDDD
  1175 						 * OUTPUT : AADDDDDDDDD
  1176 						 * POSITION:           ^
  1177 						 */
  1178 						/* If it does, we'll just bound the length and do a strcpy. */
  1179 						replaceLen = maxLen - browsed;
  1180 						/* Note, we add one to the final result for the null terminator */
  1181 						strncopy(ptr, replace, replaceLen+1);
  1182 					}
  1183 					else
  1184 					{
  1185 						/* EXAMPLE CASE:
  1186 						 * Subject: AABBBCCC
  1187 						 * Buffer : 12 bytes
  1188 						 * Search : BBB
  1189 						 * Replace: DDDDDDD
  1190 						 * OUTPUT : AADDDDDDDCC
  1191 						 * POSITION:         ^
  1192 						 */
  1193 						/* We're going to have some bytes left over... */
  1194 						size_t origBytesToCopy = (textLen - (browsed + searchLen)) + 1;
  1195 						size_t realBytesToCopy = (maxLen - (browsed + replaceLen)) + 1;
  1196 						char *moveFrom = ptr + searchLen + (origBytesToCopy - realBytesToCopy);
  1197 						char *moveTo = ptr + replaceLen;
  1198 
  1199 						/* First, move our old data out of the way. */
  1200 						memmove(moveTo, moveFrom, realBytesToCopy);
  1201 
  1202 						/* Now, do our replacement. */
  1203 						memcpy(ptr, replace, replaceLen);
  1204 					}
  1205 				}
  1206 				else
  1207 				{
  1208 					/* EXAMPLE CASE:
  1209 					 * Subject: AABBBCCC
  1210 					 * Buffer : 12 bytes
  1211 					 * Search : BBB
  1212 					 * Replace: DDDD
  1213 					 * OUTPUT : AADDDDCCC
  1214 					 * POSITION:      ^
  1215 					 */
  1216 					/* Yes, we have enough space.  Do a normal move operation. */
  1217 					char *moveFrom = ptr + searchLen;
  1218 					char *moveTo = ptr + replaceLen;
  1219 
  1220 					/* First move our old data out of the way. */
  1221 					size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
  1222 					memmove(moveTo, moveFrom, bytesToCopy);
  1223 
  1224 					/* Now do our replacement. */
  1225 					memcpy(ptr, replace, replaceLen);
  1226 				}
  1227 			}
  1228 			else if (replaceLen < searchLen)
  1229 			{
  1230 				/* EXAMPLE CASE:
  1231 				 * Subject: AABBBCCC
  1232 				 * Buffer : 12 bytes
  1233 				 * Search : BBB
  1234 				 * Replace: D
  1235 				 * OUTPUT : AADCCC
  1236 				 * POSOTION:   ^
  1237 				 */
  1238 				/* If the replacement does not grow the string length, we do not
  1239 				 * need to do any fancy checking at all.  Yay!
  1240 				 */
  1241 				char *moveFrom = ptr + searchLen;		/* Start after the search pointer */
  1242 				char *moveTo = ptr + replaceLen;		/* Copy to where the replacement ends */
  1243 
  1244 				/* Copy our replacement in, if any */
  1245 				if (replaceLen)
  1246 				{
  1247 					memcpy(ptr, replace, replaceLen);
  1248 				}
  1249 
  1250 				/* Figure out how many bytes to move down, including null terminator */
  1251 				size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
  1252 
  1253 				/* Move the rest of the string down */
  1254 				memmove(moveTo, moveFrom, bytesToCopy);
  1255 			}
  1256 			else
  1257 			{
  1258 				/* EXAMPLE CASE:
  1259 				 * Subject: AABBBCCC
  1260 				 * Buffer : 12 bytes
  1261 				 * Search : BBB
  1262 				 * Replace: DDD
  1263 				 * OUTPUT : AADDDCCC
  1264 				 * POSITION:     ^
  1265 				 */
  1266 				/* We don't have to move anything around, just do a straight copy */
  1267 				memcpy(ptr, replace, replaceLen);
  1268 			}
  1269 
  1270 			return ptr + replaceLen;
  1271 		}
  1272 		ptr++;
  1273 		browsed++;
  1274 	}
  1275 
  1276 	return NULL;
  1277 }
  1278 
  1279 char *UTIL_TrimWhitespace(char *str, size_t &len)
  1280 {
  1281 	char *end = str + len - 1;
  1282 
  1283 	if (!len)
  1284 	{
  1285 		return str;
  1286 	}
  1287 
  1288 	/* Iterate backwards through string until we reach first non-whitespace char */
  1289 	while (end >= str && textparsers->IsWhitespace(end))
  1290 	{
  1291 		end--;
  1292 		len--;
  1293 	}
  1294 
  1295 	/* Replace first whitespace char (at the end) with null terminator.
  1296 	 * If there is none, we're just replacing the null terminator. 
  1297 	 */
  1298 	*(end + 1) = '\0';
  1299 
  1300 	while (*str != '\0' && textparsers->IsWhitespace(str))
  1301 	{
  1302 		str++;
  1303 		len--;
  1304 	}
  1305 
  1306 	return str;
  1307 }
  1308