core/sm_stringutil.cpp
author Matt Woodrow <pred@alliedmods.net>
Thu May 01 23:48:27 2008 +0000 (2008-05-01)
changeset 2035 941abff6deed
parent 1965 63d5a5f6935f
child 2042 4a2faafca477
permissions -rw-r--r--
Added msvc9 Project files to sdktools. Badly.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <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 AddBinary(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 		if (val & 1)
   370 		{
   371 			text[digits++] = '1';
   372 		}
   373 		else
   374 		{
   375 			text[digits++] = '0';
   376 		}
   377 		val >>= 1;
   378 	} while (val);
   379 
   380 	buf = *buf_p;
   381 
   382 	if (!(flags & LADJUST))
   383 	{
   384 		while (digits < width && maxlen)
   385 		{
   386 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   387 			width--;
   388 			maxlen--;
   389 		}
   390 	}
   391 
   392 	while (digits-- && maxlen)
   393 	{
   394 		*buf++ = text[digits];
   395 		width--;
   396 		maxlen--;
   397 	}
   398 
   399 	if (flags & LADJUST)
   400 	{
   401 		while (width-- && maxlen)
   402 		{
   403 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   404 			maxlen--;
   405 		}
   406 	}
   407 
   408 	*buf_p = buf;
   409 }
   410 
   411 void AddUInt(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
   412 {
   413 	char text[32];
   414 	int digits;
   415 	char *buf;
   416 
   417 	digits = 0;
   418 	do
   419 	{
   420 		text[digits++] = '0' + val % 10;
   421 		val /= 10;
   422 	} while (val);
   423 
   424 	buf = *buf_p;
   425 
   426 	if (!(flags & LADJUST))
   427 	{
   428 		while (digits < width && maxlen)
   429 		{
   430 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   431 			width--;
   432 			maxlen--;
   433 		}
   434 	}
   435 
   436 	while (digits-- && maxlen)
   437 	{
   438 		*buf++ = text[digits];
   439 		width--;
   440 		maxlen--;
   441 	}
   442 
   443 	if (flags & LADJUST)
   444 	{
   445 		while (width-- && maxlen)
   446 		{
   447 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   448 			maxlen--;
   449 		}
   450 	}
   451 
   452 	*buf_p = buf;
   453 }
   454 
   455 void AddInt(char **buf_p, size_t &maxlen, int val, int width, int flags)
   456 {
   457 	char text[32];
   458 	int digits;
   459 	int signedVal;
   460 	char *buf;
   461 	unsigned int unsignedVal;
   462 
   463 	digits = 0;
   464 	signedVal = val;
   465 	if (val < 0)
   466 	{
   467 		/* we want the unsigned version */
   468 		unsignedVal = abs(val);
   469 	}
   470 	else
   471 	{
   472 		unsignedVal = val;
   473 	}
   474 
   475 	do
   476 	{
   477 		text[digits++] = '0' + unsignedVal % 10;
   478 		unsignedVal /= 10;
   479 	} while (unsignedVal);
   480 
   481 	if (signedVal < 0)
   482 	{
   483 		text[digits++] = '-';
   484 	}
   485 
   486 	buf = *buf_p;
   487 
   488 	if (!(flags & LADJUST))
   489 	{
   490 		while ((digits < width) && maxlen)
   491 		{
   492 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   493 			width--;
   494 			maxlen--;
   495 		}
   496 	}
   497 
   498 	while (digits-- && maxlen)
   499 	{
   500 		*buf++ = text[digits];
   501 		width--;
   502 		maxlen--;
   503 	}
   504 
   505 	if (flags & LADJUST)
   506 	{
   507 		while (width-- && maxlen)
   508 		{
   509 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   510 			maxlen--;
   511 		}
   512 	}
   513 
   514 	*buf_p = buf;
   515 }
   516 
   517 void AddHex(char **buf_p, size_t &maxlen, unsigned int val, int width, int flags)
   518 {
   519 	char text[32];
   520 	int digits;
   521 	char *buf;
   522 	char digit;
   523 	int hexadjust;
   524 
   525 	if (flags & UPPERDIGITS)
   526 	{
   527 		hexadjust = 'A' - '9' - 1;
   528 	}
   529 	else
   530 	{
   531 		hexadjust = 'a' - '9' - 1;
   532 	}
   533 
   534 	digits = 0;
   535 	do 
   536 	{
   537 		digit = ('0' + val % 16);
   538 		if (digit > '9')
   539 		{
   540 			digit += hexadjust;
   541 		}
   542 
   543 		text[digits++] = digit;
   544 		val /= 16;
   545 	} while(val);
   546 
   547 	buf = *buf_p;
   548 
   549 	if (!(flags & LADJUST))
   550 	{
   551 		while (digits < width && maxlen)
   552 		{
   553 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   554 			width--;
   555 			maxlen--;
   556 		}
   557 	}
   558 
   559 	while (digits-- && maxlen)
   560 	{
   561 		*buf++ = text[digits];
   562 		width--;
   563 		maxlen--;
   564 	}
   565 
   566 	if (flags & LADJUST)
   567 	{
   568 		while (width-- && maxlen)
   569 		{
   570 			*buf++ = (flags & ZEROPAD) ? '0' : ' ';
   571 			maxlen--;
   572 		}
   573 	}
   574 
   575 	*buf_p = buf;
   576 }
   577 
   578 size_t gnprintf(char *buffer, size_t maxlen, const char *format, void **args)
   579 {
   580 	if (!buffer || !maxlen)
   581 	{
   582 		return 0;
   583 	}
   584 
   585 	int arg = 0;
   586 	char *buf_p;
   587 	char ch;
   588 	int flags;
   589 	int width;
   590 	int prec;
   591 	int n;
   592 	char sign;
   593 	const char *fmt;
   594 	size_t llen = maxlen - 1;
   595 
   596 	buf_p = buffer;
   597 	fmt = format;
   598 
   599 	while (true)
   600 	{
   601 		// run through the format string until we hit a '%' or '\0'
   602 		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
   603 		{
   604 			*buf_p++ = ch;
   605 			llen--;
   606 		}
   607 		if ((ch == '\0') || (llen <= 0))
   608 		{
   609 			goto done;
   610 		}
   611 
   612 		// skip over the '%'
   613 		fmt++;
   614 
   615 		// reset formatting state
   616 		flags = 0;
   617 		width = 0;
   618 		prec = -1;
   619 		sign = '\0';
   620 
   621 rflag:
   622 		ch = *fmt++;
   623 reswitch:
   624 		switch(ch)
   625 		{
   626 		case '-':
   627 			{
   628 				flags |= LADJUST;
   629 				goto rflag;
   630 			}
   631 		case '.':
   632 			{
   633 				n = 0;
   634 				while(is_digit((ch = *fmt++)))
   635 				{
   636 					n = 10 * n + (ch - '0');
   637 				}
   638 				prec = (n < 0) ? -1 : n;
   639 				goto reswitch;
   640 			}
   641 		case '0':
   642 			{
   643 				flags |= ZEROPAD;
   644 				goto rflag;
   645 			}
   646 		case '1':
   647 		case '2':
   648 		case '3':
   649 		case '4':
   650 		case '5':
   651 		case '6':
   652 		case '7':
   653 		case '8':
   654 		case '9':
   655 			{
   656 				n = 0;
   657 				do
   658 				{
   659 					n = 10 * n + (ch - '0');
   660 					ch = *fmt++;
   661 				} while(is_digit(ch));
   662 				width = n;
   663 				goto reswitch;
   664 			}
   665 		case 'c':
   666 			{
   667 				if (!llen)
   668 				{
   669 					goto done;
   670 				}
   671 				char *c = (char *)args[arg];
   672 				*buf_p++ = *c;
   673 				llen--;
   674 				arg++;
   675 				break;
   676 			}
   677 		case 'b':
   678 			{
   679 				int *value = (int *)args[arg];
   680 				AddBinary(&buf_p, llen, *value, width, flags);
   681 				arg++;
   682 				break;
   683 			}
   684 		case 'd':
   685 		case 'i':
   686 			{
   687 				int *value = (int *)args[arg];
   688 				AddInt(&buf_p, llen, *value, width, flags);
   689 				arg++;
   690 				break;
   691 			}
   692 		case 'u':
   693 			{
   694 				unsigned int *value = (unsigned int *)args[arg];
   695 				AddUInt(&buf_p, llen, *value, width, flags);
   696 				arg++;
   697 				break;
   698 			}
   699 		case 'f':
   700 			{
   701 				float *value = (float *)args[arg];
   702 				AddFloat(&buf_p, llen, *value, width, prec, flags);
   703 				arg++;
   704 				break;
   705 			}
   706 		case 's':
   707 			{
   708 				const char *str = (const char *)args[arg];
   709 				AddString(&buf_p, llen, str, width, prec);
   710 				arg++;
   711 				break;
   712 			}
   713 		case 'X':
   714 			{
   715 				unsigned int *value = (unsigned int *)args[arg];
   716 				flags |= UPPERDIGITS;
   717 				AddHex(&buf_p, llen, *value, width, flags);
   718 				arg++;
   719 				break;
   720 			}
   721 		case 'x':
   722 			{
   723 				unsigned int *value = (unsigned int *)args[arg];
   724 				AddHex(&buf_p, llen, *value, width, flags);
   725 				arg++;
   726 				break;
   727 			}
   728 		case '%':
   729 			{
   730 				if (!llen)
   731 				{
   732 					goto done;
   733 				}
   734 				*buf_p++ = ch;
   735 				llen--;
   736 				break;
   737 			}
   738 		case '\0':
   739 			{
   740 				if (!llen)
   741 				{
   742 					goto done;
   743 				}
   744 				*buf_p++ = '%';
   745 				llen--;
   746 				goto done;
   747 			}
   748 		default:
   749 			{
   750 				if (!llen)
   751 				{
   752 					goto done;
   753 				}
   754 				*buf_p++ = ch;
   755 				llen--;
   756 				break;
   757 			}
   758 		}
   759 	}
   760 
   761 done:
   762 	*buf_p = '\0';
   763 
   764 	return (maxlen - llen - 1);
   765 }
   766 
   767 size_t atcprintf(char *buffer, size_t maxlen, const char *format, IPluginContext *pCtx, const cell_t *params, int *param)
   768 {
   769 	if (!buffer || !maxlen)
   770 	{
   771 		return 0;
   772 	}
   773 
   774 	int arg;
   775 	int args = params[0];
   776 	char *buf_p;
   777 	char ch;
   778 	int flags;
   779 	int width;
   780 	int prec;
   781 	int n;
   782 	char sign;
   783 	const char *fmt;
   784 	size_t llen = maxlen - 1;
   785 
   786 	buf_p = buffer;
   787 	arg = *param;
   788 	fmt = format;
   789 
   790 	while (true)
   791 	{
   792 		// run through the format string until we hit a '%' or '\0'
   793 		for (ch = *fmt; llen && ((ch = *fmt) != '\0') && (ch != '%'); fmt++)
   794 		{
   795 			*buf_p++ = ch;
   796 			llen--;
   797 		}
   798 		if ((ch == '\0') || (llen <= 0))
   799 		{
   800 			goto done;
   801 		}
   802 
   803 		// skip over the '%'
   804 		fmt++;
   805 
   806 		// reset formatting state
   807 		flags = 0;
   808 		width = 0;
   809 		prec = -1;
   810 		sign = '\0';
   811 
   812 rflag:
   813 		ch = *fmt++;
   814 reswitch:
   815 		switch(ch)
   816 		{
   817 		case '-':
   818 			{
   819 				flags |= LADJUST;
   820 				goto rflag;
   821 			}
   822 		case '.':
   823 			{
   824 				n = 0;
   825 				while(is_digit((ch = *fmt++)))
   826 				{
   827 					n = 10 * n + (ch - '0');
   828 				}
   829 				prec = (n < 0) ? -1 : n;
   830 				goto reswitch;
   831 			}
   832 		case '0':
   833 			{
   834 				flags |= ZEROPAD;
   835 				goto rflag;
   836 			}
   837 		case '1':
   838 		case '2':
   839 		case '3':
   840 		case '4':
   841 		case '5':
   842 		case '6':
   843 		case '7':
   844 		case '8':
   845 		case '9':
   846 			{
   847 				n = 0;
   848 				do
   849 				{
   850 					n = 10 * n + (ch - '0');
   851 					ch = *fmt++;
   852 				} while(is_digit(ch));
   853 				width = n;
   854 				goto reswitch;
   855 			}
   856 		case 'c':
   857 			{
   858 				CHECK_ARGS(0);
   859 				if (!llen)
   860 				{
   861 					goto done;
   862 				}
   863 				char *c;
   864 				pCtx->LocalToString(params[arg], &c);
   865 				*buf_p++ = *c;
   866 				llen--;
   867 				arg++;
   868 				break;
   869 			}
   870 		case 'b':
   871 			{
   872 				CHECK_ARGS(0);
   873 				cell_t *value;
   874 				pCtx->LocalToPhysAddr(params[arg], &value);
   875 				AddBinary(&buf_p, llen, *value, width, flags);
   876 				arg++;
   877 				break;
   878 			}
   879 		case 'd':
   880 		case 'i':
   881 			{
   882 				CHECK_ARGS(0);
   883 				cell_t *value;
   884 				pCtx->LocalToPhysAddr(params[arg], &value);
   885 				AddInt(&buf_p, llen, static_cast<int>(*value), width, flags);
   886 				arg++;
   887 				break;
   888 			}
   889 		case 'u':
   890 			{
   891 				CHECK_ARGS(0);
   892 				cell_t *value;
   893 				pCtx->LocalToPhysAddr(params[arg], &value);
   894 				AddUInt(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
   895 				arg++;
   896 				break;
   897 			}
   898 		case 'f':
   899 			{
   900 				CHECK_ARGS(0);
   901 				cell_t *value;
   902 				pCtx->LocalToPhysAddr(params[arg], &value);
   903 				AddFloat(&buf_p, llen, sp_ctof(*value), width, prec, flags);
   904 				arg++;
   905 				break;
   906 			}
   907 		case 'L':
   908 			{
   909 				CHECK_ARGS(0);
   910 				cell_t *value;
   911 				pCtx->LocalToPhysAddr(params[arg], &value);
   912 				char buffer[255];
   913 				if (*value)
   914 				{
   915 					CPlayer *player = g_Players.GetPlayerByIndex(*value);
   916 					if (!player || !player->IsConnected())
   917 					{
   918 						return pCtx->ThrowNativeError("Client index %d is invalid", *value);
   919 					}
   920 					const char *auth = player->GetAuthString();
   921 					if (!auth || auth[0] == '\0')
   922 					{
   923 						auth = "STEAM_ID_PENDING";
   924 					}
   925 					int userid = engine->GetPlayerUserId(player->GetEdict());
   926 					UTIL_Format(buffer, 
   927 						sizeof(buffer), 
   928 						"%s<%d><%s><>", 
   929 						player->GetName(),
   930 						userid,
   931 						auth);
   932 				}
   933 				else
   934 				{
   935 					UTIL_Format(buffer,
   936 						sizeof(buffer),
   937 						"Console<0><Console><Console>");
   938 				}
   939 				AddString(&buf_p, llen, buffer, width, prec);
   940 				arg++;
   941 				break;
   942 			}
   943 		case 'N':
   944 			{
   945 				CHECK_ARGS(0);
   946 				cell_t *value;
   947 				pCtx->LocalToPhysAddr(params[arg], &value);
   948 
   949 				const char *name = "Console";
   950 				if (*value)
   951 				{
   952 					CPlayer *player = g_Players.GetPlayerByIndex(*value);
   953 					if (!player || !player->IsConnected())
   954 					{
   955 						return pCtx->ThrowNativeError("Client index %d is invalid", *value);
   956 					}
   957 					name = player->GetName();
   958 				}
   959 				AddString(&buf_p, llen, name, width, prec);
   960 				arg++;
   961 				break;
   962 			}
   963 		case 's':
   964 			{
   965 				CHECK_ARGS(0);
   966 				char *str;
   967 				int err;
   968 				if ((err=pCtx->LocalToString(params[arg], &str)) != SP_ERROR_NONE)
   969 				{
   970 					pCtx->ThrowNativeErrorEx(err, "Could not deference string");
   971 					return 0;
   972 				}
   973 				AddString(&buf_p, llen, str, width, prec);
   974 				arg++;
   975 				break;
   976 			}
   977 		case 'T':
   978 			{
   979 				CHECK_ARGS(1);
   980 				char *key;
   981 				bool error;
   982 				size_t res;
   983 				cell_t *target;
   984 				pCtx->LocalToString(params[arg++], &key);
   985 				pCtx->LocalToPhysAddr(params[arg++], &target);
   986 				res = Translate(buf_p, llen, pCtx, key, *target, params, &arg, &error);
   987 				if (error)
   988 				{
   989 					return 0;
   990 				}
   991 				buf_p += res;
   992 				llen -= res;
   993 				break;
   994 			}
   995 		case 't':
   996 			{
   997 				CHECK_ARGS(0);
   998 				char *key;
   999 				bool error;
  1000 				size_t res;
  1001 				cell_t target = g_SourceMod.GetGlobalTarget();
  1002 				pCtx->LocalToString(params[arg++], &key);
  1003 				res = Translate(buf_p, llen, pCtx, key, target, params, &arg, &error);
  1004 				if (error)
  1005 				{
  1006 					return 0;
  1007 				}
  1008 				buf_p += res;
  1009 				llen -= res;
  1010 				break;
  1011 			}
  1012 		case 'X':
  1013 			{
  1014 				CHECK_ARGS(0);
  1015 				cell_t *value;
  1016 				pCtx->LocalToPhysAddr(params[arg], &value);
  1017 				flags |= UPPERDIGITS;
  1018 				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
  1019 				arg++;
  1020 				break;
  1021 			}
  1022 		case 'x':
  1023 			{
  1024 				CHECK_ARGS(0);
  1025 				cell_t *value;
  1026 				pCtx->LocalToPhysAddr(params[arg], &value);
  1027 				AddHex(&buf_p, llen, static_cast<unsigned int>(*value), width, flags);
  1028 				arg++;
  1029 				break;
  1030 			}
  1031 		case '%':
  1032 			{
  1033 				if (!llen)
  1034 				{
  1035 					goto done;
  1036 				}
  1037 				*buf_p++ = ch;
  1038 				llen--;
  1039 				break;
  1040 			}
  1041 		case '\0':
  1042 			{
  1043 				if (!llen)
  1044 				{
  1045 					goto done;
  1046 				}
  1047 				*buf_p++ = '%';
  1048 				llen--;
  1049 				goto done;
  1050 			}
  1051 		default:
  1052 			{
  1053 				if (!llen)
  1054 				{
  1055 					goto done;
  1056 				}
  1057 				*buf_p++ = ch;
  1058 				llen--;
  1059 				break;
  1060 			}
  1061 		}
  1062 	}
  1063 
  1064 done:
  1065 	*buf_p = '\0';
  1066 	*param = arg;
  1067 	return (maxlen - llen - 1);
  1068 }
  1069 
  1070 const char *stristr(const char *str, const char *substr)
  1071 {
  1072 	if (!*substr)
  1073 	{
  1074 		return ((char *)str);
  1075 	}
  1076 
  1077 	char *needle = (char *)substr;
  1078 	char *prevloc = (char *)str;
  1079 	char *haystack = (char *)str;
  1080 
  1081 	while (*haystack)
  1082 	{
  1083 		if (tolower(*haystack) == tolower(*needle))
  1084 		{
  1085 			haystack++;
  1086 			if (!*++needle)
  1087 			{
  1088 				return prevloc;
  1089 			}
  1090 		}
  1091 		else
  1092 		{
  1093 			haystack = ++prevloc;
  1094 			needle = (char *)substr;
  1095 		}
  1096 	}
  1097 
  1098 	return NULL;
  1099 }
  1100 
  1101 unsigned int strncopy(char *dest, const char *src, size_t count)
  1102 {
  1103 	if (!count)
  1104 	{
  1105 		return 0;
  1106 	}
  1107 
  1108 	char *start = dest;
  1109 	while ((*src) && (--count))
  1110 	{
  1111 		*dest++ = *src++;
  1112 	}
  1113 	*dest = '\0';
  1114 
  1115 	return (dest - start);
  1116 }
  1117 
  1118 size_t UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
  1119 {
  1120 	va_list ap;
  1121 	va_start(ap, fmt);
  1122 	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
  1123 	va_end(ap);
  1124 
  1125 	if (len >= maxlength)
  1126 	{
  1127 		buffer[maxlength - 1] = '\0';
  1128 		return (maxlength - 1);
  1129 	}
  1130 	else
  1131 	{
  1132 		return len;
  1133 	}
  1134 }
  1135 
  1136 size_t UTIL_FormatArgs(char *buffer, size_t maxlength, const char *fmt, va_list ap)
  1137 {
  1138 	size_t len = vsnprintf(buffer, maxlength, fmt, ap);
  1139 
  1140 	if (len >= maxlength)
  1141 	{
  1142 		buffer[maxlength - 1] = '\0';
  1143 		return (maxlength - 1);
  1144 	}
  1145 	else
  1146 	{
  1147 		return len;
  1148 	}
  1149 }
  1150 
  1151 char *sm_strdup(const char *str)
  1152 {
  1153 	char *ptr = new char[strlen(str)+1];
  1154 	strcpy(ptr, str);
  1155 	return ptr;
  1156 }
  1157 
  1158 unsigned int UTIL_ReplaceAll(char *subject, size_t maxlength, const char *search, const char *replace)
  1159 {
  1160 	size_t searchLen = strlen(search);
  1161 	size_t replaceLen = strlen(replace);
  1162 
  1163 	char *ptr = subject;
  1164 	unsigned int total = 0;
  1165 	while ((ptr = UTIL_ReplaceEx(ptr, maxlength, search, searchLen, replace, replaceLen)) != NULL)
  1166 	{
  1167 		total++;
  1168 		if (*ptr == '\0')
  1169 		{
  1170 			break;
  1171 		}
  1172 	}
  1173 
  1174 	return total;
  1175 }
  1176 
  1177 /**
  1178  * NOTE: Do not edit this for the love of god unless you have 
  1179  * read the test cases and understand the code behind each one.
  1180  * While I don't guarantee there aren't mistakes, I do guarantee
  1181  * that plugins will end up relying on tiny idiosyncracies of this
  1182  * function, just like they did with AMX Mod X.
  1183  *
  1184  * There are explicitly more cases than the AMX Mod X version because
  1185  * we're not doing a blind copy.  Each case is specifically optimized
  1186  * for what needs to be done.  Even better, we don't have to error on
  1187  * bad buffer sizes.  Instead, this function will smartly cut off the 
  1188  * string in a way that pushes old data out.
  1189  */
  1190 char *UTIL_ReplaceEx(char *subject, size_t maxLen, const char *search, size_t searchLen, const char *replace, size_t replaceLen)
  1191 {
  1192 	char *ptr = subject;
  1193 	size_t browsed = 0;
  1194 	size_t textLen = strlen(subject);
  1195 
  1196 	/* It's not possible to search or replace */
  1197 	if (searchLen > textLen)
  1198 	{
  1199 		return NULL;
  1200 	}
  1201 
  1202 	/* Handle the case of one byte replacement.
  1203 	 * It's only valid in one case.
  1204 	 */
  1205 	if (maxLen == 1)
  1206 	{
  1207 		/* If the search matches and the replace length is 0, 
  1208 		 * we can just terminate the string and be done.
  1209 		 */
  1210 		if (strcmp(subject, search) == 0 && replaceLen == 0)
  1211 		{
  1212 			*subject = '\0';
  1213 			return subject;
  1214 		}
  1215 		else
  1216 		{
  1217 			return NULL;
  1218 		}
  1219 	}
  1220 
  1221 	/* Subtract one off the maxlength so we can include the null terminator */
  1222 	maxLen--;
  1223 
  1224 	while (*ptr != '\0' && (browsed <= textLen - searchLen))
  1225 	{
  1226 		/* See if we get a comparison */
  1227 		if (strncmp(ptr, search, searchLen) == 0)
  1228 		{
  1229 			if (replaceLen > searchLen)
  1230 			{
  1231 				/* First, see if we have enough space to do this operation */
  1232 				if (maxLen - textLen < replaceLen - searchLen)
  1233 				{
  1234 					/* First, see if the replacement length goes out of bounds. */
  1235 					if (browsed + replaceLen >= maxLen)
  1236 					{
  1237 						/* EXAMPLE CASE:
  1238 						 * Subject: AABBBCCC
  1239 						 * Buffer : 12 bytes
  1240 						 * Search : BBB
  1241 						 * Replace: DDDDDDDDDD
  1242 						 * OUTPUT : AADDDDDDDDD
  1243 						 * POSITION:           ^
  1244 						 */
  1245 						/* If it does, we'll just bound the length and do a strcpy. */
  1246 						replaceLen = maxLen - browsed;
  1247 						/* Note, we add one to the final result for the null terminator */
  1248 						strncopy(ptr, replace, replaceLen+1);
  1249 					}
  1250 					else
  1251 					{
  1252 						/* EXAMPLE CASE:
  1253 						 * Subject: AABBBCCC
  1254 						 * Buffer : 12 bytes
  1255 						 * Search : BBB
  1256 						 * Replace: DDDDDDD
  1257 						 * OUTPUT : AADDDDDDDCC
  1258 						 * POSITION:         ^
  1259 						 */
  1260 						/* We're going to have some bytes left over... */
  1261 						size_t origBytesToCopy = (textLen - (browsed + searchLen)) + 1;
  1262 						size_t realBytesToCopy = (maxLen - (browsed + replaceLen)) + 1;
  1263 						char *moveFrom = ptr + searchLen + (origBytesToCopy - realBytesToCopy);
  1264 						char *moveTo = ptr + replaceLen;
  1265 
  1266 						/* First, move our old data out of the way. */
  1267 						memmove(moveTo, moveFrom, realBytesToCopy);
  1268 
  1269 						/* Now, do our replacement. */
  1270 						memcpy(ptr, replace, replaceLen);
  1271 					}
  1272 				}
  1273 				else
  1274 				{
  1275 					/* EXAMPLE CASE:
  1276 					 * Subject: AABBBCCC
  1277 					 * Buffer : 12 bytes
  1278 					 * Search : BBB
  1279 					 * Replace: DDDD
  1280 					 * OUTPUT : AADDDDCCC
  1281 					 * POSITION:      ^
  1282 					 */
  1283 					/* Yes, we have enough space.  Do a normal move operation. */
  1284 					char *moveFrom = ptr + searchLen;
  1285 					char *moveTo = ptr + replaceLen;
  1286 
  1287 					/* First move our old data out of the way. */
  1288 					size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
  1289 					memmove(moveTo, moveFrom, bytesToCopy);
  1290 
  1291 					/* Now do our replacement. */
  1292 					memcpy(ptr, replace, replaceLen);
  1293 				}
  1294 			}
  1295 			else if (replaceLen < searchLen)
  1296 			{
  1297 				/* EXAMPLE CASE:
  1298 				 * Subject: AABBBCCC
  1299 				 * Buffer : 12 bytes
  1300 				 * Search : BBB
  1301 				 * Replace: D
  1302 				 * OUTPUT : AADCCC
  1303 				 * POSOTION:   ^
  1304 				 */
  1305 				/* If the replacement does not grow the string length, we do not
  1306 				 * need to do any fancy checking at all.  Yay!
  1307 				 */
  1308 				char *moveFrom = ptr + searchLen;		/* Start after the search pointer */
  1309 				char *moveTo = ptr + replaceLen;		/* Copy to where the replacement ends */
  1310 
  1311 				/* Copy our replacement in, if any */
  1312 				if (replaceLen)
  1313 				{
  1314 					memcpy(ptr, replace, replaceLen);
  1315 				}
  1316 
  1317 				/* Figure out how many bytes to move down, including null terminator */
  1318 				size_t bytesToCopy = (textLen - (browsed + searchLen)) + 1;
  1319 
  1320 				/* Move the rest of the string down */
  1321 				memmove(moveTo, moveFrom, bytesToCopy);
  1322 			}
  1323 			else
  1324 			{
  1325 				/* EXAMPLE CASE:
  1326 				 * Subject: AABBBCCC
  1327 				 * Buffer : 12 bytes
  1328 				 * Search : BBB
  1329 				 * Replace: DDD
  1330 				 * OUTPUT : AADDDCCC
  1331 				 * POSITION:     ^
  1332 				 */
  1333 				/* We don't have to move anything around, just do a straight copy */
  1334 				memcpy(ptr, replace, replaceLen);
  1335 			}
  1336 
  1337 			return ptr + replaceLen;
  1338 		}
  1339 		ptr++;
  1340 		browsed++;
  1341 	}
  1342 
  1343 	return NULL;
  1344 }
  1345 
  1346 char *UTIL_TrimWhitespace(char *str, size_t &len)
  1347 {
  1348 	char *end = str + len - 1;
  1349 
  1350 	if (!len)
  1351 	{
  1352 		return str;
  1353 	}
  1354 
  1355 	/* Iterate backwards through string until we reach first non-whitespace char */
  1356 	while (end >= str && textparsers->IsWhitespace(end))
  1357 	{
  1358 		end--;
  1359 		len--;
  1360 	}
  1361 
  1362 	/* Replace first whitespace char (at the end) with null terminator.
  1363 	 * If there is none, we're just replacing the null terminator. 
  1364 	 */
  1365 	*(end + 1) = '\0';
  1366 
  1367 	while (*str != '\0' && textparsers->IsWhitespace(str))
  1368 	{
  1369 		str++;
  1370 		len--;
  1371 	}
  1372 
  1373 	return str;
  1374 }
  1375