core/sm_stringutil.cpp
author David Anderson <dvander@alliedmods.net>
Thu Jun 07 05:53:32 2007 +0000 (2007-06-07)
changeset 906 21fcb8ccdf2b
parent 905 4b0a5d2f1cbe
child 909 4feac08f6764
permissions -rw-r--r--
fixed a bug where the console was not a valid loggable player from %L
     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 'L':
   726 			{
   727 				CHECK_ARGS(0);
   728 				cell_t *value;
   729 				pCtx->LocalToPhysAddr(params[arg], &value);
   730 				char buffer[255];
   731 				if (*value)
   732 				{
   733 					CPlayer *player = g_Players.GetPlayerByIndex(*value);
   734 					if (!player || !player->IsConnected())
   735 					{
   736 						return pCtx->ThrowNativeError("Client index %d is invalid", *value);
   737 					}
   738 					const char *auth = player->GetAuthString();
   739 					if (!auth || auth[0] == '\0')
   740 					{
   741 						auth = "STEAM_ID_PENDING";
   742 					}
   743 					int userid = engine->GetPlayerUserId(player->GetEdict());
   744 					UTIL_Format(buffer, 
   745 						sizeof(buffer), 
   746 						"%s<%d><%s><>", 
   747 						player->GetName(),
   748 						userid,
   749 						auth);
   750 				} else {
   751 					UTIL_Format(buffer,
   752 						sizeof(buffer),
   753 						"Console<0><Console><Console>");
   754 				}
   755 				AddString(&buf_p, llen, buffer, width, prec);
   756 				arg++;
   757 				break;
   758 			}
   759 		case 's':
   760 			{
   761 				CHECK_ARGS(0);
   762 				char *str;
   763 				int err;
   764 				if ((err=pCtx->LocalToString(params[arg], &str)) != SP_ERROR_NONE)
   765 				{
   766 					pCtx->ThrowNativeErrorEx(err, "Could not deference string");
   767 					return 0;
   768 				}
   769 				AddString(&buf_p, llen, str, width, prec);
   770 				arg++;
   771 				break;
   772 			}
   773 		case 'T':
   774 			{
   775 				CHECK_ARGS(1);
   776 				char *key;
   777 				bool error;
   778 				size_t res;
   779 				cell_t *target;
   780 				pCtx->LocalToString(params[arg++], &key);
   781 				pCtx->LocalToPhysAddr(params[arg++], &target);
   782 				res = Translate(buf_p, llen, pCtx, key, *target, params, &arg, &error);
   783 				if (error)
   784 				{
   785 					return 0;
   786 				}
   787 				buf_p += res;
   788 				llen -= res;
   789 				break;
   790 			}
   791 		case 't':
   792 			{
   793 				CHECK_ARGS(0);
   794 				char *key;
   795 				bool error;
   796 				size_t res;
   797 				cell_t target = g_SourceMod.GetGlobalTarget();
   798 				pCtx->LocalToString(params[arg++], &key);
   799 				res = Translate(buf_p, llen, pCtx, key, target, params, &arg, &error);
   800 				if (error)
   801 				{
   802 					return 0;
   803 				}
   804 				buf_p += res;
   805 				llen -= res;
   806 				break;
   807 			}
   808 		case 'X':
   809 			{
   810 				CHECK_ARGS(0);
   811 				cell_t *value;
   812 				pCtx->LocalToPhysAddr(params[arg], &value);
   813 				flags |= UPPERDIGITS;
   814 				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
   815 				arg++;
   816 				break;
   817 			}
   818 		case 'x':
   819 			{
   820 				CHECK_ARGS(0);
   821 				cell_t *value;
   822 				pCtx->LocalToPhysAddr(params[arg], &value);
   823 				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
   824 				arg++;
   825 				break;
   826 			}
   827 		case '%':
   828 			{
   829 				if (!llen)
   830 				{
   831 					goto done;
   832 				}
   833 				*buf_p++ = ch;
   834 				llen--;
   835 				break;
   836 			}
   837 		case '\0':
   838 			{
   839 				if (!llen)
   840 				{
   841 					goto done;
   842 				}
   843 				*buf_p++ = '%';
   844 				llen--;
   845 				goto done;
   846 			}
   847 		default:
   848 			{
   849 				if (!llen)
   850 				{
   851 					goto done;
   852 				}
   853 				*buf_p++ = ch;
   854 				llen--;
   855 				break;
   856 			}
   857 		}
   858 	}
   859 
   860 done:
   861 	*buf_p = '\0';
   862 	*param = arg;
   863 	return (maxlen - llen - 1);
   864 }
   865 
   866 const char *stristr(const char *str, const char *substr)
   867 {
   868 	if (!*substr)
   869 	{
   870 		return ((char *)str);
   871 	}
   872 
   873 	char *needle = (char *)substr;
   874 	char *prevloc = (char *)str;
   875 	char *haystack = (char *)str;
   876 
   877 	while (*haystack)
   878 	{
   879 		if (tolower(*haystack) == tolower(*needle))
   880 		{
   881 			haystack++;
   882 			if (!*++needle)
   883 			{
   884 				return prevloc;
   885 			}
   886 		} else {
   887 			haystack = ++prevloc;
   888 			needle = (char *)substr;
   889 		}
   890 	}
   891 
   892 	return NULL;
   893 }
   894 
   895 unsigned int strncopy(char *dest, const char *src, size_t count)
   896 {
   897 	if (!count)
   898 	{
   899 		return 0;
   900 	}
   901 
   902 	char *start = dest;
   903 	while ((*src) && (--count))
   904 	{
   905 		*dest++ = *src++;
   906 	}
   907 	*dest = '\0';
   908 
   909 	return (dest - start);
   910 }
   911 
   912 size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
   913 {
   914 	va_list ap;
   915 	va_start(ap, fmt);
   916 	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
   917 	va_end(ap);
   918 
   919 	if (len >= maxlength)
   920 	{
   921 		buffer[maxlength - 1] = '\0';
   922 		return (maxlength - 1);
   923 	} else {
   924 		return len;
   925 	}
   926 }
   927 
   928 size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap)
   929 {
   930 	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
   931 
   932 	if (len >= maxlength)
   933 	{
   934 		buffer[maxlength - 1] = '\0';
   935 		return (maxlength - 1);
   936 	} else {
   937 		return len;
   938 	}
   939 }
   940 
   941 char *sm_strdup(const char *str)
   942 {
   943 	char *ptr = new char[strlen(str)+1];
   944 	strcpy(ptr, str);
   945 	return ptr;
   946 }
   947 
   948 unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace)
   949 {
   950 	size_t searchLen = strlen(search);
   951 	size_t replaceLen = strlen(replace);
   952 
   953 	char *ptr = subject;
   954 	unsigned int total = 0;
   955 	while ((ptr = UTIL_ReplaceEx(ptr, maxlength, search, searchLen, replace, replaceLen)) != NULL)
   956 	{
   957 		total++;
   958 		if (*ptr == '\0')
   959 		{
   960 			break;
   961 		}
   962 	}
   963 
   964 	return total;
   965 }
   966 
   967 /**
   968  * NOTE: Do not edit this for the love of god unless you have 
   969  * read the test cases and understand the code behind each one.
   970  * While I don't guarantee there aren't mistakes, I do guarantee
   971  * that plugins will end up relying on tiny idiosyncracies of this
   972  * function, just like they did with AMX Mod X.
   973  *
   974  * There are explicitly more cases than the AMX Mod X version because
   975  * we're not doing a blind copy.  Each case is specifically optimized
   976  * for what needs to be done.  Even better, we don't have to error on
   977  * bad buffer sizes.  Instead, this function will smartly cut off the 
   978  * string in a way that pushes old data out.
   979  */
   980 char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen)
   981 {
   982 	char *ptr = subject;
   983 	size_t browsed = 0;
   984 	size_t textLen = strlen(subject);
   985 
   986 	/* It's not possible to search or replace */
   987 	if (searchLen > textLen || maxLen <= 1)
   988 	{
   989 		return NULL;
   990 	}
   991 
   992 	/* Subtract one off the maxlength so we can include the null terminator */
   993 	maxLen--;
   994 
   995 	while (*ptr != '\0' && (browsed <= textLen - searchLen))
   996 	{
   997 		/* See if we get a comparison */
   998 		if (strncmp(ptr, search, searchLen) == 0)
   999 		{
  1000 			if (replaceLen > searchLen)
  1001 			{
  1002 				/* First, see if we have enough space to do this operation */
  1003 				if (maxLen - textLen < replaceLen - searchLen)
  1004 				{
  1005 					/* First, see if the replacement length goes out of bounds. */
  1006 					if (browsed + replaceLen >= maxLen)
  1007 					{
  1008 						/* EXAMPLE CASE:
  1009 						 * Subject: AABBBCCC
  1010 						 * Buffer : 12 bytes
  1011 						 * Search : BBB
  1012 						 * Replace: DDDDDDDDDD
  1013 						 * OUTPUT : AADDDDDDDDD
  1014 						 * POSITION:           ^
  1015 						 */
  1016 						/* If it does, we'll just bound the length and do a strcpy. */
  1017 						replaceLen = maxLen - browsed;
  1018 						/* Note, we add one to the final result for the null terminator */
  1019 						strncopy(ptr, replace, replaceLen+1);
  1020 					} else {
  1021 						/* EXAMPLE CASE:
  1022 						 * Subject: AABBBCCC
  1023 						 * Buffer : 12 bytes
  1024 						 * Search : BBB
  1025 						 * Replace: DDDDDDD
  1026 						 * OUTPUT : AADDDDDDDCC
  1027 						 * POSITION:         ^
  1028 						 */
  1029 						/* We're going to have some bytes left over... */
  1030 						size_t origBytesToCopy = (textLen - (browsed + searchLen)) + 1;
  1031 						size_t realBytesToCopy = (maxLen - (browsed + replaceLen)) + 1;
  1032 						char *moveFrom = ptr + searchLen + (origBytesToCopy - realBytesToCopy);
  1033 						char *moveTo = ptr + replaceLen;
  1034 
  1035 						/* First, move our old data out of the way. */
  1036 						memmove(moveTo, moveFrom, realBytesToCopy);
  1037 
  1038 						/* Now, do our replacement. */
  1039 						memcpy(ptr, replace, replaceLen);
  1040 					}
  1041 				} else {
  1042 					/* EXAMPLE CASE:
  1043 					 * Subject: AABBBCCC
  1044 					 * Buffer : 12 bytes
  1045 					 * Search : BBB
  1046 					 * Replace: DDDD
  1047 					 * OUTPUT : AADDDDCCC
  1048 					 * POSITION:      ^
  1049 					 */
  1050 					/* Yes, we have enough space.  Do a normal move operation. */
  1051 					char *moveFrom = ptr + searchLen;
  1052 					char *moveTo = ptr + replaceLen;
  1053 
  1054 					/* First move our old data out of the way. */
  1055 					size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
  1056 					memmove(moveTo, moveFrom, bytesToCopy);
  1057 
  1058 					/* Now do our replacement. */
  1059 					memcpy(ptr, replace, replaceLen);
  1060 				}
  1061 			} else if (replaceLen < searchLen) {
  1062 				/* EXAMPLE CASE:
  1063 				 * Subject: AABBBCCC
  1064 				 * Buffer : 12 bytes
  1065 				 * Search : BBB
  1066 				 * Replace: D
  1067 				 * OUTPUT : AADCCC
  1068 				 * POSOTION:   ^
  1069 				 */
  1070 				/* If the replacement does not grow the string length, we do not
  1071 				 * need to do any fancy checking at all.  Yay!
  1072 				 */
  1073 				char *moveFrom = ptr + searchLen;		/* Start after the search pointer */
  1074 				char *moveTo = ptr + replaceLen;		/* Copy to where the replacement ends */
  1075 
  1076 				/* Copy our replacement in, if any */
  1077 				if (replaceLen)
  1078 				{
  1079 					memcpy(ptr, replace, replaceLen);
  1080 				}
  1081 
  1082 				/* Figure out how many bytes to move down, including null terminator */
  1083 				size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
  1084 
  1085 				/* Move the rest of the string down */
  1086 				memmove(moveTo, moveFrom, bytesToCopy);
  1087 			} else {
  1088 				/* EXAMPLE CASE:
  1089 				 * Subject: AABBBCCC
  1090 				 * Buffer : 12 bytes
  1091 				 * Search : BBB
  1092 				 * Replace: DDD
  1093 				 * OUTPUT : AADDDCCC
  1094 				 * POSITION:     ^
  1095 				 */
  1096 				/* We don't have to move anything around, just do a straight copy */
  1097 				memcpy(ptr, replace, replaceLen);
  1098 			}
  1099 
  1100 			return ptr + replaceLen;
  1101 		}
  1102 		ptr++;
  1103 		browsed++;
  1104 	}
  1105 
  1106 	return NULL;
  1107 }