core/sm_stringutil.cpp
author David Anderson <dvander@alliedmods.net>
Fri Jun 01 02:30:29 2007 +0000 (2007-06-01)
changeset 868 bdbf922e246f
parent 835 4c9cf82967bd
child 888 b3c080b0e9bb
permissions -rw-r--r--
-initial import of database natives + config
-initial import of completed database layer
-CreateIdentity now requires a non-optional second pointer. This breaks backwards compatibility for CreateIdentity(), however, this is not a function extension authors are supposed to be calling
     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 <stdlib.h>
    16 #include <ctype.h>
    17 #include <stdarg.h>
    18 #include "sm_stringutil.h"
    19 #include "Logger.h"
    20 #include "PluginSys.h"
    21 #include "Translator.h"
    22 #include "PlayerManager.h"
    23 
    24 #define LADJUST			0x00000004		/* left adjustment */
    25 #define ZEROPAD			0x00000080		/* zero (as opposed to blank) pad */
    26 #define UPPERDIGITS		0x00000200		/* make alpha digits uppercase */
    27 #define to_digit(c)		((c) - '0')
    28 #define is_digit(c)		((unsigned)to_digit(c) <= 9)
    29 
    30 #define CHECK_ARGS(x) \
    31 	if ((arg+x) > args) { \
    32 		pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "String formatted incorrectly - parameter %d (total %d)", arg, args); \
    33 		return 0; \
    34 	}
    35 
    36 size_t CorePlayerTranslate(int client, char *buffer, size_t maxlength, const char *phrase, void **params)
    37 {
    38 	Translation pTrans;
    39 	TransError err;
    40 
    41 	err = g_pCorePhrases->GetTranslation(phrase, g_Translator.GetServerLanguage(), &pTrans);
    42 	if (err != Trans_Okay && g_Translator.GetServerLanguage() != CORELANG_ENGLISH)
    43 	{
    44 		err = g_pCorePhrases->GetTranslation(phrase, CORELANG_ENGLISH, &pTrans);
    45 	}
    46 
    47 	if (err != Trans_Okay)
    48 	{
    49 		return UTIL_Format(buffer, maxlength, "%s", phrase);
    50 	}
    51 
    52 	return g_Translator.Translate(buffer, maxlength, params, &pTrans);
    53 }
    54 
    55 inline bool TryTranslation(CPlugin *pl, const char *key, unsigned int langid, unsigned int langcount, Translation *pTrans)
    56 {
    57 	TransError err = Trans_BadLanguage;
    58 	CPhraseFile *phrfl;
    59 
    60 	for (size_t i=0; i<langcount && err!=Trans_Okay; i++)
    61 	{
    62 		phrfl = g_Translator.GetFileByIndex(pl->GetLangFileByIndex(i));
    63 		err = phrfl->GetTranslation(key, langid, pTrans);
    64 	}
    65 
    66 	return (err == Trans_Okay) ? true : false;
    67 }
    68 
    69 size_t Translate(char *buffer, size_t maxlen, IPluginContext *pCtx, const char *key, cell_t target, const cell_t *params, int *arg, bool *error)
    70 {
    71 	unsigned int langid;
    72 	*error = false;
    73 	Translation pTrans;
    74 	CPlugin *pl = (CPlugin *)g_PluginSys.FindPluginByContext(pCtx->GetContext());
    75 	size_t langcount = pl->GetLangFileCount();
    76 	void *new_params[MAX_TRANSLATE_PARAMS];
    77 	unsigned int max_params = 0;
    78 
    79 try_serverlang:
    80 	if (target == LANG_SERVER)
    81 	{
    82 		langid = g_Translator.GetServerLanguage();
    83  	} else if ((target >= 1) && (target <= g_Players.GetMaxClients())) {
    84 		langid = g_Translator.GetServerLanguage();
    85 	} else {
    86 		pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Translation failed: invalid client index %d", target);
    87 		goto error_out;
    88 	}
    89 
    90 	if (!TryTranslation(pl, key, langid, langcount, &pTrans))
    91 	{
    92 		if (target != LANG_SERVER)
    93 		{
    94 			target = LANG_SERVER;
    95 			goto try_serverlang;
    96 		} else if (langid != LANGUAGE_ENGLISH) {
    97 			if (!TryTranslation(pl, key, LANGUAGE_ENGLISH, langcount, &pTrans))
    98 			{
    99 				pCtx->ThrowNativeErrorEx(SP_ERROR_PARAM, "Language phrase \"%s\" not found", key);
   100 				goto error_out;
   101 			}
   102 		}
   103 	}
   104 
   105 	max_params = pTrans.fmt_count;
   106 
   107 	for (size_t i=0; i<max_params; i++)
   108 	{
   109 		pCtx->LocalToPhysAddr(params[*arg], reinterpret_cast<cell_t **>(&new_params[i]));
   110 		(*arg)++;
   111 		if ((*arg) + i > (size_t)params[0])
   112 		{
   113 			pCtx->ThrowNativeErrorEx(SP_ERROR_PARAMS_MAX, "Translation string formatted incorrectly - parameter %d (total %d)", (*arg) + i, params[0]);
   114 			goto error_out;
   115 		}
   116 	}
   117 
   118 	return g_Translator.Translate(buffer, maxlen, new_params, &pTrans);
   119 error_out:
   120 	*error = true;
   121 	return 0;
   122 }
   123 
   124 //:TODO: review this code before we choose a license
   125 
   126 void AddString(char **buf_p, size_t &maxlen, const char *string, int width, int prec)
   127 {
   128 	int size = 0;
   129 	char *buf;
   130 	static char nlstr[] = {'(','n','u','l','l',')','\0'};
   131 
   132 	buf = *buf_p;
   133 
   134 	if (string == NULL)
   135 	{
   136 		string = nlstr;
   137 		prec = -1;
   138 	}
   139 
   140 	if (prec >= 0)
   141 	{
   142 		for (size = 0; size < prec; size++) 
   143 		{
   144 			if (string[size] == '\0')
   145 			{
   146 				break;
   147 			}
   148 		}
   149 	} else {
   150 		while (string[size++]);
   151 		size--;
   152 	}
   153 
   154 	if (size > (int)maxlen)
   155 	{
   156 		size = maxlen;
   157 	}
   158 
   159 	maxlen -= size;
   160 	width -= size;
   161 
   162 	while (size--)
   163 	{
   164 		*buf++ = *string++;
   165 	}
   166 
   167 	while ((width-- > 0) && maxlen)
   168 	{
   169 		*buf++ = ' ';
   170 		maxlen--;
   171 	}
   172 
   173 	*buf_p = buf;
   174 }
   175 
   176 void AddFloat(char **buf_p, size_t &maxlen, double fval, int width, int prec)
   177 {
   178 	char text[32];
   179 	int digits;
   180 	double signedVal;
   181 	char *buf;
   182 	int val;
   183 
   184 	// get the sign
   185 	signedVal = fval;
   186 	if (fval < 0)
   187 	{
   188 		fval = -fval;
   189 	}
   190 
   191 	// write the float number
   192 	digits = 0;
   193 	val = (int)fval;
   194 	do
   195 	{
   196 		text[digits++] = '0' + val % 10;
   197 		val /= 10;
   198 	} while (val);
   199 
   200 	if (signedVal < 0)
   201 	{
   202 		text[digits++] = '-';
   203 	}
   204 
   205 	buf = *buf_p;
   206 
   207 	while ((digits < width) && maxlen)
   208 	{
   209 		*buf++ = ' ';
   210 		width--;
   211 		maxlen--;
   212 	}
   213 
   214 	while ((digits--) && maxlen)
   215 	{
   216 		*buf++ = text[digits];
   217 		maxlen--;
   218 	}
   219 
   220 	*buf_p = buf;
   221 
   222 	if (prec < 0)
   223 	{
   224 		prec = 6;
   225 	}
   226 	// write the fraction
   227 	digits = 0;
   228 	while (digits < prec)
   229 	{
   230 		fval -= (int)fval;
   231 		fval *= 10.0;
   232 		val = (int)fval;
   233 		text[digits++] = '0' + val % 10;
   234 	}
   235 
   236 	if ((digits > 0) && maxlen)
   237 	{
   238 		buf = *buf_p;
   239 		*buf++ = '.';
   240 		maxlen--;
   241 		for (prec = 0; maxlen && (prec < digits); prec++)
   242 		{
   243 			*buf++ = text[prec];
   244 			maxlen--;
   245 		}
   246 		*buf_p = buf;
   247 	}
   248 }
   249 
   250 void AddUInt(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
   251 {
   252 	char text[32];
   253 	int digits;
   254 	char *buf;
   255 
   256 	digits = 0;
   257 	do
   258 	{
   259 		text[digits++] = '0' + val % 10;
   260 		val /= 10;
   261 	} while (val);
   262 
   263 	buf = *buf_p;
   264 
   265 	if (!(flags & LADJUST))
   266 	{
   267 		while (digits < width && maxlen)
   268 		{
   269 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   270 			width--;
   271 			maxlen--;
   272 		}
   273 	}
   274 
   275 	while (digits-- && maxlen)
   276 	{
   277 		*buf++ = text[digits];
   278 		width--;
   279 		maxlen--;
   280 	}
   281 
   282 	if (flags & LADJUST)
   283 	{
   284 		while (width-- && maxlen)
   285 		{
   286 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   287 			maxlen--;
   288 		}
   289 	}
   290 
   291 	*buf_p = buf;
   292 }
   293 
   294 void AddInt(char **buf_p, size_t &maxlen, int val, int width, int flags)
   295 {
   296 	char text[32];
   297 	int digits;
   298 	int signedVal;
   299 	char *buf;
   300 	unsigned int unsignedVal;
   301 
   302 	digits = 0;
   303 	signedVal = val;
   304 	if (val < 0)
   305 	{
   306 		/* we want the unsigned version */
   307 		unsignedVal = abs(val);
   308 	} else {
   309 		unsignedVal = val;
   310 	}
   311 	do
   312 	{
   313 		text[digits++] = '0' + unsignedVal % 10;
   314 		unsignedVal /= 10;
   315 	} while (unsignedVal);
   316 
   317 	if (signedVal < 0)
   318 	{
   319 		text[digits++] = '-';
   320 	}
   321 
   322 	buf = *buf_p;
   323 
   324 	if (!(flags & LADJUST))
   325 	{
   326 		while ((digits < width) && maxlen)
   327 		{
   328 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   329 			width--;
   330 			maxlen--;
   331 		}
   332 	}
   333 
   334 	while (digits-- && maxlen)
   335 	{
   336 		*buf++ = text[digits];
   337 		width--;
   338 		maxlen--;
   339 	}
   340 
   341 	if (flags & LADJUST)
   342 	{
   343 		while (width-- && maxlen)
   344 		{
   345 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   346 			maxlen--;
   347 		}
   348 	}
   349 
   350 	*buf_p = buf;
   351 }
   352 
   353 void AddHex(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
   354 {
   355 	char text[32];
   356 	int digits;
   357 	char *buf;
   358 	char digit;
   359 	int hexadjust;
   360 
   361 	if (flags & UPPERDIGITS)
   362 	{
   363 		hexadjust = 'A' - '9' - 1;
   364 	} else {
   365 		hexadjust = 'a' - '9' - 1;
   366 	}
   367 
   368 	digits = 0;
   369 	do 
   370 	{
   371 		digit = ('0' + val % 16);
   372 		if (digit > '9')
   373 		{
   374 			digit += hexadjust;
   375 		}
   376 
   377 		text[digits++] = digit;
   378 		val /= 16;
   379 	} while(val);
   380 
   381 	buf = *buf_p;
   382 
   383 	if (!(flags & LADJUST))
   384 	{
   385 		while (digits < width && maxlen)
   386 		{
   387 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   388 			width--;
   389 			maxlen--;
   390 		}
   391 	}
   392 
   393 	while (digits-- && maxlen)
   394 	{
   395 		*buf++ = text[digits];
   396 		width--;
   397 		maxlen--;
   398 	}
   399 
   400 	if (flags & LADJUST)
   401 	{
   402 		while (width-- && maxlen)
   403 		{
   404 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   405 			maxlen--;
   406 		}
   407 	}
   408 
   409 	*buf_p = buf;
   410 }
   411 
   412 size_t gnprintf(char *buffer, size_t maxlen, const char *format, void **args)
   413 {
   414 	if (!buffer || !maxlen)
   415 	{
   416 		return 0;
   417 	}
   418 
   419 	int arg = 0;
   420 	char *buf_p;
   421 	char ch;
   422 	int flags;
   423 	int width;
   424 	int prec;
   425 	int n;
   426 	char sign;
   427 	const char *fmt;
   428 	size_t llen = maxlen - 1;
   429 
   430 	buf_p = buffer;
   431 	fmt = format;
   432 
   433 	while (true)
   434 	{
   435 		// run through the format string until we hit a '%' or '\0'
   436 		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
   437 		{
   438 			*buf_p++ = ch;
   439 			llen--;
   440 		}
   441 		if ((ch == '\0') || (llen <= 0))
   442 		{
   443 			goto done;
   444 		}
   445 
   446 		// skip over the '%'
   447 		fmt++;
   448 
   449 		// reset formatting state
   450 		flags = 0;
   451 		width = 0;
   452 		prec = -1;
   453 		sign = '\0';
   454 
   455 rflag:
   456 		ch = *fmt++;
   457 reswitch:
   458 		switch(ch)
   459 		{
   460 		case '-':
   461 			{
   462 				flags |= LADJUST;
   463 				goto rflag;
   464 			}
   465 		case '.':
   466 			{
   467 				n = 0;
   468 				while(is_digit((ch = *fmt++)))
   469 				{
   470 					n = 10 * n + (ch - '0');
   471 				}
   472 				prec = (n < 0) ? -1 : n;
   473 				goto reswitch;
   474 			}
   475 		case '0':
   476 			{
   477 				flags |= ZEROPAD;
   478 				goto rflag;
   479 			}
   480 		case '1':
   481 		case '2':
   482 		case '3':
   483 		case '4':
   484 		case '5':
   485 		case '6':
   486 		case '7':
   487 		case '8':
   488 		case '9':
   489 			{
   490 				n = 0;
   491 				do
   492 				{
   493 					n = 10 * n + (ch - '0');
   494 					ch = *fmt++;
   495 				} while(is_digit(ch));
   496 				width = n;
   497 				goto reswitch;
   498 			}
   499 		case 'c':
   500 			{
   501 				if (!llen)
   502 				{
   503 					goto done;
   504 				}
   505 				char *c = (char *)args[arg];
   506 				*buf_p++ = *c;
   507 				llen--;
   508 				arg++;
   509 				break;
   510 			}
   511 		case 'd':
   512 		case 'i':
   513 			{
   514 				int *value = (int *)args[arg];
   515 				AddInt(&buf_p, llen, *value, width, flags);
   516 				arg++;
   517 				break;
   518 			}
   519 		case 'u':
   520 			{
   521 				unsigned int *value = (unsigned int *)args[arg];
   522 				AddUInt(&buf_p, llen, *value, width, flags);
   523 				arg++;
   524 				break;
   525 			}
   526 		case 'f':
   527 			{
   528 				float *value = (float *)args[arg];
   529 				AddFloat(&buf_p, llen, *value, width, prec);
   530 				arg++;
   531 				break;
   532 			}
   533 		case 's':
   534 			{
   535 				const char *str = (const char *)args[arg];
   536 				AddString(&buf_p, llen, str, width, prec);
   537 				arg++;
   538 				break;
   539 			}
   540 		case 'X':
   541 			{
   542 				unsigned int *value = (unsigned int *)args[arg];
   543 				flags |= UPPERDIGITS;
   544 				AddHex(&buf_p, llen, *value, width, flags);
   545 				arg++;
   546 				break;
   547 			}
   548 		case 'x':
   549 			{
   550 				unsigned int *value = (unsigned int *)args[arg];
   551 				AddHex(&buf_p, llen, *value, width, flags);
   552 				arg++;
   553 				break;
   554 			}
   555 		case '%':
   556 			{
   557 				if (!llen)
   558 				{
   559 					goto done;
   560 				}
   561 				*buf_p++ = ch;
   562 				llen--;
   563 				break;
   564 			}
   565 		case '\0':
   566 			{
   567 				if (!llen)
   568 				{
   569 					goto done;
   570 				}
   571 				*buf_p++ = '%';
   572 				llen--;
   573 				goto done;
   574 			}
   575 		default:
   576 			{
   577 				if (!llen)
   578 				{
   579 					goto done;
   580 				}
   581 				*buf_p++ = ch;
   582 				llen--;
   583 				break;
   584 			}
   585 		}
   586 	}
   587 
   588 done:
   589 	*buf_p = '\0';
   590 
   591 	return (maxlen - llen - 1);
   592 }
   593 
   594 size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param)
   595 {
   596 	if (!buffer || !maxlen)
   597 	{
   598 		return 0;
   599 	}
   600 
   601 	int arg;
   602 	int args = params[0];
   603 	char *buf_p;
   604 	char ch;
   605 	int flags;
   606 	int width;
   607 	int prec;
   608 	int n;
   609 	char sign;
   610 	const char *fmt;
   611 	size_t llen = maxlen - 1;
   612 
   613 	buf_p = buffer;
   614 	arg = *param;
   615 	fmt = format;
   616 
   617 	while (true)
   618 	{
   619 		// run through the format string until we hit a '%' or '\0'
   620 		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
   621 		{
   622 			*buf_p++ = ch;
   623 			llen--;
   624 		}
   625 		if ((ch == '\0') || (llen <= 0))
   626 		{
   627 			goto done;
   628 		}
   629 
   630 		// skip over the '%'
   631 		fmt++;
   632 
   633 		// reset formatting state
   634 		flags = 0;
   635 		width = 0;
   636 		prec = -1;
   637 		sign = '\0';
   638 
   639 rflag:
   640 		ch = *fmt++;
   641 reswitch:
   642 		switch(ch)
   643 		{
   644 		case '-':
   645 			{
   646 				flags |= LADJUST;
   647 				goto rflag;
   648 			}
   649 		case '.':
   650 			{
   651 				n = 0;
   652 				while(is_digit((ch = *fmt++)))
   653 				{
   654 					n = 10 * n + (ch - '0');
   655 				}
   656 				prec = (n < 0) ? -1 : n;
   657 				goto reswitch;
   658 			}
   659 		case '0':
   660 			{
   661 				flags |= ZEROPAD;
   662 				goto rflag;
   663 			}
   664 		case '1':
   665 		case '2':
   666 		case '3':
   667 		case '4':
   668 		case '5':
   669 		case '6':
   670 		case '7':
   671 		case '8':
   672 		case '9':
   673 			{
   674 				n = 0;
   675 				do
   676 				{
   677 					n = 10 * n + (ch - '0');
   678 					ch = *fmt++;
   679 				} while(is_digit(ch));
   680 				width = n;
   681 				goto reswitch;
   682 			}
   683 		case 'c':
   684 			{
   685 				CHECK_ARGS(0);
   686 				if (!llen)
   687 				{
   688 					goto done;
   689 				}
   690 				char *c;
   691 				pCtx->LocalToString(params[arg], &c);
   692 				*buf_p++ = *c;
   693 				llen--;
   694 				arg++;
   695 				break;
   696 			}
   697 		case 'd':
   698 		case 'i':
   699 			{
   700 				CHECK_ARGS(0);
   701 				cell_t *value;
   702 				pCtx->LocalToPhysAddr(params[arg], &value);
   703 				AddInt(&buf_p, llen, static_cast<int>(*value), width, flags);
   704 				arg++;
   705 				break;
   706 			}
   707 		case 'u':
   708 			{
   709 				CHECK_ARGS(0);
   710 				cell_t *value;
   711 				pCtx->LocalToPhysAddr(params[arg], &value);
   712 				AddUInt(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
   713 				arg++;
   714 				break;
   715 			}
   716 		case 'f':
   717 			{
   718 				CHECK_ARGS(0);
   719 				cell_t *value;
   720 				pCtx->LocalToPhysAddr(params[arg], &value);
   721 				AddFloat(&buf_p, llen, sp_ctof(*value), width, prec);
   722 				arg++;
   723 				break;
   724 			}
   725 		case 's':
   726 			{
   727 				CHECK_ARGS(0);
   728 				char *str;
   729 				int err;
   730 				if ((err=pCtx->LocalToString(params[arg], &str)) != SP_ERROR_NONE)
   731 				{
   732 					pCtx->ThrowNativeErrorEx(err, "Could not deference string");
   733 					return 0;
   734 				}
   735 				AddString(&buf_p, llen, str, width, prec);
   736 				arg++;
   737 				break;
   738 			}
   739 		case 'T':
   740 			{
   741 				CHECK_ARGS(0);
   742 				char *key;
   743 				bool error;
   744 				size_t res;
   745 				cell_t *target;
   746 				pCtx->LocalToPhysAddr(params[arg++], &target);
   747 				pCtx->LocalToString(params[arg++], &key);
   748 				res = Translate(buf_p, llen, pCtx, key, *target, params, &arg, &error);
   749 				if (error)
   750 				{
   751 					return 0;
   752 				}
   753 				buf_p += res;
   754 				llen -= res;
   755 				break;
   756 			}
   757 		case 't':
   758 			{
   759 				CHECK_ARGS(0);
   760 				char *key;
   761 				bool error;
   762 				size_t res;
   763 				cell_t target = g_SourceMod.GetGlobalTarget();
   764 				pCtx->LocalToString(params[arg++], &key);
   765 				res = Translate(buf_p, llen, pCtx, key, target, params, &arg, &error);
   766 				if (error)
   767 				{
   768 					return 0;
   769 				}
   770 				buf_p += res;
   771 				llen -= res;
   772 				break;
   773 			}
   774 		case 'X':
   775 			{
   776 				CHECK_ARGS(0);
   777 				cell_t *value;
   778 				pCtx->LocalToPhysAddr(params[arg], &value);
   779 				flags |= UPPERDIGITS;
   780 				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
   781 				arg++;
   782 				break;
   783 			}
   784 		case 'x':
   785 			{
   786 				CHECK_ARGS(0);
   787 				cell_t *value;
   788 				pCtx->LocalToPhysAddr(params[arg], &value);
   789 				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
   790 				arg++;
   791 				break;
   792 			}
   793 		case '%':
   794 			{
   795 				if (!llen)
   796 				{
   797 					goto done;
   798 				}
   799 				*buf_p++ = ch;
   800 				llen--;
   801 				break;
   802 			}
   803 		case '\0':
   804 			{
   805 				if (!llen)
   806 				{
   807 					goto done;
   808 				}
   809 				*buf_p++ = '%';
   810 				llen--;
   811 				goto done;
   812 			}
   813 		default:
   814 			{
   815 				if (!llen)
   816 				{
   817 					goto done;
   818 				}
   819 				*buf_p++ = ch;
   820 				llen--;
   821 				break;
   822 			}
   823 		}
   824 	}
   825 
   826 done:
   827 	*buf_p = '\0';
   828 	*param = arg;
   829 	return (maxlen - llen - 1);
   830 }
   831 
   832 const char *stristr(const char *str, const char *substr)
   833 {
   834 	if (!*substr)
   835 	{
   836 		return ((char *)str);
   837 	}
   838 
   839 	char *needle = (char *)substr;
   840 	char *prevloc = (char *)str;
   841 	char *haystack = (char *)str;
   842 
   843 	while (*haystack)
   844 	{
   845 		if (tolower(*haystack) == tolower(*needle))
   846 		{
   847 			haystack++;
   848 			if (!*++needle)
   849 			{
   850 				return prevloc;
   851 			}
   852 		} else {
   853 			haystack = ++prevloc;
   854 			needle = (char *)substr;
   855 		}
   856 	}
   857 
   858 	return NULL;
   859 }
   860 
   861 unsigned int strncopy(char *dest, const char *src, size_t count)
   862 {
   863 	if (!count)
   864 	{
   865 		return 0;
   866 	}
   867 
   868 	char *start = dest;
   869 	while ((*src) && (--count))
   870 	{
   871 		*dest++ = *src++;
   872 	}
   873 	*dest = '\0';
   874 
   875 	return (dest - start);
   876 }
   877 
   878 size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
   879 {
   880 	va_list ap;
   881 	va_start(ap, fmt);
   882 	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
   883 	va_end(ap);
   884 
   885 	if (len >= maxlength)
   886 	{
   887 		buffer[maxlength - 1] = '\0';
   888 		return (maxlength - 1);
   889 	} else {
   890 		return len;
   891 	}
   892 }
   893 
   894 size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap)
   895 {
   896 	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
   897 
   898 	if (len >= maxlength)
   899 	{
   900 		buffer[maxlength - 1] = '\0';
   901 		return (maxlength - 1);
   902 	} else {
   903 		return len;
   904 	}
   905 }
   906 
   907 char *sm_strdup(const char *str)
   908 {
   909 	char *ptr = new char[strlen(str)+1];
   910 	strcpy(ptr, str);
   911 	return ptr;
   912 }
   913 
   914 unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace)
   915 {
   916 	size_t searchLen = strlen(search);
   917 	size_t replaceLen = strlen(replace);
   918 
   919 	char *ptr = subject;
   920 	unsigned int total = 0;
   921 	while ((ptr = UTIL_ReplaceEx(ptr, maxlength, search, searchLen, replace, replaceLen)) != NULL)
   922 	{
   923 		total++;
   924 		if (*ptr == '\0')
   925 		{
   926 			break;
   927 		}
   928 	}
   929 
   930 	return total;
   931 }
   932 
   933 /**
   934  * NOTE: Do not edit this for the love of god unless you have 
   935  * read the test cases and understand the code behind each one.
   936  * While I don't guarantee there aren't mistakes, I do guarantee
   937  * that plugins will end up relying on tiny idiosyncracies of this
   938  * function, just like they did with AMX Mod X.
   939  *
   940  * There are explicitly more cases than the AMX Mod X version because
   941  * we're not doing a blind copy.  Each case is specifically optimized
   942  * for what needs to be done.  Even better, we don't have to error on
   943  * bad buffer sizes.  Instead, this function will smartly cut off the 
   944  * string in a way that pushes old data out.
   945  */
   946 char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen)
   947 {
   948 	char *ptr = subject;
   949 	size_t browsed = 0;
   950 	size_t textLen = strlen(subject);
   951 
   952 	/* It's not possible to search or replace */
   953 	if (searchLen > textLen || maxLen <= 1)
   954 	{
   955 		return NULL;
   956 	}
   957 
   958 	/* Subtract one off the maxlength so we can include the null terminator */
   959 	maxLen--;
   960 
   961 	while (*ptr != '\0' && (browsed <= textLen - searchLen))
   962 	{
   963 		/* See if we get a comparison */
   964 		if (strncmp(ptr, search, searchLen) == 0)
   965 		{
   966 			if (replaceLen > searchLen)
   967 			{
   968 				/* First, see if we have enough space to do this operation */
   969 				if (maxLen - textLen < replaceLen - searchLen)
   970 				{
   971 					/* First, see if the replacement length goes out of bounds. */
   972 					if (browsed + replaceLen >= maxLen)
   973 					{
   974 						/* EXAMPLE CASE:
   975 						 * Subject: AABBBCCC
   976 						 * Buffer : 12 bytes
   977 						 * Search : BBB
   978 						 * Replace: DDDDDDDDDD
   979 						 * OUTPUT : AADDDDDDDDD
   980 						 * POSITION:           ^
   981 						 */
   982 						/* If it does, we'll just bound the length and do a strcpy. */
   983 						replaceLen = maxLen - browsed;
   984 						/* Note, we add one to the final result for the null terminator */
   985 						strncopy(ptr, replace, replaceLen+1);
   986 					} else {
   987 						/* EXAMPLE CASE:
   988 						 * Subject: AABBBCCC
   989 						 * Buffer : 12 bytes
   990 						 * Search : BBB
   991 						 * Replace: DDDDDDD
   992 						 * OUTPUT : AADDDDDDDCC
   993 						 * POSITION:         ^
   994 						 */
   995 						/* We're going to have some bytes left over... */
   996 						size_t origBytesToCopy = (textLen - (browsed + searchLen)) + 1;
   997 						size_t realBytesToCopy = (maxLen - (browsed + replaceLen)) + 1;
   998 						char *moveFrom = ptr + searchLen + (origBytesToCopy - realBytesToCopy);
   999 						char *moveTo = ptr + replaceLen;
  1000 
  1001 						/* First, move our old data out of the way. */
  1002 						memmove(moveTo, moveFrom, realBytesToCopy);
  1003 
  1004 						/* Now, do our replacement. */
  1005 						memcpy(ptr, replace, replaceLen);
  1006 					}
  1007 				} else {
  1008 					/* EXAMPLE CASE:
  1009 					 * Subject: AABBBCCC
  1010 					 * Buffer : 12 bytes
  1011 					 * Search : BBB
  1012 					 * Replace: DDDD
  1013 					 * OUTPUT : AADDDDCCC
  1014 					 * POSITION:      ^
  1015 					 */
  1016 					/* Yes, we have enough space.  Do a normal move operation. */
  1017 					char *moveFrom = ptr + searchLen;
  1018 					char *moveTo = ptr + replaceLen;
  1019 
  1020 					/* First move our old data out of the way. */
  1021 					size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
  1022 					memmove(moveTo, moveFrom, bytesToCopy);
  1023 
  1024 					/* Now do our replacement. */
  1025 					memcpy(ptr, replace, replaceLen);
  1026 				}
  1027 			} else if (replaceLen < searchLen) {
  1028 				/* EXAMPLE CASE:
  1029 				 * Subject: AABBBCCC
  1030 				 * Buffer : 12 bytes
  1031 				 * Search : BBB
  1032 				 * Replace: D
  1033 				 * OUTPUT : AADCCC
  1034 				 * POSOTION:   ^
  1035 				 */
  1036 				/* If the replacement does not grow the string length, we do not
  1037 				 * need to do any fancy checking at all.  Yay!
  1038 				 */
  1039 				char *moveFrom = ptr + searchLen;		/* Start after the search pointer */
  1040 				char *moveTo = ptr + replaceLen;		/* Copy to where the replacement ends */
  1041 
  1042 				/* Copy our replacement in, if any */
  1043 				if (replaceLen)
  1044 				{
  1045 					memcpy(ptr, replace, replaceLen);
  1046 				}
  1047 
  1048 				/* Figure out how many bytes to move down, including null terminator */
  1049 				size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
  1050 
  1051 				/* Move the rest of the string down */
  1052 				memmove(moveTo, moveFrom, bytesToCopy);
  1053 			} else {
  1054 				/* EXAMPLE CASE:
  1055 				 * Subject: AABBBCCC
  1056 				 * Buffer : 12 bytes
  1057 				 * Search : BBB
  1058 				 * Replace: DDD
  1059 				 * OUTPUT : AADDDCCC
  1060 				 * POSITION:     ^
  1061 				 */
  1062 				/* We don't have to move anything around, just do a straight copy */
  1063 				memcpy(ptr, replace, replaceLen);
  1064 			}
  1065 
  1066 			return ptr + replaceLen;
  1067 		}
  1068 		ptr++;
  1069 		browsed++;
  1070 	}
  1071 
  1072 	return NULL;
  1073 }